From 16fb61875222113ef208897c44415ef17faa291a Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 26 Apr 2023 17:47:18 +0200 Subject: [PATCH 01/98] bump nucypher-core to 0.6.0 --- package.json | 2 +- src/characters/bob.ts | 4 +- src/characters/porter.ts | 10 +++-- src/characters/universal-bob.ts | 13 +++--- src/keyring.ts | 2 +- src/policies/policy.ts | 2 +- src/sdk/strategy.ts | 22 +++++----- test/acceptance/alice-grants.test.ts | 21 ++++++++-- test/acceptance/delay-enact.test.ts | 2 +- test/docs/cbd.test.ts | 2 +- test/integration/conditions.test.ts | 2 +- test/integration/enrico.test.ts | 4 +- test/unit/strategy.test.ts | 24 +++++------ test/unit/testVariables.ts | 60 ++++++++++++++++++++++++---- test/utils.ts | 8 ++-- yarn.lock | 8 ++-- 16 files changed, 124 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index 7bcc725b3..eef0e6f90 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "prebuild": "yarn typechain" }, "dependencies": { - "@nucypher/nucypher-core": "0.4.0", + "@nucypher/nucypher-core": "^0.6.0", "axios": "^0.21.1", "ethers": "^5.4.1", "joi": "^17.7.0", diff --git a/src/characters/bob.ts b/src/characters/bob.ts index 5d4c67101..1ee8606af 100644 --- a/src/characters/bob.ts +++ b/src/characters/bob.ts @@ -27,11 +27,11 @@ export class RemoteBob { const dk = decryptingKey instanceof PublicKey ? decryptingKey - : PublicKey.fromBytes(decryptingKey); + : PublicKey.fromCompressedBytes(decryptingKey); const vk = verifyingKey instanceof PublicKey ? verifyingKey - : PublicKey.fromBytes(verifyingKey); + : PublicKey.fromCompressedBytes(verifyingKey); return new RemoteBob(dk, vk); } } diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 7834ab491..ee0df9a40 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -93,7 +93,9 @@ export class Porter { return resp.data.result.ursulas.map((u: UrsulaResponse) => ({ checksumAddress: u.checksum_address, uri: u.uri, - encryptingKey: PublicKey.fromBytes(fromHexString(u.encrypting_key)), + encryptingKey: PublicKey.fromCompressedBytes( + fromHexString(u.encrypting_key) + ), })); } @@ -111,9 +113,9 @@ export class Porter { const data: PostRetrieveCFragsRequest = { treasure_map: toBase64(treasureMap.toBytes()), retrieval_kits: retrievalKits.map((rk) => toBase64(rk.toBytes())), - alice_verifying_key: toHexString(aliceVerifyingKey.toBytes()), - bob_encrypting_key: toHexString(bobEncryptingKey.toBytes()), - bob_verifying_key: toHexString(bobVerifyingKey.toBytes()), + alice_verifying_key: toHexString(aliceVerifyingKey.toCompressedBytes()), + bob_encrypting_key: toHexString(bobEncryptingKey.toCompressedBytes()), + bob_verifying_key: toHexString(bobVerifyingKey.toCompressedBytes()), context, }; const resp: AxiosResponse = await axios.post( diff --git a/src/characters/universal-bob.ts b/src/characters/universal-bob.ts index be8b6afc6..3bdbb95d7 100644 --- a/src/characters/universal-bob.ts +++ b/src/characters/universal-bob.ts @@ -140,10 +140,11 @@ export class tDecDecrypter { public toObj(): decrypterJSON { return { porterUri: this.porter.porterUrl.toString(), - policyEncryptingKeyBytes: this.policyEncryptingKey.toBytes(), + policyEncryptingKeyBytes: this.policyEncryptingKey.toCompressedBytes(), encryptedTreasureMapBytes: this.encryptedTreasureMap.toBytes(), - publisherVerifyingKeyBytes: this.publisherVerifyingKey.toBytes(), - bobSecretKeyBytes: this.keyring.secretKey.toSecretBytes(), + publisherVerifyingKeyBytes: + this.publisherVerifyingKey.toCompressedBytes(), + bobSecretKeyBytes: this.keyring.secretKey.toBEBytes(), }; } @@ -160,10 +161,10 @@ export class tDecDecrypter { }: decrypterJSON) { return new tDecDecrypter( porterUri, - PublicKey.fromBytes(policyEncryptingKeyBytes), + PublicKey.fromCompressedBytes(policyEncryptingKeyBytes), EncryptedTreasureMap.fromBytes(encryptedTreasureMapBytes), - PublicKey.fromBytes(publisherVerifyingKeyBytes), - SecretKey.fromBytes(bobSecretKeyBytes) + PublicKey.fromCompressedBytes(publisherVerifyingKeyBytes), + SecretKey.fromBEBytes(bobSecretKeyBytes) ); } diff --git a/src/keyring.ts b/src/keyring.ts index 4e0f06b99..4b769b95e 100644 --- a/src/keyring.ts +++ b/src/keyring.ts @@ -55,7 +55,7 @@ export class Keyring { private getSecretKeyFromLabel(label: string): SecretKey { return SecretKeyFactory.fromSecureRandomness( - this.secretKey.toSecretBytes() + this.secretKey.toBEBytes() ).makeKey(toBytes(label)); } diff --git a/src/policies/policy.ts b/src/policies/policy.ts index 54d95719e..dbb23eccb 100644 --- a/src/policies/policy.ts +++ b/src/policies/policy.ts @@ -131,7 +131,7 @@ export class BlockchainPolicy { this.delegatingKey, encryptedTreasureMap, // revocationKit, - this.publisher.verifyingKey.toBytes(), + this.publisher.verifyingKey.toCompressedBytes(), this.shares, this.startDate, this.endDate diff --git a/src/sdk/strategy.ts b/src/sdk/strategy.ts index 15190f8b8..490e44d57 100644 --- a/src/sdk/strategy.ts +++ b/src/sdk/strategy.ts @@ -136,8 +136,8 @@ export class Strategy { }: StrategyJSON) { return new Strategy( Cohort.fromObj(cohort), - SecretKey.fromBytes(aliceSecretKeyBytes), - SecretKey.fromBytes(bobSecretKeyBytes), + SecretKey.fromBEBytes(aliceSecretKeyBytes), + SecretKey.fromBEBytes(bobSecretKeyBytes), startDate, endDate, conditionSet @@ -147,8 +147,8 @@ export class Strategy { public toObj(): StrategyJSON { return { cohort: this.cohort.toObj(), - aliceSecretKeyBytes: this.aliceSecretKey.toSecretBytes(), - bobSecretKeyBytes: this.bobSecretKey.toSecretBytes(), + aliceSecretKeyBytes: this.aliceSecretKey.toBEBytes(), + bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), conditionSet: this.conditionSet, startDate: this.startDate, endDate: this.endDate, @@ -183,23 +183,25 @@ export class DeployedStrategy { conditionSet, }: DeployedStrategyJSON) { const id = HRAC.fromBytes(policy.id); - const policyKey = PublicKey.fromBytes(policy.policyKey); + const policyKey = PublicKey.fromCompressedBytes(policy.policyKey); const encryptedTreasureMap = EncryptedTreasureMap.fromBytes( policy.encryptedTreasureMap ); - const aliceVerifyingKey = PublicKey.fromBytes(policy.aliceVerifyingKey); + const aliceVerifyingKey = PublicKey.fromCompressedBytes( + policy.aliceVerifyingKey + ); const newPolicy = { id, label: policy.label, policyKey, encryptedTreasureMap, - aliceVerifyingKey: aliceVerifyingKey.toBytes(), + aliceVerifyingKey: aliceVerifyingKey.toCompressedBytes(), size: policy.size, startTimestamp: policy.startTimestamp, endTimestamp: policy.endTimestamp, txHash: policy.txHash, }; - const bobSecretKey = SecretKey.fromBytes(bobSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); const label = newPolicy.label; const cohort = Cohort.fromObj(cohortConfig); const encrypter = new Enrico(newPolicy.policyKey, undefined, conditionSet); @@ -226,13 +228,13 @@ export class DeployedStrategy { const policy = { ...this.policy, id: this.policy.id.toBytes(), - policyKey: this.policy.policyKey.toBytes(), + policyKey: this.policy.policyKey.toCompressedBytes(), encryptedTreasureMap: this.policy.encryptedTreasureMap.toBytes(), }; return { policy, cohortConfig: this.cohort.toObj(), - bobSecretKeyBytes: this.bobSecretKey.toSecretBytes(), + bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), conditionSet: this.conditionSet, }; } diff --git a/test/acceptance/alice-grants.test.ts b/test/acceptance/alice-grants.test.ts index 3e1f661aa..8ccf532fc 100644 --- a/test/acceptance/alice-grants.test.ts +++ b/test/acceptance/alice-grants.test.ts @@ -64,7 +64,9 @@ describe('story: alice shares message with bob through policy', () => { }; policy = await alice.grant(policyParams); - expect(policy.aliceVerifyingKey).toEqual(alice.verifyingKey.toBytes()); + expect(policy.aliceVerifyingKey).toEqual( + alice.verifyingKey.toCompressedBytes() + ); expect(policy.label).toBe(label); expect(getUrsulasSpy).toHaveBeenCalled(); expect(generateKFragsSpy).toHaveBeenCalled(); @@ -121,12 +123,23 @@ describe('story: alice shares message with bob through policy', () => { bobVerifyingKey_, ] = retrieveCFragsSpy.mock.calls[0]; expect( - bytesEqual(aliceVerifyingKey_.toBytes(), aliceVerifyingKey.toBytes()) + bytesEqual( + aliceVerifyingKey_.toCompressedBytes(), + aliceVerifyingKey.toCompressedBytes() + ) + ); + expect( + bytesEqual( + bobEncryptingKey_.toCompressedBytes(), + bob.decryptingKey.toCompressedBytes() + ) ); expect( - bytesEqual(bobEncryptingKey_.toBytes(), bob.decryptingKey.toBytes()) + bytesEqual( + bobVerifyingKey_.toCompressedBytes(), + bob.verifyingKey.toCompressedBytes() + ) ); - expect(bytesEqual(bobVerifyingKey_.toBytes(), bob.verifyingKey.toBytes())); const { verifiedCFrags } = reencryptKFrags( verifiedKFrags, diff --git a/test/acceptance/delay-enact.test.ts b/test/acceptance/delay-enact.test.ts index 6ca4cc623..9e76b7d5a 100644 --- a/test/acceptance/delay-enact.test.ts +++ b/test/acceptance/delay-enact.test.ts @@ -38,7 +38,7 @@ describe('story: alice1 creates a policy but alice2 enacts it', () => { policyParams ); expect(preEnactedPolicy.aliceVerifyingKey).toEqual( - alice1.verifyingKey.toBytes() + alice1.verifyingKey.toCompressedBytes() ); expect(preEnactedPolicy.label).toBe(label); diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 650ad1f2f..79fb91b4e 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -52,7 +52,7 @@ describe('Get Started (CBD PoC)', () => { jest .spyOn(providers, 'Web3Provider') .mockImplementation(() => - mockWeb3Provider(SecretKey.random().toSecretBytes()) + mockWeb3Provider(SecretKey.random().toBEBytes()) ); // diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 5fe41aa84..373a2d54e 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -239,7 +239,7 @@ describe('produce context parameters from conditions', () => { describe('condition context', () => { it('should serialize to JSON with context params', async () => { - const web3Provider = mockWeb3Provider(SecretKey.random().toSecretBytes()); + const web3Provider = mockWeb3Provider(SecretKey.random().toBEBytes()); const rpcCondition = new Conditions.RpcCondition({ chain: 5, diff --git a/test/integration/enrico.test.ts b/test/integration/enrico.test.ts index f3281b0ad..fb57894c4 100644 --- a/test/integration/enrico.test.ts +++ b/test/integration/enrico.test.ts @@ -51,7 +51,9 @@ describe('enrico', () => { threshold, shares ); - expect(delegatingKey.toBytes()).toEqual(policyEncryptingKey.toBytes()); + expect(delegatingKey.toCompressedBytes()).toEqual( + policyEncryptingKey.toCompressedBytes() + ); // Bob can decrypt re-encrypted ciphertext const { verifiedCFrags } = reencryptKFrags( diff --git a/test/unit/strategy.test.ts b/test/unit/strategy.test.ts index be450cc69..9c85c7ca0 100644 --- a/test/unit/strategy.test.ts +++ b/test/unit/strategy.test.ts @@ -40,9 +40,9 @@ describe('Strategy', () => { shares: 3, porterUri: 'https://_this.should.crash', }; - const aliceSecretKey = SecretKey.fromBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBytes(bobSecretKeyBytes); - const aliceProvider = mockWeb3Provider(aliceSecretKey.toSecretBytes()); + const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); + const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); Date.now = jest.fn(() => 1487076708000); afterEach(() => { @@ -120,15 +120,15 @@ describe('Deployed Strategy', () => { shares: 3, porterUri: 'https://_this.should.crash', }; - const aliceSecretKey = SecretKey.fromBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBytes(bobSecretKeyBytes); + const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); afterEach(() => { jest.clearAllMocks(); }); it('can export to JSON', async () => { - const aliceProvider = mockWeb3Provider(aliceSecretKey.toSecretBytes()); + const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); const mockedUrsulas = mockUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); @@ -163,8 +163,8 @@ describe('Deployed Strategy', () => { }); it('can encrypt and decrypt', async () => { - const aliceProvider = mockWeb3Provider(aliceSecretKey.toSecretBytes()); - const bobProvider = mockWeb3Provider(bobSecretKey.toSecretBytes()); + const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); + const bobProvider = mockWeb3Provider(bobSecretKey.toBEBytes()); const mockedUrsulas = mockUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); @@ -195,11 +195,10 @@ describe('Deployed Strategy', () => { parameters: [3591], chain: 5, }); - const plaintext = 'this is a secret'; - const conditions = new ConditionSet([ownsNFT]); - encrypter.conditions = conditions; + encrypter.conditions = new ConditionSet([ownsNFT]); const encryptedMessageKit = encrypter.encryptMessage(plaintext); + // Setup mocks for `retrieveAndDecrypt` const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); const ursulaAddresses = ( @@ -227,8 +226,7 @@ describe('tDecDecrypter', () => { const importedStrategy = DeployedStrategy.fromJSON(deployedStrategyJSON); it('can export to JSON', () => { - const decrypter = importedStrategy.decrypter; - const configJSON = decrypter.toJSON(); + const configJSON = importedStrategy.decrypter.toJSON(); expect(configJSON).toEqual(decrypterJSON); }); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index 643c853c9..bc315b18c 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -1,12 +1,48 @@ -export const strategyJSON = - '{"cohort":{"ursulaAddresses":["0x5cf1703a1c99a4b42eb056535840e93118177232","0x7fff551249d223f723557a96a0e1a469c79cc934","0x9c7c824239d3159327024459ad69bb215859bd25"],"threshold":2,"shares":3,"porterUri":"https://_this.should.crash"},"aliceSecretKeyBytes":"base64:N1K+vcukPJQkVi57P5jXca5W9CwX48VEBVV0H9CYWDU=","bobSecretKeyBytes":"base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=","startDate":"2017-02-14T12:51:48.000Z","endDate":"2017-03-16T12:51:48.000Z"}'; +export const strategyJSON = JSON.stringify({ + cohort: { + ursulaAddresses: [ + '0x5cf1703a1c99a4b42eb056535840e93118177232', + '0x7fff551249d223f723557a96a0e1a469c79cc934', + '0x9c7c824239d3159327024459ad69bb215859bd25', + ], + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }, + aliceSecretKeyBytes: 'base64:N1K+vcukPJQkVi57P5jXca5W9CwX48VEBVV0H9CYWDU=', + bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', + startDate: '2017-02-14T12:51:48.000Z', + endDate: '2017-03-16T12:51:48.000Z', +}); export const encryptedTreasureMapBase64 = - 'RU1hcAABAACSxGIDdzg9r81RRC+kQGy0C0s8LdQXwcDf+MJtuJ9+i3Zp+bICp+WRepaHInVIh7O3hiiz0Frs4Xr7sct2nNaXHrbFLgzKHdNnmhg7EIpPWs2HUJMjwh4Ny0rKVyx2/Ilvh24t5sUGtg2tnZKEMnf1Z9E8wIEv1itZmsjjXmnlrshHxPdQ/1cJneXIU+ch+n7t+BJQkOJSfLE74++KZJ+mUnPpCh8QWssHeeGNBeB2oB9CtpmcBzDoLdJJClTVte4OmADofiQ6RJswQfGCM6Gw21rJOqm5q7/6jaR47gvMlu/A94Y3WkiUAFbaGcr4vQzeAflRsR0N9LPfKIIqHfajWNCEagp0GgoaG6DsQVeEnaL7z+meg8HeQdO+m09OFh4b8Ic0m2BtYSscrlUBlU5JWrxKtLjPuxBTIQyX8FQV6gYnLyzkAEBAOgPgjuwHsTZeFUatTDYYhA/Y2hlfdmLeYW5pZ74/+uNJBof/9QB1beCn+TfrTbVyjGADn2JU8NwhnsNdDM3XFQy6WUoHLrnTs4lYj16unC7Ir6DnI6UXj84ixjli2ge8wgHpZkmQJKrMc1HvfDlM3TWG18rvEB0fE70sSMGhhegp/cjh52C3eTUACaEkO127r1kF/NjCDtbOsk9CnD2mwGyOMdr17B5TojWINwJ0SsT9gCgnZjEuxbX45NAr+M+Nz4TwbPF8ap67mj3dbGvuokNHsBfjn8YCRAIDgP83SYnR/Y4VqqKL7uzRbfmu2ljCkTohe9rJUNhwaVYpl0PODuVZQo1jAmSBZ9/HzVNbkqGwiTAtjz5CHXW7oKl8SwXiBUHXXOu0jvi8RgOP9BZeoL48sFLiSWDIxI2XAxEEuaD16vgvvthl0mxklP7R5p7vvAfAdP/lNCy28B5r7PPsXh6MmYyyaObetm2HzcJhB3sijfXHM5iEoQ41PKXRTRxrgAw1eVAjOnNW3Ld9LJEOqB6hh/U3UYUPhrXkk4kdc7EAm4gTMbq5wmEsZQRnWIn5IsBEAMFlh3z6PvtrcuwRJkIwQSF96Ai0kE776e9pjsiuEUyZFyn4wRTfg2mxdteiC4vKTBxh2BJNVxzRONtTMgcZ0JHZpCf/ZRct2Bke8F4eWn+AmtYDQEdASHnJQtjQPe5Fl5LoePZ1uIVaKgWywz6pV4zvX4hBAZ2gK/wo8hzJNRtAaKoAr8FQxG9HUlY2htZmCtI60m2a2y8Vj3IKTa2X/2PqB/OnuZEq18FCve91TmKqghCHqteB1ON6dMl+1XK4WpTlyELbha6cni8er2w4dDO+P6e40lWzQJKUlfyYfCoB8uRDCLw82a58kG80dYGZFZAmGhqItL9RC6Ofm2TBq0F+jfdSS3DVxrJG5AldhuKbIK73ojAaLV8FVjRyT6uwo6M4JAj12krCgbvOXUaHM3jT08ZFmOI0ldmzG/I6I73LHEGm7Hey6a+K4ry5EiXA+KgLMNA8pct6HihOKBws7ckClqYOos6nAyUkABpbASdfmyat8jtUNyIXprEtZr4wvB/2E011cf9EsnIVhEovpPmvMp7IdgID0Qxs219MFFr7sJ5XjHeOINA4jb6gh5r5uQzuYtpmxv7ppi8fs/x0co8pOsgHDfy4zRGDys/Z9f/QrEB6VHms9xh7Kwpp3HLAtCcEAkCg+AsnC++6VMwWwvcLbjfE2uIYsVJJ+ciX7rSYZyDzC0IayyqNJLgS30li1xPb2u5YHMA+2vzSQGx9Zae2QSXFwDQ5Qui94RfQP//x6pqZuutXI8fqGDey50DYtPYd8J6AiKcoOPBivqSTMXj2Nb9FItbDm5DYTRM7CmQkmbYLR+/hord5AFSYauEB3zsqME3vD3mBY5OvWfPqnSpUUGe2RTF+u3mtmcfptaO9fwWSr6ckBwBMB42DuoyXVuYsdXA6WCtqCjQGqCBG0qsvOJFA3Rd2q5cy4Q+V537LlOJAkhSu/9/1WDma7JbELCSq5fCl9hOVJ96c8CfJacSVhNM4LRT71qDEf9wI2AVQrjYeMGdSXDEtidXyz1oPZpCoc8mPALPLOvgwBhLYK175NteQ9GQ+9ySMI1NWBbv/FP1jwKrFtT46FdFQHKv8xc/z29muPNPmgev3ht1tvPzrrvyCtQLdASaaHTc1FlZcMlYopgOxvz8q3MUki0wjwpVudTSaowf8Bfduj3r6TSPo11+k97P49fy3LLxOsTrvVaetD0rNIs704MI0/DmNZd0FkqyX6JzFILUCZunK9azQoJX9vr9BzsQJ2r3vmlSYNA+n9C2+YIcZqE7nHjO38XTbnfCcrZvWohBZ+Q9gDWPD7Y378w3OgIrkzNEUJhFHCahLVj41l3Y3HIMI8GM98uejNjEVZLW6NEMrjhGh+aimmvgFCAIJYXFMFe8oSfeFRQBQnvOc0y+XJl5Mbm6g5oKp'; - -export const deployedStrategyJSON = - '{"policy":{"id":"base64:tKuR/HipMeU4trCdytbZCQ==","label":"test","policyKey":"base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C","encryptedTreasureMap":"base64:RU1hcAABAACSxGIDdzg9r81RRC+kQGy0C0s8LdQXwcDf+MJtuJ9+i3Zp+bICp+WRepaHInVIh7O3hiiz0Frs4Xr7sct2nNaXHrbFLgzKHdNnmhg7EIpPWs2HUJMjwh4Ny0rKVyx2/Ilvh24t5sUGtg2tnZKEMnf1Z9E8wIEv1itZmsjjXmnlrshHxPdQ/1cJneXIU+ch+n7t+BJQkOJSfLE74++KZJ+mUnPpCh8QWssHeeGNBeB2oB9CtpmcBzDoLdJJClTVte4OmADofiQ6RJswQfGCM6Gw21rJOqm5q7/6jaR47gvMlu/A94Y3WkiUAFbaGcr4vQzeAflRsR0N9LPfKIIqHfajWNCEagp0GgoaG6DsQVeEnaL7z+meg8HeQdO+m09OFh4b8Ic0m2BtYSscrlUBlU5JWrxKtLjPuxBTIQyX8FQV6gYnLyzkAEBAOgPgjuwHsTZeFUatTDYYhA/Y2hlfdmLeYW5pZ74/+uNJBof/9QB1beCn+TfrTbVyjGADn2JU8NwhnsNdDM3XFQy6WUoHLrnTs4lYj16unC7Ir6DnI6UXj84ixjli2ge8wgHpZkmQJKrMc1HvfDlM3TWG18rvEB0fE70sSMGhhegp/cjh52C3eTUACaEkO127r1kF/NjCDtbOsk9CnD2mwGyOMdr17B5TojWINwJ0SsT9gCgnZjEuxbX45NAr+M+Nz4TwbPF8ap67mj3dbGvuokNHsBfjn8YCRAIDgP83SYnR/Y4VqqKL7uzRbfmu2ljCkTohe9rJUNhwaVYpl0PODuVZQo1jAmSBZ9/HzVNbkqGwiTAtjz5CHXW7oKl8SwXiBUHXXOu0jvi8RgOP9BZeoL48sFLiSWDIxI2XAxEEuaD16vgvvthl0mxklP7R5p7vvAfAdP/lNCy28B5r7PPsXh6MmYyyaObetm2HzcJhB3sijfXHM5iEoQ41PKXRTRxrgAw1eVAjOnNW3Ld9LJEOqB6hh/U3UYUPhrXkk4kdc7EAm4gTMbq5wmEsZQRnWIn5IsBEAMFlh3z6PvtrcuwRJkIwQSF96Ai0kE776e9pjsiuEUyZFyn4wRTfg2mxdteiC4vKTBxh2BJNVxzRONtTMgcZ0JHZpCf/ZRct2Bke8F4eWn+AmtYDQEdASHnJQtjQPe5Fl5LoePZ1uIVaKgWywz6pV4zvX4hBAZ2gK/wo8hzJNRtAaKoAr8FQxG9HUlY2htZmCtI60m2a2y8Vj3IKTa2X/2PqB/OnuZEq18FCve91TmKqghCHqteB1ON6dMl+1XK4WpTlyELbha6cni8er2w4dDO+P6e40lWzQJKUlfyYfCoB8uRDCLw82a58kG80dYGZFZAmGhqItL9RC6Ofm2TBq0F+jfdSS3DVxrJG5AldhuKbIK73ojAaLV8FVjRyT6uwo6M4JAj12krCgbvOXUaHM3jT08ZFmOI0ldmzG/I6I73LHEGm7Hey6a+K4ry5EiXA+KgLMNA8pct6HihOKBws7ckClqYOos6nAyUkABpbASdfmyat8jtUNyIXprEtZr4wvB/2E011cf9EsnIVhEovpPmvMp7IdgID0Qxs219MFFr7sJ5XjHeOINA4jb6gh5r5uQzuYtpmxv7ppi8fs/x0co8pOsgHDfy4zRGDys/Z9f/QrEB6VHms9xh7Kwpp3HLAtCcEAkCg+AsnC++6VMwWwvcLbjfE2uIYsVJJ+ciX7rSYZyDzC0IayyqNJLgS30li1xPb2u5YHMA+2vzSQGx9Zae2QSXFwDQ5Qui94RfQP//x6pqZuutXI8fqGDey50DYtPYd8J6AiKcoOPBivqSTMXj2Nb9FItbDm5DYTRM7CmQkmbYLR+/hord5AFSYauEB3zsqME3vD3mBY5OvWfPqnSpUUGe2RTF+u3mtmcfptaO9fwWSr6ckBwBMB42DuoyXVuYsdXA6WCtqCjQGqCBG0qsvOJFA3Rd2q5cy4Q+V537LlOJAkhSu/9/1WDma7JbELCSq5fCl9hOVJ96c8CfJacSVhNM4LRT71qDEf9wI2AVQrjYeMGdSXDEtidXyz1oPZpCoc8mPALPLOvgwBhLYK175NteQ9GQ+9ySMI1NWBbv/FP1jwKrFtT46FdFQHKv8xc/z29muPNPmgev3ht1tvPzrrvyCtQLdASaaHTc1FlZcMlYopgOxvz8q3MUki0wjwpVudTSaowf8Bfduj3r6TSPo11+k97P49fy3LLxOsTrvVaetD0rNIs704MI0/DmNZd0FkqyX6JzFILUCZunK9azQoJX9vr9BzsQJ2r3vmlSYNA+n9C2+YIcZqE7nHjO38XTbnfCcrZvWohBZ+Q9gDWPD7Y378w3OgIrkzNEUJhFHCahLVj41l3Y3HIMI8GM98uejNjEVZLW6NEMrjhGh+aimmvgFCAIJYXFMFe8oSfeFRQBQnvOc0y+XJl5Mbm6g5oKp","aliceVerifyingKey":"base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj","size":3,"startTimestamp":"2017-02-14T12:51:48.000Z","endTimestamp":"2017-03-16T12:51:48.000Z","txHash":"0x1234567890123456789012345678901234567890"},"cohortConfig":{"ursulaAddresses":["0x5cf1703a1c99a4b42eb056535840e93118177232","0x7fff551249d223f723557a96a0e1a469c79cc934","0x9c7c824239d3159327024459ad69bb215859bd25"],"threshold":2,"shares":3,"porterUri":"https://_this.should.crash"},"bobSecretKeyBytes":"base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk="}'; + 'RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw='; +export const deployedStrategyJSON = JSON.stringify({ + policy: { + id: 'base64:tKuR/HipMeU4trCdytbZCQ==', + label: 'test', + policyKey: 'base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C', + encryptedTreasureMap: + 'base64:RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw=', + aliceVerifyingKey: 'base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj', + size: 3, + startTimestamp: '2017-02-14T12:51:48.000Z', + endTimestamp: '2017-03-16T12:51:48.000Z', + txHash: '0x1234567890123456789012345678901234567890', + }, + cohortConfig: { + ursulaAddresses: [ + '0x5cf1703a1c99a4b42eb056535840e93118177232', + '0x7fff551249d223f723557a96a0e1a469c79cc934', + '0x9c7c824239d3159327024459ad69bb215859bd25', + ], + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }, + bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', +}); export const aliceSecretKeyBytes = new Uint8Array([ 55, 82, 190, 189, 203, 164, 60, 148, 36, 86, 46, 123, 63, 152, 215, 113, 174, 86, 244, 44, 23, 227, 197, 68, 5, 85, 116, 31, 208, 152, 88, 53, @@ -17,5 +53,13 @@ export const bobSecretKeyBytes = new Uint8Array([ 229, 2, 106, 176, 205, 33, 168, 23, 213, 233, 200, 238, 11, 193, 153, ]); -export const decrypterJSON = - '{"porterUri":"https://_this.should.crash/","policyEncryptingKeyBytes":"base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C","encryptedTreasureMapBytes":"base64:RU1hcAABAACSxGIDdzg9r81RRC+kQGy0C0s8LdQXwcDf+MJtuJ9+i3Zp+bICp+WRepaHInVIh7O3hiiz0Frs4Xr7sct2nNaXHrbFLgzKHdNnmhg7EIpPWs2HUJMjwh4Ny0rKVyx2/Ilvh24t5sUGtg2tnZKEMnf1Z9E8wIEv1itZmsjjXmnlrshHxPdQ/1cJneXIU+ch+n7t+BJQkOJSfLE74++KZJ+mUnPpCh8QWssHeeGNBeB2oB9CtpmcBzDoLdJJClTVte4OmADofiQ6RJswQfGCM6Gw21rJOqm5q7/6jaR47gvMlu/A94Y3WkiUAFbaGcr4vQzeAflRsR0N9LPfKIIqHfajWNCEagp0GgoaG6DsQVeEnaL7z+meg8HeQdO+m09OFh4b8Ic0m2BtYSscrlUBlU5JWrxKtLjPuxBTIQyX8FQV6gYnLyzkAEBAOgPgjuwHsTZeFUatTDYYhA/Y2hlfdmLeYW5pZ74/+uNJBof/9QB1beCn+TfrTbVyjGADn2JU8NwhnsNdDM3XFQy6WUoHLrnTs4lYj16unC7Ir6DnI6UXj84ixjli2ge8wgHpZkmQJKrMc1HvfDlM3TWG18rvEB0fE70sSMGhhegp/cjh52C3eTUACaEkO127r1kF/NjCDtbOsk9CnD2mwGyOMdr17B5TojWINwJ0SsT9gCgnZjEuxbX45NAr+M+Nz4TwbPF8ap67mj3dbGvuokNHsBfjn8YCRAIDgP83SYnR/Y4VqqKL7uzRbfmu2ljCkTohe9rJUNhwaVYpl0PODuVZQo1jAmSBZ9/HzVNbkqGwiTAtjz5CHXW7oKl8SwXiBUHXXOu0jvi8RgOP9BZeoL48sFLiSWDIxI2XAxEEuaD16vgvvthl0mxklP7R5p7vvAfAdP/lNCy28B5r7PPsXh6MmYyyaObetm2HzcJhB3sijfXHM5iEoQ41PKXRTRxrgAw1eVAjOnNW3Ld9LJEOqB6hh/U3UYUPhrXkk4kdc7EAm4gTMbq5wmEsZQRnWIn5IsBEAMFlh3z6PvtrcuwRJkIwQSF96Ai0kE776e9pjsiuEUyZFyn4wRTfg2mxdteiC4vKTBxh2BJNVxzRONtTMgcZ0JHZpCf/ZRct2Bke8F4eWn+AmtYDQEdASHnJQtjQPe5Fl5LoePZ1uIVaKgWywz6pV4zvX4hBAZ2gK/wo8hzJNRtAaKoAr8FQxG9HUlY2htZmCtI60m2a2y8Vj3IKTa2X/2PqB/OnuZEq18FCve91TmKqghCHqteB1ON6dMl+1XK4WpTlyELbha6cni8er2w4dDO+P6e40lWzQJKUlfyYfCoB8uRDCLw82a58kG80dYGZFZAmGhqItL9RC6Ofm2TBq0F+jfdSS3DVxrJG5AldhuKbIK73ojAaLV8FVjRyT6uwo6M4JAj12krCgbvOXUaHM3jT08ZFmOI0ldmzG/I6I73LHEGm7Hey6a+K4ry5EiXA+KgLMNA8pct6HihOKBws7ckClqYOos6nAyUkABpbASdfmyat8jtUNyIXprEtZr4wvB/2E011cf9EsnIVhEovpPmvMp7IdgID0Qxs219MFFr7sJ5XjHeOINA4jb6gh5r5uQzuYtpmxv7ppi8fs/x0co8pOsgHDfy4zRGDys/Z9f/QrEB6VHms9xh7Kwpp3HLAtCcEAkCg+AsnC++6VMwWwvcLbjfE2uIYsVJJ+ciX7rSYZyDzC0IayyqNJLgS30li1xPb2u5YHMA+2vzSQGx9Zae2QSXFwDQ5Qui94RfQP//x6pqZuutXI8fqGDey50DYtPYd8J6AiKcoOPBivqSTMXj2Nb9FItbDm5DYTRM7CmQkmbYLR+/hord5AFSYauEB3zsqME3vD3mBY5OvWfPqnSpUUGe2RTF+u3mtmcfptaO9fwWSr6ckBwBMB42DuoyXVuYsdXA6WCtqCjQGqCBG0qsvOJFA3Rd2q5cy4Q+V537LlOJAkhSu/9/1WDma7JbELCSq5fCl9hOVJ96c8CfJacSVhNM4LRT71qDEf9wI2AVQrjYeMGdSXDEtidXyz1oPZpCoc8mPALPLOvgwBhLYK175NteQ9GQ+9ySMI1NWBbv/FP1jwKrFtT46FdFQHKv8xc/z29muPNPmgev3ht1tvPzrrvyCtQLdASaaHTc1FlZcMlYopgOxvz8q3MUki0wjwpVudTSaowf8Bfduj3r6TSPo11+k97P49fy3LLxOsTrvVaetD0rNIs704MI0/DmNZd0FkqyX6JzFILUCZunK9azQoJX9vr9BzsQJ2r3vmlSYNA+n9C2+YIcZqE7nHjO38XTbnfCcrZvWohBZ+Q9gDWPD7Y378w3OgIrkzNEUJhFHCahLVj41l3Y3HIMI8GM98uejNjEVZLW6NEMrjhGh+aimmvgFCAIJYXFMFe8oSfeFRQBQnvOc0y+XJl5Mbm6g5oKp","publisherVerifyingKeyBytes":"base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj","bobSecretKeyBytes":"base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk="}'; +export const decrypterJSON = JSON.stringify({ + porterUri: 'https://_this.should.crash/', + policyEncryptingKeyBytes: + 'base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C', + encryptedTreasureMapBytes: + 'base64:RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw=', + publisherVerifyingKeyBytes: + 'base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj', + bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', +}); diff --git a/test/utils.ts b/test/utils.ts index c27b5da7b..13f0bb504 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -37,7 +37,7 @@ const mockConfig: Configuration = { }; export const mockBob = (): Bob => { - const secretKey = SecretKey.fromBytes( + const secretKey = SecretKey.fromBEBytes( toBytes('fake-secret-key-32-bytes-bob-xxx') ); return Bob.fromSecretKey(mockConfig, secretKey); @@ -49,8 +49,8 @@ export const mockRemoteBob = (): RemoteBob => { }; export const mockAlice = (aliceKey = 'fake-secret-key-32-bytes-alice-x') => { - const secretKey = SecretKey.fromBytes(toBytes(aliceKey)); - const provider = mockWeb3Provider(secretKey.toSecretBytes()); + const secretKey = SecretKey.fromBEBytes(toBytes(aliceKey)); + const provider = mockWeb3Provider(secretKey.toBEBytes()); return Alice.fromSecretKey(mockConfig, secretKey, provider); }; @@ -123,7 +123,7 @@ export const mockGetUrsulas = (ursulas: readonly Ursula[]) => { return { result: { ursulas: mockUrsulas.map(({ encryptingKey, uri, checksumAddress }) => ({ - encrypting_key: toHexString(encryptingKey.toBytes()), + encrypting_key: toHexString(encryptingKey.toCompressedBytes()), uri: uri, checksum_address: checksumAddress, })), diff --git a/yarn.lock b/yarn.lock index bf7e3e148..30f8d5744 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1643,10 +1643,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nucypher/nucypher-core@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.4.0.tgz#1388abde9de61ed848959b86bc96e3d12ef97ae4" - integrity sha512-Pu4tRCwf7lnhZSoMjp889uTIUM7bNmYEe05TOOCuMYbJKmQWe7tYKeH0+rflUBISzGtMx2+xDe2CYYTyEGkvSw== +"@nucypher/nucypher-core@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.6.0.tgz#07f60e2ba084efe3174afa7eefcb75ba58a0ba39" + integrity sha512-6S+x3J8vTrcXowtPl0GtprF7WgyP3lsSSG3+AYJXS17Sz9n2UeUdfWIxXrXt4bPj27w0sfgwYfXDoVhS/DP68A== "@sideway/address@^4.1.3": version "4.1.4" From ddde9940cea9f36059d0ba7923aebcd86ca12cc7 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 3 May 2023 12:05:03 +0200 Subject: [PATCH 02/98] fix leaking mocks --- README.md | 7 +++--- package.json | 2 +- test/acceptance/alice-grants.test.ts | 4 ++-- test/acceptance/delay-enact.test.ts | 4 ++-- test/docs/cbd.test.ts | 6 ++--- test/integration/pre.test.ts | 4 ++-- test/unit/cohort.test.ts | 10 ++++----- test/unit/strategy.test.ts | 33 ++++++++++++++++------------ test/unit/testVariables.ts | 4 ++-- test/utils.ts | 19 +++++++++------- 10 files changed, 51 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 5cd089265..c85ae1b19 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ Full documentation can be found [here](https://docs.threshold.network/app-develo > **Warning** > > `nucypher-ts` is under [active development](https://github.com/nucypher/nucypher-ts/pulls): -> -> - SDK does not support policy revocation. -> - We expect breaking changes. +> +> - SDK does not support policy revocation. +> - We expect breaking changes. ## Installation @@ -26,6 +26,7 @@ To learn more, follow the tutorial at Threshold Network's [docs](https://docs.th See [`nucypher-ts/examples`](https://github.com/nucypher/nucypher-ts/tree/main/examples) to find out how to integrate `nucypher-ts` into your favorite web framework. We also provide two code samples of TAC applications: + - [nucypher/tdec-sandbox](https://github.com/nucypher/tdec-sandbox) - [nucypher/tdec-nft-example](https://github.com/nucypher/tdec-nft-example) diff --git a/package.json b/package.json index eef0e6f90..bfa464554 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "test:lint": "eslint src test --ext .ts", "test:exports": "ts-unused-exports tsconfig.json --ignoreFiles src/index.ts", "test:prettier": "prettier \"src/**/*.ts\" \"test/**/*.ts\" --list-different", - "test:unit": "jest", + "test:unit": "jest --detectOpenHandles --forceExit --runInBand", "watch:build": "tsc -p tsconfig.json -w", "watch:test": "jest --watch", "cov": "run-s build test:unit && open-cli coverage/index.html", diff --git a/test/acceptance/alice-grants.test.ts b/test/acceptance/alice-grants.test.ts index 8ccf532fc..f4414e2bb 100644 --- a/test/acceptance/alice-grants.test.ts +++ b/test/acceptance/alice-grants.test.ts @@ -12,6 +12,7 @@ import { toBytes } from '../../src/utils'; import { bytesEqual, fromBytes, + makeTestUrsulas, mockAlice, mockBob, mockEncryptTreasureMap, @@ -21,7 +22,6 @@ import { mockPublishToBlockchain, mockRemoteBob, mockRetrieveCFragsRequest, - mockUrsulas, reencryptKFrags, } from '../utils'; @@ -31,7 +31,7 @@ describe('story: alice shares message with bob through policy', () => { const shares = 3; const startDate = new Date(); const endDate = new Date(Date.now() + 60 * 1000); - const mockedUrsulas = mockUrsulas().slice(0, shares); + const mockedUrsulas = makeTestUrsulas().slice(0, shares); // Intermediate variables used for mocking let encryptedTreasureMap: EncryptedTreasureMap; diff --git a/test/acceptance/delay-enact.test.ts b/test/acceptance/delay-enact.test.ts index 9e76b7d5a..151719f6f 100644 --- a/test/acceptance/delay-enact.test.ts +++ b/test/acceptance/delay-enact.test.ts @@ -1,11 +1,11 @@ import { + makeTestUrsulas, mockAlice, mockEncryptTreasureMap, mockGenerateKFrags, mockGetUrsulas, mockPublishToBlockchain, mockRemoteBob, - mockUrsulas, } from '../utils'; describe('story: alice1 creates a policy but alice2 enacts it', () => { @@ -13,7 +13,7 @@ describe('story: alice1 creates a policy but alice2 enacts it', () => { const shares = 3; const startDate = new Date(); const endDate = new Date(Date.now() + 60 * 1000); // 60s later - const mockedUrsulas = mockUrsulas().slice(0, shares); + const mockedUrsulas = makeTestUrsulas().slice(0, shares); const label = 'fake-data-label'; it('alice generates a new policy', async () => { diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 79fb91b4e..956448355 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -11,6 +11,7 @@ import { import { Ursula } from '../../src/characters/porter'; import { toBytes } from '../../src/utils'; import { + makeTestUrsulas, mockDetectEthereumProvider, mockEncryptTreasureMap, mockGenerateKFrags, @@ -18,7 +19,6 @@ import { mockMakeTreasureMap, mockPublishToBlockchain, mockRetrieveCFragsRequest, - mockUrsulas, mockWeb3Provider, } from '../utils'; @@ -42,7 +42,7 @@ describe('Get Started (CBD PoC)', () => { it('can run the get started example', async () => { const detectEthereumProvider = mockDetectEthereumProvider(); - const mockedUrsulas = mockUrsulas(); + const mockedUrsulas = makeTestUrsulas(); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -126,7 +126,7 @@ describe('Get Started (CBD PoC)', () => { // End of the code example // - const expectedAddresses = mockUrsulas().map((u) => u.checksumAddress); + const expectedAddresses = makeTestUrsulas().map((u) => u.checksumAddress); const condObj = conditions.conditions[0].toObj(); expect(newCohort.ursulaAddresses).toEqual(expectedAddresses); expect(condObj.parameters).toEqual([5954]); diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index c896c7b6c..b5bf3c311 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -10,13 +10,13 @@ import { } from '../../src'; import { RetrievalResult } from '../../src/kits/retrieval'; import { toBytes, zip } from '../../src/utils'; -import { mockAlice, mockBob, mockUrsulas, reencryptKFrags } from '../utils'; +import { makeTestUrsulas, mockAlice, mockBob, reencryptKFrags } from '../utils'; describe('proxy reencryption', () => { const plaintext = toBytes('plaintext-message'); const threshold = 2; const shares = 3; - const ursulas = mockUrsulas().slice(0, shares); + const ursulas = makeTestUrsulas().slice(0, shares); const label = 'fake-data-label'; const alice = mockAlice(); const bob = mockBob(); diff --git a/test/unit/cohort.test.ts b/test/unit/cohort.test.ts index 54ed84457..7cfd66eea 100644 --- a/test/unit/cohort.test.ts +++ b/test/unit/cohort.test.ts @@ -1,9 +1,9 @@ -import { Cohort } from '../../src/sdk/cohort'; -import { mockGetUrsulas, mockUrsulas } from '../utils'; +import { Cohort } from '../../src'; +import { makeTestUrsulas, mockGetUrsulas } from '../utils'; describe('Cohort', () => { it('can create Cohort from configuration', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const config = { @@ -23,7 +23,7 @@ describe('Cohort', () => { }); it('can export to JSON', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const config = { threshold: 2, @@ -39,7 +39,7 @@ describe('Cohort', () => { }); it('can import from JSON', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const configJSON = diff --git a/test/unit/strategy.test.ts b/test/unit/strategy.test.ts index 9c85c7ca0..38ada25b3 100644 --- a/test/unit/strategy.test.ts +++ b/test/unit/strategy.test.ts @@ -15,13 +15,13 @@ import { import { Ursula } from '../../src/characters/porter'; import { fromBase64, toBytes } from '../../src/utils'; import { + makeTestUrsulas, mockEncryptTreasureMap, mockGenerateKFrags, mockGetUrsulas, mockMakeTreasureMap, mockPublishToBlockchain, mockRetrieveCFragsRequest, - mockUrsulas, mockWeb3Provider, } from '../utils'; @@ -46,11 +46,11 @@ describe('Strategy', () => { Date.now = jest.fn(() => 1487076708000); afterEach(() => { - jest.clearAllMocks(); + jest.restoreAllMocks(); }); it('can create Strategy from configuration', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const testCohort = await Cohort.create(cohortConfig); @@ -61,26 +61,29 @@ describe('Strategy', () => { bobSecretKey ); - const expectedUrsulas = [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ]; + const expectedUrsulas = mockedUrsulas.map( + (ursula) => ursula.checksumAddress + ); expect(getUrsulasSpy).toHaveBeenCalled(); expect(testStrategy.cohort.ursulaAddresses).toEqual(expectedUrsulas); }); it('can export to JSON', async () => { + const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const testCohort = await Cohort.create(cohortConfig); const testStrategy = Strategy.create( testCohort, undefined, aliceSecretKey, - bobSecretKey + bobSecretKey, + new Date('2017-02-14T12:51:48.000Z'), + new Date('2123-03-16T12:51:48.000Z') ); const configJSON = testStrategy.toJSON(); expect(configJSON).toEqual(strategyJSON); + expect(getUrsulasSpy).toHaveBeenCalled(); }); it('can import from JSON', async () => { @@ -94,7 +97,7 @@ describe('Strategy', () => { }); it('can deploy and return DeployedStrategy', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -124,12 +127,12 @@ describe('Deployed Strategy', () => { const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); afterEach(() => { - jest.clearAllMocks(); + jest.restoreAllMocks(); }); it('can export to JSON', async () => { const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -143,7 +146,9 @@ describe('Deployed Strategy', () => { testCohort, undefined, aliceSecretKey, - bobSecretKey + bobSecretKey, + new Date('2017-02-14T12:51:48.000Z'), + new Date('2123-03-16T12:51:48.000Z') ); const testDeployed = await testStrategy.deploy('test', aliceProvider); expect(getUrsulasSpy).toHaveBeenCalled(); @@ -165,7 +170,7 @@ describe('Deployed Strategy', () => { it('can encrypt and decrypt', async () => { const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); const bobProvider = mockWeb3Provider(bobSecretKey.toBEBytes()); - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index bc315b18c..34c1cc099 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -12,7 +12,7 @@ export const strategyJSON = JSON.stringify({ aliceSecretKeyBytes: 'base64:N1K+vcukPJQkVi57P5jXca5W9CwX48VEBVV0H9CYWDU=', bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', startDate: '2017-02-14T12:51:48.000Z', - endDate: '2017-03-16T12:51:48.000Z', + endDate: '2123-03-16T12:51:48.000Z', }); export const encryptedTreasureMapBase64 = @@ -28,7 +28,7 @@ export const deployedStrategyJSON = JSON.stringify({ aliceVerifyingKey: 'base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj', size: 3, startTimestamp: '2017-02-14T12:51:48.000Z', - endTimestamp: '2017-03-16T12:51:48.000Z', + endTimestamp: '2123-03-16T12:51:48.000Z', txHash: '0x1234567890123456789012345678901234567890', }, cohortConfig: { diff --git a/test/utils.ts b/test/utils.ts index 13f0bb504..f4a0bac71 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -6,6 +6,7 @@ import { Capsule, CapsuleFrag, EncryptedTreasureMap, + PublicKey, reencrypt, SecretKey, VerifiedCapsuleFrag, @@ -80,7 +81,7 @@ export const mockWeb3Provider = ( } as unknown as ethers.providers.Web3Provider; }; -export const mockUrsulas = (): readonly Ursula[] => { +export const makeTestUrsulas = (): readonly Ursula[] => { return [ { encryptingKey: SecretKey.random().publicKey(), @@ -148,11 +149,6 @@ export const mockCFragResponse = ( verifiedKFrags: readonly VerifiedKeyFrag[], capsule: Capsule ): readonly RetrieveCFragsResponse[] => { - if (ursulas.length !== verifiedKFrags.length) { - throw new Error( - 'Number of verifiedKFrags must match the number of Ursulas' - ); - } const reencrypted = verifiedKFrags .map((kFrag) => reencrypt(capsule, kFrag)) .map((cFrag) => CapsuleFrag.fromBytes(cFrag.toBytes())); @@ -179,8 +175,15 @@ export const mockRetrieveCFragsRequestThrows = () => { .mockRejectedValue(new Error('fake-reencryption-request-failed-error')); }; -export const mockGenerateKFrags = () => { - return jest.spyOn(Alice.prototype as any, 'generateKFrags'); +export const mockGenerateKFrags = (withValue?: { + delegatingKey: PublicKey; + verifiedKFrags: VerifiedKeyFrag[]; +}) => { + const spy = jest.spyOn(Alice.prototype as any, 'generateKFrags'); + if (withValue) { + return spy.mockImplementation(() => withValue); + } + return spy; }; export const mockEncryptTreasureMap = (withValue?: EncryptedTreasureMap) => { From af6bba61161e962bb3e6414b0eb5901c00105f57 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 3 May 2023 12:15:49 +0200 Subject: [PATCH 03/98] bump nucypher-core to 0.7.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index bfa464554..23a48e6e2 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "prebuild": "yarn typechain" }, "dependencies": { - "@nucypher/nucypher-core": "^0.6.0", + "@nucypher/nucypher-core": "^0.7.0", "axios": "^0.21.1", "ethers": "^5.4.1", "joi": "^17.7.0", diff --git a/yarn.lock b/yarn.lock index 30f8d5744..98cab979a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1643,10 +1643,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nucypher/nucypher-core@^0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.6.0.tgz#07f60e2ba084efe3174afa7eefcb75ba58a0ba39" - integrity sha512-6S+x3J8vTrcXowtPl0GtprF7WgyP3lsSSG3+AYJXS17Sz9n2UeUdfWIxXrXt4bPj27w0sfgwYfXDoVhS/DP68A== +"@nucypher/nucypher-core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.7.0.tgz#2549364f39087a32be3abd8510baa45c1089d205" + integrity sha512-L8jDHIkEPgK4rPOwf7UkePGI/AHnP77TyE7iktO1G/bMGPZzsZ4YVg0Tw13T/3mPLEVjFOFZqA+h75wK3rHU6g== "@sideway/address@^4.1.3": version "4.1.4" From 4c35dd69feb18a69dd8ba8014584cba8ac6c780f Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 3 May 2023 13:32:40 +0200 Subject: [PATCH 04/98] add ferveo-wasm to deps --- package.json | 1 + test/unit/ferveo-example.test.ts | 182 +++++++++++++++++++++++++++++++ yarn.lock | 5 + 3 files changed, 188 insertions(+) create mode 100644 test/unit/ferveo-example.test.ts diff --git a/package.json b/package.json index 23a48e6e2..fc23a9f88 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ }, "dependencies": { "@nucypher/nucypher-core": "^0.7.0", + "ferveo-wasm": "^0.1.0", "axios": "^0.21.1", "ethers": "^5.4.1", "joi": "^17.7.0", diff --git a/test/unit/ferveo-example.test.ts b/test/unit/ferveo-example.test.ts new file mode 100644 index 000000000..95f229872 --- /dev/null +++ b/test/unit/ferveo-example.test.ts @@ -0,0 +1,182 @@ +import { + AggregatedTranscript, + combineDecryptionSharesPrecomputed, + combineDecryptionSharesSimple, + DecryptionSharePrecomputed, + DecryptionShareSimple, + decryptWithSharedSecret, + Dkg, + encrypt, + EthereumAddress, + Keypair, + Validator, + ValidatorMessage, +} from 'ferveo-wasm'; + +const zip = (a: Array, b: Array): Array<[A1, A2]> => + a.map((k: A1, i: number) => [k, b[i]]); + +const genEthAddr = (i: number) => { + const ethAddr = + '0x' + '0'.repeat(40 - i.toString(16).length) + i.toString(16); + return EthereumAddress.fromString(ethAddr); +}; + +function setupTest() { + const tau = 1; + const sharesNum = 4; + const threshold = Math.floor((sharesNum * 2) / 3); + + const validator_keypairs: Keypair[] = []; + const validators: Validator[] = []; + for (let i = 0; i < sharesNum; i++) { + const keypair = Keypair.random(); + validator_keypairs.push(keypair); + const validator = new Validator(genEthAddr(i), keypair.publicKey); + validators.push(validator); + } + + // Each validator holds their own DKG instance and generates a transcript every + // validator, including themselves + const messages: ValidatorMessage[] = []; + validators.forEach((sender) => { + const dkg = new Dkg(tau, sharesNum, threshold, validators, sender); + const transcript = dkg.generateTranscript(); + const message = new ValidatorMessage(sender, transcript); + messages.push(message); + }); + + // Now that every validator holds a dkg instance and a transcript for every other validator, + // every validator can aggregate the transcripts + const dkg = new Dkg(tau, sharesNum, threshold, validators, validators[0]); + + // Let's say that we've only received `threshold` transcripts + const receivedMessages = messages.slice(0, threshold); + + const serverAggregate = dkg.aggregateTranscript(receivedMessages); + expect(serverAggregate.verify(sharesNum, receivedMessages)).toBe(true); + + // Client can also aggregate the transcripts and verify them + const clientAggregate = new AggregatedTranscript(receivedMessages); + expect(clientAggregate.verify(sharesNum, receivedMessages)).toBe(true); + + // In the meantime, the client creates a ciphertext and decryption request + const msg = Buffer.from('my-msg'); + const aad = Buffer.from('my-aad'); + const ciphertext = encrypt(msg, aad, dkg.finalKey()); + + return { + tau, + sharesNum, + threshold, + validator_keypairs, + validators, + dkg, + receivedMessages, + msg, + aad, + ciphertext, + }; +} + +// This test suite replicates tests from ferveo-wasm/tests/node.rs +describe('ferveo-wasm', () => { + it('simple tdec variant', () => { + const { + tau, + sharesNum, + threshold, + validator_keypairs, + validators, + dkg, + receivedMessages, + msg, + aad, + ciphertext, + } = setupTest(); + + // Having aggregated the transcripts, the validators can now create decryption shares + const decryptionShares: DecryptionShareSimple[] = []; + zip(validators, validator_keypairs).forEach(([validator, keypair]) => { + expect(validator.publicKey.equals(keypair.publicKey)).toBe(true); + + const dkg = new Dkg(tau, sharesNum, threshold, validators, validator); + const aggregate = dkg.aggregateTranscript(receivedMessages); + const isValid = aggregate.verify(sharesNum, receivedMessages); + expect(isValid).toBe(true); + + const decryptionShare = aggregate.createDecryptionShareSimple( + dkg, + ciphertext, + aad, + keypair + ); + decryptionShares.push(decryptionShare); + }); + + // Now, the decryption share can be used to decrypt the ciphertext + // This part is in the client API + + const sharedSecret = combineDecryptionSharesSimple( + decryptionShares, + dkg.publicParams() + ); + + // The client should have access to the public parameters of the DKG + + const plaintext = decryptWithSharedSecret( + ciphertext, + aad, + sharedSecret, + dkg.publicParams() + ); + expect(Buffer.from(plaintext)).toEqual(msg); + }); + + it('precomputed tdec variant', () => { + const { + tau, + sharesNum, + threshold, + validator_keypairs, + validators, + dkg, + receivedMessages, + msg, + aad, + ciphertext, + } = setupTest(); + + // Having aggregated the transcripts, the validators can now create decryption shares + const decryptionShares: DecryptionSharePrecomputed[] = []; + zip(validators, validator_keypairs).forEach(([validator, keypair]) => { + const dkg = new Dkg(tau, sharesNum, threshold, validators, validator); + const aggregate = dkg.aggregateTranscript(receivedMessages); + const isValid = aggregate.verify(sharesNum, receivedMessages); + expect(isValid).toBe(true); + + const decryptionShare = aggregate.createDecryptionSharePrecomputed( + dkg, + ciphertext, + aad, + keypair + ); + decryptionShares.push(decryptionShare); + }); + + // Now, the decryption share can be used to decrypt the ciphertext + // This part is in the client API + + const sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares); + + // The client should have access to the public parameters of the DKG + + const plaintext = decryptWithSharedSecret( + ciphertext, + aad, + sharedSecret, + dkg.publicParams() + ); + expect(Buffer.from(plaintext)).toEqual(msg); + }); +}); diff --git a/yarn.lock b/yarn.lock index 98cab979a..c598cf309 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3419,6 +3419,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +ferveo-wasm@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ferveo-wasm/-/ferveo-wasm-0.1.0.tgz#03fa787ed1978f457d4847282d864778a3799f21" + integrity sha512-KcPU0QVU/GkXoDeL8mG63Mm6kKyCECmMyF4QGnPqcCSeEXVY+sno67Vk98I/1b372QWsjye/6Rm5J5HBI8icCQ== + figures@^3.0.0, figures@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" From 22583506e564d4525e632e35a3b70697b2e43eb9 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 3 May 2023 16:06:06 +0200 Subject: [PATCH 05/98] feat: add coordinator contract --- abi/Coordinator.json | 550 +++++++++++++++++++++++++++++ src/agents/contracts.ts | 19 +- src/agents/coordinator.ts | 31 ++ src/agents/subscription-manager.ts | 7 +- src/config.ts | 4 + src/types.ts | 1 + types/ethers-contracts/index.ts | 2 + 7 files changed, 610 insertions(+), 4 deletions(-) create mode 100644 abi/Coordinator.json create mode 100644 src/agents/coordinator.ts diff --git a/abi/Coordinator.json b/abi/Coordinator.json new file mode 100644 index 000000000..eb2c7f72c --- /dev/null +++ b/abi/Coordinator.json @@ -0,0 +1,550 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "uint32", + "name": "_timeout", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_maxDkgSize", + "type": "uint32" + }, + { + "internalType": "contract ApplicationInterface", + "name": "_application", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "node", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "aggregatedTranscriptDigest", + "type": "bytes32" + } + ], + "name": "AggregationPosted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "indexed": false, + "internalType": "enum Coordinator.RitualState", + "name": "status", + "type": "uint8" + } + ], + "name": "EndRitual", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "oldSize", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "newSize", + "type": "uint32" + } + ], + "name": "MaxDkgSizeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "StartAggregationRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "nodes", + "type": "address[]" + } + ], + "name": "StartRitual", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "StartTranscriptRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "oldTimeout", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "newTimeout", + "type": "uint32" + } + ], + "name": "TimeoutChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "node", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "transcriptDigest", + "type": "bytes32" + } + ], + "name": "TranscriptPosted", + "type": "event" + }, + { + "inputs": [], + "name": "PUBLIC_KEY_SIZE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "applicationInterface", + "outputs": [ + { + "internalType": "contract ApplicationInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "internalType": "address", + "name": "node", + "type": "address" + } + ], + "name": "getNodeIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "getParticipants", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "node", + "type": "address" + }, + { + "internalType": "bool", + "name": "aggregated", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "transcript", + "type": "bytes" + } + ], + "internalType": "struct Coordinator.Participant[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "ritualId", + "type": "uint256" + } + ], + "name": "getRitualState", + "outputs": [ + { + "internalType": "enum Coordinator.RitualState", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "nodes", + "type": "address[]" + } + ], + "name": "initiateRitual", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "maxDkgSize", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "numberOfRituals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "nodeIndex", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "aggregatedTranscript", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + } + ], + "name": "postAggregation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "nodeIndex", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "transcript", + "type": "bytes" + } + ], + "name": "postTranscript", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "rituals", + "outputs": [ + { + "internalType": "uint32", + "name": "id", + "type": "uint32" + }, + { + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "internalType": "uint32", + "name": "dkgSize", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "initTimestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "totalTranscripts", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "totalAggregations", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "aggregatedTranscriptHash", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "aggregationMismatch", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "aggregatedTranscript", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "publicKeyHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "newSize", + "type": "uint32" + } + ], + "name": "setMaxDkgSize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "newTimeout", + "type": "uint32" + } + ], + "name": "setTimeout", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "timeout", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] +} diff --git a/src/agents/contracts.ts b/src/agents/contracts.ts index f967d67d9..79c90bdaf 100644 --- a/src/agents/contracts.ts +++ b/src/agents/contracts.ts @@ -2,26 +2,41 @@ import { ChainId, ChecksumAddress } from '../types'; type Contracts = { readonly SUBSCRIPTION_MANAGER: ChecksumAddress; + readonly COORDINATOR: ChecksumAddress; }; const POLYGON: Contracts = { SUBSCRIPTION_MANAGER: '0xB0194073421192F6Cf38d72c791Be8729721A0b3', + COORDINATOR: '0x0', }; const MUMBAI: Contracts = { SUBSCRIPTION_MANAGER: '0xb9015d7b35ce7c81dde38ef7136baa3b1044f313', + COORDINATOR: '0x0', +}; + +const GOERLI: Contracts = { + SUBSCRIPTION_MANAGER: '0x0', + COORDINATOR: '0x2cf19429168a0943992D8e7dE534E9b802C687B6', }; const CONTRACTS: { readonly [key in ChainId]: Contracts } = { [ChainId.POLYGON]: POLYGON, [ChainId.MUMBAI]: MUMBAI, + [ChainId.GOERLI]: GOERLI, }; -export const getContracts = (chainId: number): Contracts => { +export const getContract = ( + chainId: number, + contract: keyof Contracts +): ChecksumAddress => { if (!Object.values(ChainId).includes(chainId)) { throw new Error(`No contracts found for chainId: ${chainId}`); } - return CONTRACTS[chainId as ChainId]; + if (!Object.keys(CONTRACTS[chainId as ChainId]).includes(contract)) { + throw new Error(`No contract found for name: ${contract}`); + } + return CONTRACTS[chainId as ChainId][contract]; }; export const DEFAULT_WAIT_N_CONFIRMATIONS = 1; diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts new file mode 100644 index 000000000..51c438322 --- /dev/null +++ b/src/agents/coordinator.ts @@ -0,0 +1,31 @@ +import { ethers } from 'ethers'; + +import { + Coordinator, + Coordinator__factory, +} from '../../types/ethers-contracts'; + +import { getContract } from './contracts'; + +export class CoordinatorAgent { + public static async getParticipants( + provider: ethers.providers.Provider, + ritualId: number + ): Promise { + const Coordinator = await this.connectReadOnly(provider); + return await Coordinator.getParticipants(ritualId); + } + + private static async connectReadOnly(provider: ethers.providers.Provider) { + return await this.connect(provider); + } + + private static async connect( + provider: ethers.providers.Provider, + signer?: ethers.providers.JsonRpcSigner + ): Promise { + const network = await provider.getNetwork(); + const contractAddress = getContract(network.chainId, 'COORDINATOR'); + return Coordinator__factory.connect(contractAddress, signer ?? provider); + } +} diff --git a/src/agents/subscription-manager.ts b/src/agents/subscription-manager.ts index 53bb61685..d05aaed3a 100644 --- a/src/agents/subscription-manager.ts +++ b/src/agents/subscription-manager.ts @@ -11,7 +11,7 @@ import { } from '../../types/ethers-contracts'; import { ChecksumAddress } from '../types'; -import { DEFAULT_WAIT_N_CONFIRMATIONS, getContracts } from './contracts'; +import { DEFAULT_WAIT_N_CONFIRMATIONS, getContract } from './contracts'; export class SubscriptionManagerAgent { public static async createPolicy( @@ -76,7 +76,10 @@ export class SubscriptionManagerAgent { signer?: ethers.providers.JsonRpcSigner ): Promise { const network = await provider.getNetwork(); - const contractAddress = getContracts(network.chainId).SUBSCRIPTION_MANAGER; + const contractAddress = getContract( + network.chainId, + 'SUBSCRIPTION_MANAGER' + ); return SubscriptionManager__factory.connect( contractAddress, signer ?? provider diff --git a/src/config.ts b/src/config.ts index 32b969429..99bfb02cf 100644 --- a/src/config.ts +++ b/src/config.ts @@ -11,6 +11,10 @@ const CONFIGS: { readonly [key in ChainId]: Configuration } = { [ChainId.MUMBAI]: { porterUri: 'https://porter-tapir.nucypher.community', }, + [ChainId.GOERLI]: { + // TODO: Confirm this is correct + porterUri: 'https://porter-tapir.nucypher.community', + }, }; export const defaultConfiguration = (chainId: number): Configuration => { diff --git a/src/types.ts b/src/types.ts index 876ff745b..5fedf560e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,4 +5,5 @@ export type Base64EncodedBytes = string; export enum ChainId { POLYGON = 137, MUMBAI = 80001, + GOERLI = 5, } diff --git a/types/ethers-contracts/index.ts b/types/ethers-contracts/index.ts index 8bb492b58..a72a65e49 100644 --- a/types/ethers-contracts/index.ts +++ b/types/ethers-contracts/index.ts @@ -1,6 +1,8 @@ /* Autogenerated file. Do not edit manually. */ /* tslint:disable */ /* eslint-disable */ +export type { Coordinator } from "./Coordinator"; export type { SubscriptionManager } from "./SubscriptionManager"; +export { Coordinator__factory } from "./factories/Coordinator__factory"; export { SubscriptionManager__factory } from "./factories/SubscriptionManager__factory"; From 6f3eaf64d6b7c419d7b1159cd0a1a3264266e1ea Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 4 May 2023 11:30:31 +0200 Subject: [PATCH 06/98] feat: add bare-bones impl of dkg client --- examples/api-example.ts | 9 +- package.json | 2 +- src/agents/coordinator.ts | 38 +- src/agents/subscription-manager.ts | 2 +- src/dkg.ts | 41 + src/policies/policy.ts | 6 +- test/acceptance/alice-grants.test.ts | 16 +- test/acceptance/delay-enact.test.ts | 14 +- test/docs/cbd.test.ts | 10 +- test/integration/conditions.test.ts | 4 +- test/integration/dkg-client.test.ts | 47 + test/integration/enrico.test.ts | 14 +- test/integration/message-kit.test.ts | 4 +- test/integration/pre.test.ts | 8 +- test/unit/cohort.test.ts | 8 +- test/unit/ferveo-example.test.ts | 182 --- test/unit/strategy.test.ts | 22 +- test/utils.ts | 207 +++- yarn.lock | 1546 ++++++++++++++------------ 19 files changed, 1218 insertions(+), 962 deletions(-) create mode 100644 src/dkg.ts create mode 100644 test/integration/dkg-client.test.ts delete mode 100644 test/unit/ferveo-example.test.ts diff --git a/examples/api-example.ts b/examples/api-example.ts index 6991b2a66..0df37e9ea 100644 --- a/examples/api-example.ts +++ b/examples/api-example.ts @@ -1,7 +1,7 @@ import { BlockchainPolicyParameters, Enrico } from '../src'; import { toBytes } from '../src/utils'; -import { fromBytes, mockAlice, mockBob, mockRemoteBob } from '../test/utils'; +import { fromBytes, fakeAlice, fakeBob, fakeRemoteBob } from '../test/utils'; /** * This is an abridged version of PRE API usage example. @@ -10,9 +10,9 @@ import { fromBytes, mockAlice, mockBob, mockRemoteBob } from '../test/utils'; async function example() { // Alice creates a policy and grants access to Bob - const alice = mockAlice(); + const alice = fakeAlice(); // `remoteBob` is a just a container for public keys of Bob - const remoteBob = mockRemoteBob(); + const remoteBob = fakeRemoteBob(); const policyParams: BlockchainPolicyParameters = { bob: remoteBob, label: 'fake-data-label', @@ -29,7 +29,7 @@ async function example() { const encryptedMessage = enrico.encryptMessage(toBytes(message)); // Bob retrieves & decrypts encrypted message - const bob = mockBob(); + const bob = fakeBob(); const retrievedMessage = await bob.retrieveAndDecrypt( policy.policyKey, alice.verifyingKey, @@ -39,4 +39,3 @@ async function example() { const bobPlaintext = fromBytes(retrievedMessage[0]); console.log(bobPlaintext); } - diff --git a/package.json b/package.json index fc23a9f88..763ab07db 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ }, "dependencies": { "@nucypher/nucypher-core": "^0.7.0", - "ferveo-wasm": "^0.1.0", + "ferveo-wasm": "^0.1.1", "axios": "^0.21.1", "ethers": "^5.4.1", "joi": "^17.7.0", diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts index 51c438322..159492ff9 100644 --- a/src/agents/coordinator.ts +++ b/src/agents/coordinator.ts @@ -7,13 +7,45 @@ import { import { getContract } from './contracts'; -export class CoordinatorAgent { +export interface DkgRitual { + id: number; + initiator: string; + dkgSize: number; + initTimestamp: number; + totalTranscripts: number; + totalAggregations: number; + aggregatedTranscriptHash: string; + aggregationMismatch: boolean; + aggregatedTranscript: string; + publicKey: string; + publicKeyHash: string; +} + +export interface DkgParticipant { + node: string; + aggregated: boolean; + transcript: string; + publicKey: string; +} + +export class DkgCoordinatorAgent { public static async getParticipants( provider: ethers.providers.Provider, ritualId: number - ): Promise { + ): Promise { + const Coordinator = await this.connectReadOnly(provider); + // TODO: Remove `as unknown` cast after regenerating the contract types: https://github.com/nucypher/nucypher-contracts/pull/77 + return (await Coordinator.getParticipants( + ritualId + )) as unknown as DkgParticipant[]; + } + + public static async getRitual( + provider: ethers.providers.Provider, + ritualId: number + ): Promise { const Coordinator = await this.connectReadOnly(provider); - return await Coordinator.getParticipants(ritualId); + return await Coordinator.rituals(ritualId); } private static async connectReadOnly(provider: ethers.providers.Provider) { diff --git a/src/agents/subscription-manager.ts b/src/agents/subscription-manager.ts index d05aaed3a..ac8a5a3c5 100644 --- a/src/agents/subscription-manager.ts +++ b/src/agents/subscription-manager.ts @@ -13,7 +13,7 @@ import { ChecksumAddress } from '../types'; import { DEFAULT_WAIT_N_CONFIRMATIONS, getContract } from './contracts'; -export class SubscriptionManagerAgent { +export class PreSubscriptionManagerAgent { public static async createPolicy( web3Provider: ethers.providers.Web3Provider, valueInWei: BigNumber, diff --git a/src/dkg.ts b/src/dkg.ts new file mode 100644 index 000000000..1230babd5 --- /dev/null +++ b/src/dkg.ts @@ -0,0 +1,41 @@ +import { ethers } from 'ethers'; +import { + AggregatedTranscript, + EthereumAddress, + PublicKey as FerveoPublicKey, + Transcript, + Validator, + ValidatorMessage, +} from 'ferveo-wasm'; + +import { DkgCoordinatorAgent } from './agents/coordinator'; +import { fromHexString } from './utils'; + +export class DkgClient { + constructor( + private readonly provider: ethers.providers.Web3Provider, + public readonly ritualId: number + ) {} + + public async verifyRitual(): Promise { + const ritual = await DkgCoordinatorAgent.getRitual( + this.provider, + this.ritualId + ); + const participants = await DkgCoordinatorAgent.getParticipants( + this.provider, + this.ritualId + ); + + const validatorMessages = participants.map((p) => { + const validatorAddress = EthereumAddress.fromString(p.node); + const publicKey = FerveoPublicKey.fromBytes(fromHexString(p.publicKey)); + const validator = new Validator(validatorAddress, publicKey); + const transcript = Transcript.fromBytes(fromHexString(p.transcript)); + return new ValidatorMessage(validator, transcript); + }); + const aggregate = new AggregatedTranscript(validatorMessages); + + return aggregate.verify(ritual.dkgSize, validatorMessages); + } +} diff --git a/src/policies/policy.ts b/src/policies/policy.ts index dbb23eccb..0e01a6926 100644 --- a/src/policies/policy.ts +++ b/src/policies/policy.ts @@ -7,7 +7,7 @@ import { VerifiedKeyFrag, } from '@nucypher/nucypher-core'; -import { SubscriptionManagerAgent } from '../agents/subscription-manager'; +import { PreSubscriptionManagerAgent } from '../agents/subscription-manager'; import { Alice } from '../characters/alice'; import { RemoteBob } from '../characters/bob'; import { Ursula } from '../characters/porter'; @@ -64,13 +64,13 @@ export class PreEnactedPolicy implements IPreEnactedPolicy { const startTimestamp = toEpoch(this.startTimestamp); const endTimestamp = toEpoch(this.endTimestamp); const ownerAddress = await publisher.web3Provider.getSigner().getAddress(); - const value = await SubscriptionManagerAgent.getPolicyCost( + const value = await PreSubscriptionManagerAgent.getPolicyCost( publisher.web3Provider, this.size, startTimestamp, endTimestamp ); - const tx = await SubscriptionManagerAgent.createPolicy( + const tx = await PreSubscriptionManagerAgent.createPolicy( publisher.web3Provider, value, this.id.toBytes(), diff --git a/test/acceptance/alice-grants.test.ts b/test/acceptance/alice-grants.test.ts index f4414e2bb..c234c1078 100644 --- a/test/acceptance/alice-grants.test.ts +++ b/test/acceptance/alice-grants.test.ts @@ -11,16 +11,16 @@ import { ChecksumAddress } from '../../src/types'; import { toBytes } from '../../src/utils'; import { bytesEqual, + fakeAlice, + fakeBob, + fakeRemoteBob, + fakeUrsulas, fromBytes, - makeTestUrsulas, - mockAlice, - mockBob, mockEncryptTreasureMap, mockGenerateKFrags, mockGetUrsulas, mockMakeTreasureMap, mockPublishToBlockchain, - mockRemoteBob, mockRetrieveCFragsRequest, reencryptKFrags, } from '../utils'; @@ -31,7 +31,7 @@ describe('story: alice shares message with bob through policy', () => { const shares = 3; const startDate = new Date(); const endDate = new Date(Date.now() + 60 * 1000); - const mockedUrsulas = makeTestUrsulas().slice(0, shares); + const mockedUrsulas = fakeUrsulas().slice(0, shares); // Intermediate variables used for mocking let encryptedTreasureMap: EncryptedTreasureMap; @@ -52,8 +52,8 @@ describe('story: alice shares message with bob through policy', () => { const makeTreasureMapSpy = mockMakeTreasureMap(); const encryptTreasureMapSpy = mockEncryptTreasureMap(); - const alice = mockAlice(); - const bob = mockRemoteBob(); + const alice = fakeAlice(); + const bob = fakeRemoteBob(); const policyParams = { bob, label, @@ -92,7 +92,7 @@ describe('story: alice shares message with bob through policy', () => { }); it('bob retrieves and decrypts the message', async () => { - const bob = mockBob(); + const bob = fakeBob(); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const retrieveCFragsSpy = mockRetrieveCFragsRequest( ursulaAddresses, diff --git a/test/acceptance/delay-enact.test.ts b/test/acceptance/delay-enact.test.ts index 151719f6f..ddd99d1b7 100644 --- a/test/acceptance/delay-enact.test.ts +++ b/test/acceptance/delay-enact.test.ts @@ -1,11 +1,11 @@ import { - makeTestUrsulas, - mockAlice, + fakeAlice, + fakeRemoteBob, + fakeUrsulas, mockEncryptTreasureMap, mockGenerateKFrags, mockGetUrsulas, mockPublishToBlockchain, - mockRemoteBob, } from '../utils'; describe('story: alice1 creates a policy but alice2 enacts it', () => { @@ -13,7 +13,7 @@ describe('story: alice1 creates a policy but alice2 enacts it', () => { const shares = 3; const startDate = new Date(); const endDate = new Date(Date.now() + 60 * 1000); // 60s later - const mockedUrsulas = makeTestUrsulas().slice(0, shares); + const mockedUrsulas = fakeUrsulas().slice(0, shares); const label = 'fake-data-label'; it('alice generates a new policy', async () => { @@ -22,9 +22,9 @@ describe('story: alice1 creates a policy but alice2 enacts it', () => { const publishToBlockchainSpy = mockPublishToBlockchain(); const encryptTreasureMapSpy = mockEncryptTreasureMap(); - const alice1 = mockAlice('fake-secret-key-32-bytes-alice-1'); - const alice2 = mockAlice('fake-secret-key-32-bytes-alice-2'); - const bob = mockRemoteBob(); + const alice1 = fakeAlice('fake-secret-key-32-bytes-alice-1'); + const alice2 = fakeAlice('fake-secret-key-32-bytes-alice-2'); + const bob = fakeRemoteBob(); const policyParams = { bob, label, diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 956448355..630091eb1 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -11,7 +11,8 @@ import { import { Ursula } from '../../src/characters/porter'; import { toBytes } from '../../src/utils'; import { - makeTestUrsulas, + fakeUrsulas, + fakeWeb3Provider, mockDetectEthereumProvider, mockEncryptTreasureMap, mockGenerateKFrags, @@ -19,7 +20,6 @@ import { mockMakeTreasureMap, mockPublishToBlockchain, mockRetrieveCFragsRequest, - mockWeb3Provider, } from '../utils'; describe('Get Started (CBD PoC)', () => { @@ -42,7 +42,7 @@ describe('Get Started (CBD PoC)', () => { it('can run the get started example', async () => { const detectEthereumProvider = mockDetectEthereumProvider(); - const mockedUrsulas = makeTestUrsulas(); + const mockedUrsulas = fakeUrsulas(); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -52,7 +52,7 @@ describe('Get Started (CBD PoC)', () => { jest .spyOn(providers, 'Web3Provider') .mockImplementation(() => - mockWeb3Provider(SecretKey.random().toBEBytes()) + fakeWeb3Provider(SecretKey.random().toBEBytes()) ); // @@ -126,7 +126,7 @@ describe('Get Started (CBD PoC)', () => { // End of the code example // - const expectedAddresses = makeTestUrsulas().map((u) => u.checksumAddress); + const expectedAddresses = fakeUrsulas().map((u) => u.checksumAddress); const condObj = conditions.conditions[0].toObj(); expect(newCohort.ursulaAddresses).toEqual(expectedAddresses); expect(condObj.parameters).toEqual([5954]); diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 373a2d54e..870f1c1dc 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -6,7 +6,7 @@ import { ConditionSet, Operator, } from '../../src'; -import { mockWeb3Provider } from '../utils'; +import { fakeWeb3Provider } from '../utils'; describe('operator', () => { it('should validate Operator', async () => { @@ -239,7 +239,7 @@ describe('produce context parameters from conditions', () => { describe('condition context', () => { it('should serialize to JSON with context params', async () => { - const web3Provider = mockWeb3Provider(SecretKey.random().toBEBytes()); + const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); const rpcCondition = new Conditions.RpcCondition({ chain: 5, diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts new file mode 100644 index 000000000..13a76aa82 --- /dev/null +++ b/test/integration/dkg-client.test.ts @@ -0,0 +1,47 @@ +import { SecretKey } from '@nucypher/nucypher-core'; + +import { DkgCoordinatorAgent } from '../../src/agents/coordinator'; +import { DkgClient } from '../../src/dkg'; +import { fakeDkgParticipants, fakeDkgRitual, fakeWeb3Provider } from '../utils'; + +const ritualId = 0; + +jest.mock('../../src/agents/coordinator', () => ({ + DkgCoordinatorAgent: { + getRitual: () => Promise.resolve(fakeDkgRitual(ritualId)), + getParticipants: () => Promise.resolve(fakeDkgParticipants()), + }, +})); + +describe('DkgCoordinatorAgent', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('fetches transcripts from the coordinator', async () => { + const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + const ritual = await DkgCoordinatorAgent.getRitual(provider, ritualId); + + expect(ritual.id).toEqual(ritualId); + }); + + it('fetches participants from the coordinator', async () => { + const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + const participants = await DkgCoordinatorAgent.getParticipants( + provider, + ritualId + ); + + expect(participants.length).toBeGreaterThan(0); + }); +}); + +describe('DkgClient', () => { + it('verifies the dkg ritual', async () => { + const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + + const dkgClient = new DkgClient(provider, ritualId); + const isValid = await dkgClient.verifyRitual(); + expect(isValid).toBe(true); + }); +}); diff --git a/test/integration/enrico.test.ts b/test/integration/enrico.test.ts index fb57894c4..c45c2e452 100644 --- a/test/integration/enrico.test.ts +++ b/test/integration/enrico.test.ts @@ -4,9 +4,9 @@ import { RetrievalResult } from '../../src/kits/retrieval'; import { toBytes } from '../../src/utils'; import { bytesEqual, + fakeAlice, + fakeBob, fromBytes, - mockAlice, - mockBob, reencryptKFrags, } from '../utils'; @@ -14,7 +14,7 @@ describe('enrico', () => { it('alice decrypts message encrypted by enrico', async () => { const label = 'fake-label'; const message = 'fake-message'; - const alice = mockAlice(); + const alice = fakeAlice(); const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); const enrico = new Enrico(policyKey); @@ -28,8 +28,8 @@ describe('enrico', () => { it('bob decrypts reencrypted message', async () => { const label = 'fake-label'; - const alice = mockAlice(); - const bob = mockBob(); + const alice = fakeAlice(); + const bob = fakeBob(); const policyEncryptingKey = alice.getPolicyEncryptingKeyFromLabel(label); const enrico = new Enrico(policyEncryptingKey); @@ -89,7 +89,7 @@ describe('enrico', () => { it('enrico generates a message kit with conditions', async () => { const label = 'fake-label'; const message = 'fake-message'; - const alice = mockAlice(); + const alice = fakeAlice(); const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); @@ -112,7 +112,7 @@ describe('enrico', () => { it('can overwrite conditions at encryption time', async () => { const label = 'fake-label'; const message = 'fake-message'; - const alice = mockAlice(); + const alice = fakeAlice(); const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); diff --git a/test/integration/message-kit.test.ts b/test/integration/message-kit.test.ts index c4070a0c0..16d27640b 100644 --- a/test/integration/message-kit.test.ts +++ b/test/integration/message-kit.test.ts @@ -1,10 +1,10 @@ import { MessageKit } from '../../src'; import { toBytes } from '../../src/utils'; -import { mockBob } from '../utils'; +import { fakeBob } from '../utils'; describe('message kit', () => { it('bob decrypts', () => { - const bob = mockBob(); + const bob = fakeBob(); const plaintext = toBytes('fake-message'); const messageKit = new MessageKit(bob.decryptingKey, plaintext, null); const decrypted = bob['keyring'].decrypt(messageKit); diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index b5bf3c311..ef3565a42 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -10,16 +10,16 @@ import { } from '../../src'; import { RetrievalResult } from '../../src/kits/retrieval'; import { toBytes, zip } from '../../src/utils'; -import { makeTestUrsulas, mockAlice, mockBob, reencryptKFrags } from '../utils'; +import { fakeAlice, fakeBob, fakeUrsulas, reencryptKFrags } from '../utils'; describe('proxy reencryption', () => { const plaintext = toBytes('plaintext-message'); const threshold = 2; const shares = 3; - const ursulas = makeTestUrsulas().slice(0, shares); + const ursulas = fakeUrsulas().slice(0, shares); const label = 'fake-data-label'; - const alice = mockAlice(); - const bob = mockBob(); + const alice = fakeAlice(); + const bob = fakeBob(); it('verifies capsule frags', async () => { const { capsule } = new MessageKit(bob.decryptingKey, plaintext, null); diff --git a/test/unit/cohort.test.ts b/test/unit/cohort.test.ts index 7cfd66eea..80ca256eb 100644 --- a/test/unit/cohort.test.ts +++ b/test/unit/cohort.test.ts @@ -1,9 +1,9 @@ import { Cohort } from '../../src'; -import { makeTestUrsulas, mockGetUrsulas } from '../utils'; +import { fakeUrsulas, mockGetUrsulas } from '../utils'; describe('Cohort', () => { it('can create Cohort from configuration', async () => { - const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const config = { @@ -23,7 +23,7 @@ describe('Cohort', () => { }); it('can export to JSON', async () => { - const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const config = { threshold: 2, @@ -39,7 +39,7 @@ describe('Cohort', () => { }); it('can import from JSON', async () => { - const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const configJSON = diff --git a/test/unit/ferveo-example.test.ts b/test/unit/ferveo-example.test.ts deleted file mode 100644 index 95f229872..000000000 --- a/test/unit/ferveo-example.test.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { - AggregatedTranscript, - combineDecryptionSharesPrecomputed, - combineDecryptionSharesSimple, - DecryptionSharePrecomputed, - DecryptionShareSimple, - decryptWithSharedSecret, - Dkg, - encrypt, - EthereumAddress, - Keypair, - Validator, - ValidatorMessage, -} from 'ferveo-wasm'; - -const zip = (a: Array, b: Array): Array<[A1, A2]> => - a.map((k: A1, i: number) => [k, b[i]]); - -const genEthAddr = (i: number) => { - const ethAddr = - '0x' + '0'.repeat(40 - i.toString(16).length) + i.toString(16); - return EthereumAddress.fromString(ethAddr); -}; - -function setupTest() { - const tau = 1; - const sharesNum = 4; - const threshold = Math.floor((sharesNum * 2) / 3); - - const validator_keypairs: Keypair[] = []; - const validators: Validator[] = []; - for (let i = 0; i < sharesNum; i++) { - const keypair = Keypair.random(); - validator_keypairs.push(keypair); - const validator = new Validator(genEthAddr(i), keypair.publicKey); - validators.push(validator); - } - - // Each validator holds their own DKG instance and generates a transcript every - // validator, including themselves - const messages: ValidatorMessage[] = []; - validators.forEach((sender) => { - const dkg = new Dkg(tau, sharesNum, threshold, validators, sender); - const transcript = dkg.generateTranscript(); - const message = new ValidatorMessage(sender, transcript); - messages.push(message); - }); - - // Now that every validator holds a dkg instance and a transcript for every other validator, - // every validator can aggregate the transcripts - const dkg = new Dkg(tau, sharesNum, threshold, validators, validators[0]); - - // Let's say that we've only received `threshold` transcripts - const receivedMessages = messages.slice(0, threshold); - - const serverAggregate = dkg.aggregateTranscript(receivedMessages); - expect(serverAggregate.verify(sharesNum, receivedMessages)).toBe(true); - - // Client can also aggregate the transcripts and verify them - const clientAggregate = new AggregatedTranscript(receivedMessages); - expect(clientAggregate.verify(sharesNum, receivedMessages)).toBe(true); - - // In the meantime, the client creates a ciphertext and decryption request - const msg = Buffer.from('my-msg'); - const aad = Buffer.from('my-aad'); - const ciphertext = encrypt(msg, aad, dkg.finalKey()); - - return { - tau, - sharesNum, - threshold, - validator_keypairs, - validators, - dkg, - receivedMessages, - msg, - aad, - ciphertext, - }; -} - -// This test suite replicates tests from ferveo-wasm/tests/node.rs -describe('ferveo-wasm', () => { - it('simple tdec variant', () => { - const { - tau, - sharesNum, - threshold, - validator_keypairs, - validators, - dkg, - receivedMessages, - msg, - aad, - ciphertext, - } = setupTest(); - - // Having aggregated the transcripts, the validators can now create decryption shares - const decryptionShares: DecryptionShareSimple[] = []; - zip(validators, validator_keypairs).forEach(([validator, keypair]) => { - expect(validator.publicKey.equals(keypair.publicKey)).toBe(true); - - const dkg = new Dkg(tau, sharesNum, threshold, validators, validator); - const aggregate = dkg.aggregateTranscript(receivedMessages); - const isValid = aggregate.verify(sharesNum, receivedMessages); - expect(isValid).toBe(true); - - const decryptionShare = aggregate.createDecryptionShareSimple( - dkg, - ciphertext, - aad, - keypair - ); - decryptionShares.push(decryptionShare); - }); - - // Now, the decryption share can be used to decrypt the ciphertext - // This part is in the client API - - const sharedSecret = combineDecryptionSharesSimple( - decryptionShares, - dkg.publicParams() - ); - - // The client should have access to the public parameters of the DKG - - const plaintext = decryptWithSharedSecret( - ciphertext, - aad, - sharedSecret, - dkg.publicParams() - ); - expect(Buffer.from(plaintext)).toEqual(msg); - }); - - it('precomputed tdec variant', () => { - const { - tau, - sharesNum, - threshold, - validator_keypairs, - validators, - dkg, - receivedMessages, - msg, - aad, - ciphertext, - } = setupTest(); - - // Having aggregated the transcripts, the validators can now create decryption shares - const decryptionShares: DecryptionSharePrecomputed[] = []; - zip(validators, validator_keypairs).forEach(([validator, keypair]) => { - const dkg = new Dkg(tau, sharesNum, threshold, validators, validator); - const aggregate = dkg.aggregateTranscript(receivedMessages); - const isValid = aggregate.verify(sharesNum, receivedMessages); - expect(isValid).toBe(true); - - const decryptionShare = aggregate.createDecryptionSharePrecomputed( - dkg, - ciphertext, - aad, - keypair - ); - decryptionShares.push(decryptionShare); - }); - - // Now, the decryption share can be used to decrypt the ciphertext - // This part is in the client API - - const sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares); - - // The client should have access to the public parameters of the DKG - - const plaintext = decryptWithSharedSecret( - ciphertext, - aad, - sharedSecret, - dkg.publicParams() - ); - expect(Buffer.from(plaintext)).toEqual(msg); - }); -}); diff --git a/test/unit/strategy.test.ts b/test/unit/strategy.test.ts index 38ada25b3..5c49eeaa3 100644 --- a/test/unit/strategy.test.ts +++ b/test/unit/strategy.test.ts @@ -15,14 +15,14 @@ import { import { Ursula } from '../../src/characters/porter'; import { fromBase64, toBytes } from '../../src/utils'; import { - makeTestUrsulas, + fakeUrsulas, + fakeWeb3Provider, mockEncryptTreasureMap, mockGenerateKFrags, mockGetUrsulas, mockMakeTreasureMap, mockPublishToBlockchain, mockRetrieveCFragsRequest, - mockWeb3Provider, } from '../utils'; import { @@ -42,7 +42,7 @@ describe('Strategy', () => { }; const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); - const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); + const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); Date.now = jest.fn(() => 1487076708000); afterEach(() => { @@ -50,7 +50,7 @@ describe('Strategy', () => { }); it('can create Strategy from configuration', async () => { - const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const testCohort = await Cohort.create(cohortConfig); @@ -69,7 +69,7 @@ describe('Strategy', () => { }); it('can export to JSON', async () => { - const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const testCohort = await Cohort.create(cohortConfig); const testStrategy = Strategy.create( @@ -97,7 +97,7 @@ describe('Strategy', () => { }); it('can deploy and return DeployedStrategy', async () => { - const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -131,8 +131,8 @@ describe('Deployed Strategy', () => { }); it('can export to JSON', async () => { - const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); - const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -168,9 +168,9 @@ describe('Deployed Strategy', () => { }); it('can encrypt and decrypt', async () => { - const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); - const bobProvider = mockWeb3Provider(bobSecretKey.toBEBytes()); - const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + const bobProvider = fakeWeb3Provider(bobSecretKey.toBEBytes()); + const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); diff --git a/test/utils.ts b/test/utils.ts index f4a0bac71..b735af163 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -14,8 +14,25 @@ import { } from '@nucypher/nucypher-core'; import axios from 'axios'; import { ethers, providers, Wallet } from 'ethers'; +import { keccak256 } from 'ethers/lib/utils'; +import { + AggregatedTranscript, + combineDecryptionSharesPrecomputed, + combineDecryptionSharesSimple, + DecryptionSharePrecomputed, + DecryptionShareSimple, + decryptWithSharedSecret, + Dkg, + encrypt, + EthereumAddress, + Keypair, + Transcript, + Validator, + ValidatorMessage, +} from 'ferveo-wasm'; import { Alice, Bob, Configuration, RemoteBob } from '../src'; +import { DkgParticipant, DkgRitual } from '../src/agents/coordinator'; import { GetUrsulasResponse, Porter, @@ -37,25 +54,25 @@ const mockConfig: Configuration = { porterUri: 'https://_this_should_crash.com/', }; -export const mockBob = (): Bob => { +export const fakeBob = (): Bob => { const secretKey = SecretKey.fromBEBytes( toBytes('fake-secret-key-32-bytes-bob-xxx') ); return Bob.fromSecretKey(mockConfig, secretKey); }; -export const mockRemoteBob = (): RemoteBob => { - const { decryptingKey, verifyingKey } = mockBob(); +export const fakeRemoteBob = (): RemoteBob => { + const { decryptingKey, verifyingKey } = fakeBob(); return RemoteBob.fromKeys(decryptingKey, verifyingKey); }; -export const mockAlice = (aliceKey = 'fake-secret-key-32-bytes-alice-x') => { +export const fakeAlice = (aliceKey = 'fake-secret-key-32-bytes-alice-x') => { const secretKey = SecretKey.fromBEBytes(toBytes(aliceKey)); - const provider = mockWeb3Provider(secretKey.toBEBytes()); + const provider = fakeWeb3Provider(secretKey.toBEBytes()); return Alice.fromSecretKey(mockConfig, secretKey, provider); }; -export const mockWeb3Provider = ( +export const fakeWeb3Provider = ( secretKeyBytes: Uint8Array, blockNumber?: number, blockTimestamp?: number @@ -66,7 +83,6 @@ export const mockWeb3Provider = ( getBlock: () => Promise.resolve(block as Block), _isProvider: true, getNetwork: () => Promise.resolve({ name: 'mockNetwork', chainId: -1 }), - request: () => '', }; const fakeSignerWithProvider = { ...new Wallet(secretKeyBytes), @@ -81,7 +97,7 @@ export const mockWeb3Provider = ( } as unknown as ethers.providers.Web3Provider; }; -export const makeTestUrsulas = (): readonly Ursula[] => { +export const fakeUrsulas = (): readonly Ursula[] => { return [ { encryptingKey: SecretKey.random().publicKey(), @@ -118,7 +134,7 @@ export const makeTestUrsulas = (): readonly Ursula[] => { }; export const mockGetUrsulas = (ursulas: readonly Ursula[]) => { - const mockPorterUrsulas = ( + const fakePorterUrsulas = ( mockUrsulas: readonly Ursula[] ): GetUrsulasResponse => { return { @@ -134,7 +150,7 @@ export const mockGetUrsulas = (ursulas: readonly Ursula[]) => { }; return jest.spyOn(axios, 'get').mockImplementation(async () => { - return Promise.resolve({ data: mockPorterUrsulas(ursulas) }); + return Promise.resolve({ data: fakePorterUrsulas(ursulas) }); }); }; export const mockPublishToBlockchain = () => { @@ -144,7 +160,7 @@ export const mockPublishToBlockchain = () => { .mockImplementation(async () => Promise.resolve(txHash)); }; -export const mockCFragResponse = ( +const fakeCFragResponse = ( ursulas: readonly ChecksumAddress[], verifiedKFrags: readonly VerifiedKeyFrag[], capsule: Capsule @@ -161,20 +177,13 @@ export const mockRetrieveCFragsRequest = ( verifiedKFrags: readonly VerifiedKeyFrag[], capsule: Capsule ) => { - const results = mockCFragResponse(ursulas, verifiedKFrags, capsule); + const results = fakeCFragResponse(ursulas, verifiedKFrags, capsule); return jest .spyOn(Porter.prototype, 'retrieveCFrags') .mockImplementation(() => { return Promise.resolve(results); }); }; - -export const mockRetrieveCFragsRequestThrows = () => { - return jest - .spyOn(Porter.prototype, 'retrieveCFrags') - .mockRejectedValue(new Error('fake-reencryption-request-failed-error')); -}; - export const mockGenerateKFrags = (withValue?: { delegatingKey: PublicKey; verifiedKFrags: VerifiedKeyFrag[]; @@ -219,3 +228,163 @@ export const mockDetectEthereumProvider = () => { return {} as unknown as providers.ExternalProvider; }); }; + +const fakeDkgRitualE2e = (variant: 'precomputed' | 'simple') => { + if (variant !== 'precomputed' && variant !== 'simple') { + throw new Error(`Invalid variant: ${variant}`); + } + + const genEthAddr = (i: number) => { + const ethAddr = + '0x' + '0'.repeat(40 - i.toString(16).length) + i.toString(16); + return EthereumAddress.fromString(ethAddr); + }; + + const tau = 1; + const sharesNum = 4; + const threshold = Math.floor((sharesNum * 2) / 3); + + const validator_keypairs: Keypair[] = []; + const validators: Validator[] = []; + for (let i = 0; i < sharesNum; i++) { + const keypair = Keypair.random(); + validator_keypairs.push(keypair); + const validator = new Validator(genEthAddr(i), keypair.publicKey); + validators.push(validator); + } + + // Each validator holds their own DKG instance and generates a transcript every + // validator, including themselves + const messages: ValidatorMessage[] = []; + const transcripts: Transcript[] = []; + validators.forEach((sender) => { + const dkg = new Dkg(tau, sharesNum, threshold, validators, sender); + const transcript = dkg.generateTranscript(); + transcripts.push(transcript); + const message = new ValidatorMessage(sender, transcript); + messages.push(message); + }); + + // Now that every validator holds a dkg instance and a transcript for every other validator, + // every validator can aggregate the transcripts + const dkg = new Dkg(tau, sharesNum, threshold, validators, validators[0]); + + // Let's say that we've only received `threshold` transcripts + const receivedMessages = messages.slice(0, threshold); + + const serverAggregate = dkg.aggregateTranscript(receivedMessages); + expect(serverAggregate.verify(sharesNum, receivedMessages)).toBe(true); + + // Client can also aggregate the transcripts and verify them + const clientAggregate = new AggregatedTranscript(receivedMessages); + expect(clientAggregate.verify(sharesNum, receivedMessages)).toBe(true); + + // In the meantime, the client creates a ciphertext and decryption request + const msg = Buffer.from('my-msg'); + const aad = Buffer.from('my-aad'); + const ciphertext = encrypt(msg, aad, dkg.finalKey()); + + // Having aggregated the transcripts, the validators can now create decryption shares + const decryptionShares: ( + | DecryptionSharePrecomputed + | DecryptionShareSimple + )[] = []; + zip(validators, validator_keypairs).forEach(([validator, keypair]) => { + const dkg = new Dkg(tau, sharesNum, threshold, validators, validator); + const aggregate = dkg.aggregateTranscript(receivedMessages); + const isValid = aggregate.verify(sharesNum, receivedMessages); + if (!isValid) { + throw new Error('Transcript is invalid'); + } + + let decryptionShare; + + if (variant === 'precomputed') { + decryptionShare = aggregate.createDecryptionSharePrecomputed( + dkg, + ciphertext, + aad, + keypair + ); + } else { + decryptionShare = aggregate.createDecryptionShareSimple( + dkg, + ciphertext, + aad, + keypair + ); + } + decryptionShares.push(decryptionShare); + }); + + // Now, the decryption share can be used to decrypt the ciphertext + // This part is in the client API + + let sharedSecret; + if (variant === 'precomputed') { + sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares); + } else { + sharedSecret = combineDecryptionSharesSimple( + decryptionShares, + dkg.publicParams() + ); + } + + // The client should have access to the public parameters of the DKG + const plaintext = decryptWithSharedSecret( + ciphertext, + aad, + sharedSecret, + dkg.publicParams() + ); + if (!bytesEqual(plaintext, msg)) { + throw new Error('Decryption failed'); + } + + return { + tau, + sharesNum, + threshold, + validator_keypairs, + validators, + transcripts, + dkg, + receivedMessages, + msg, + aad, + ciphertext, + serverAggregate, + }; +}; + +export const fakeDkgRitual = (ritualId: number): DkgRitual => { + const ritual = fakeDkgRitualE2e('precomputed'); + return { + id: ritualId, + initiator: ritual.validators[0].address.toString(), + dkgSize: ritual.sharesNum, + initTimestamp: 0, + totalTranscripts: ritual.receivedMessages.length, + totalAggregations: ritual.sharesNum, // Assuming the ritual is finished + aggregatedTranscriptHash: keccak256(ritual.serverAggregate.toBytes()), + aggregationMismatch: false, // Assuming the ritual is correct + aggregatedTranscript: toHexString(ritual.serverAggregate.toBytes()), + publicKey: toHexString(ritual.dkg.finalKey().toBytes()), + publicKeyHash: keccak256(ritual.dkg.finalKey().toBytes()), + }; +}; + +export const fakeDkgParticipants = (): DkgParticipant[] => { + const ritual = fakeDkgRitualE2e('precomputed'); + return zip( + zip(ritual.validators, ritual.transcripts), + ritual.validator_keypairs + ).map(([[v, t], k]) => { + return { + node: v.address.toString(), + aggregated: true, // Assuming all validators already contributed to the aggregate + transcript: toHexString(t.toBytes()), + publicKey: toHexString(k.publicKey.toBytes()), + }; + }); +}; diff --git a/yarn.lock b/yarn.lock index c598cf309..4df504fb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" "@babel/code-frame@7.12.11": @@ -17,46 +17,47 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" - integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5": + version "7.21.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" + integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.18.10", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" - integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.2" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-module-transforms" "^7.20.2" - "@babel/helpers" "^7.20.1" - "@babel/parser" "^7.20.2" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" + integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helpers" "^7.21.5" + "@babel/parser" "^7.21.8" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" + json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.20.1", "@babel/generator@^7.20.2", "@babel/generator@^7.7.2": - version "7.20.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" - integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== +"@babel/generator@^7.21.5", "@babel/generator@^7.7.2": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f" + integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w== dependencies: - "@babel/types" "^7.20.2" + "@babel/types" "^7.21.5" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" "@babel/helper-annotate-as-pure@^7.18.6": @@ -67,43 +68,46 @@ "@babel/types" "^7.18.6" "@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz#817f73b6c59726ab39f6ba18c234268a519e5abb" + integrity sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g== dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" + "@babel/types" "^7.21.5" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" - integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366" + integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w== dependencies: - "@babel/compat-data" "^7.20.0" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" + lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz#3c08a5b5417c7f07b5cf3dfb6dc79cbec682e8c2" - integrity sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz#205b26330258625ef8869672ebca1e0dee5a0f02" + integrity sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.5" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-replace-supers" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" + semver "^6.3.0" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" - integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz#a7886f61c2e29e21fd4aaeaf1e473deba6b571dc" + integrity sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.1.0" + regexpu-core "^5.3.1" + semver "^6.3.0" "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -117,25 +121,18 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" + integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -144,33 +141,33 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" - integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== +"@babel/helper-member-expression-to-functions@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz#3b1a009af932e586af77c1030fba9ee0bde396c0" + integrity sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.21.5" -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.21.4" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" - integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420" + integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-simple-access" "^7.21.5" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -179,12 +176,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" + integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== -"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": +"@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== @@ -194,25 +191,26 @@ "@babel/helper-wrap-function" "^7.18.9" "@babel/types" "^7.18.9" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" - integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz#a6ad005ba1c7d9bc2973dfde05a1bba7065dde3c" + integrity sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-member-expression-to-functions" "^7.21.5" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" -"@babel/helper-simple-access@^7.19.4", "@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== +"@babel/helper-simple-access@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" + integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== dependencies: - "@babel/types" "^7.20.2" + "@babel/types" "^7.21.5" -"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== @@ -226,39 +224,39 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" + integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== "@babel/helper-wrap-function@^7.18.9": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz#89f18335cff1152373222f76a4b37799636ae8b1" - integrity sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== dependencies: "@babel/helper-function-name" "^7.19.0" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" - "@babel/types" "^7.19.0" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" -"@babel/helpers@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" - integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== +"@babel/helpers@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.5.tgz#5bac66e084d7a4d2d9696bdf0175a93f7fb63c08" + integrity sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA== dependencies: - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -269,10 +267,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" - integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" + integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -281,22 +279,22 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" - integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" + integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" -"@babel/plugin-proposal-async-generator-functions@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz#352f02baa5d69f4e7529bdac39aaa02d41146af9" - integrity sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g== +"@babel/plugin-proposal-async-generator-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== dependencies: "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -308,13 +306,13 @@ "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-class-static-block@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" - integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== +"@babel/plugin-proposal-class-static-block@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" + integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-dynamic-import@^7.18.6": @@ -341,12 +339,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" - integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== +"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": @@ -365,16 +363,16 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz#a556f59d555f06961df1e572bb5eca864c84022d" - integrity sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ== +"@babel/plugin-proposal-object-rest-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: - "@babel/compat-data" "^7.20.1" - "@babel/helper-compilation-targets" "^7.20.0" + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.1" + "@babel/plugin-transform-parameters" "^7.20.7" "@babel/plugin-proposal-optional-catch-binding@^7.18.6": version "7.18.6" @@ -384,13 +382,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" - integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== +"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.18.6": @@ -401,14 +399,14 @@ "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" - integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== +"@babel/plugin-proposal-private-property-in-object@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" + integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": @@ -468,7 +466,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -539,27 +537,27 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" - integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" - integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== +"@babel/plugin-transform-arrow-functions@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" + integrity sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.21.5" -"@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" - integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== +"@babel/plugin-transform-async-to-generator@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-remap-async-to-generator" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-transform-block-scoped-functions@^7.18.6": version "7.18.6" @@ -568,39 +566,40 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-block-scoping@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz#f59b1767e6385c663fd0bce655db6ca9c8b236ed" - integrity sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ== +"@babel/plugin-transform-block-scoping@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-classes@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz#c0033cf1916ccf78202d04be4281d161f6709bb2" - integrity sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g== +"@babel/plugin-transform-classes@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" - integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== +"@babel/plugin-transform-computed-properties@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz#3a2d8bb771cd2ef1cd736435f6552fe502e11b44" + integrity sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/template" "^7.20.7" -"@babel/plugin-transform-destructuring@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz#c23741cfa44ddd35f5e53896e88c75331b8b2792" - integrity sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw== +"@babel/plugin-transform-destructuring@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -627,12 +626,12 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-for-of@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== +"@babel/plugin-transform-for-of@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" + integrity sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/plugin-transform-function-name@^7.18.9": version "7.18.9" @@ -657,31 +656,31 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-amd@^7.19.6": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz#aca391801ae55d19c4d8d2ebfeaa33df5f2a2cbd" - integrity sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg== +"@babel/plugin-transform-modules-amd@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== dependencies: - "@babel/helper-module-transforms" "^7.19.6" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-modules-commonjs@^7.19.6": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz#25b32feef24df8038fc1ec56038917eacb0b730c" - integrity sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ== +"@babel/plugin-transform-modules-commonjs@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" + integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== dependencies: - "@babel/helper-module-transforms" "^7.19.6" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-simple-access" "^7.19.4" + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-simple-access" "^7.21.5" -"@babel/plugin-transform-modules-systemjs@^7.19.6": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz#59e2a84064b5736a4471b1aa7b13d4431d327e0d" - integrity sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ== +"@babel/plugin-transform-modules-systemjs@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" + integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== dependencies: "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.19.6" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-validator-identifier" "^7.19.1" "@babel/plugin-transform-modules-umd@^7.18.6": @@ -692,13 +691,13 @@ "@babel/helper-module-transforms" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz#ec7455bab6cd8fb05c525a94876f435a48128888" - integrity sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.19.0" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-new-target@^7.18.6": version "7.18.6" @@ -715,10 +714,10 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.20.1": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz#7b3468d70c3c5b62e46be0a47b6045d8590fb748" - integrity sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA== +"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -729,13 +728,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-regenerator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" - integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== +"@babel/plugin-transform-regenerator@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz#576c62f9923f94bcb1c855adc53561fd7913724e" + integrity sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - regenerator-transform "^0.15.0" + "@babel/helper-plugin-utils" "^7.21.5" + regenerator-transform "^0.15.1" "@babel/plugin-transform-reserved-words@^7.18.6": version "7.18.6" @@ -751,13 +750,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-spread@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" - integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== +"@babel/plugin-transform-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-transform-sticky-regex@^7.18.6": version "7.18.6" @@ -780,12 +779,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== +"@babel/plugin-transform-unicode-escapes@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" + integrity sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" @@ -796,30 +795,30 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-env@^7.15.6": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" - integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== - dependencies: - "@babel/compat-data" "^7.20.1" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-option" "^7.18.6" + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb" + integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg== + dependencies: + "@babel/compat-data" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.20.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" + "@babel/plugin-proposal-async-generator-functions" "^7.20.7" "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.21.0" "@babel/plugin-proposal-dynamic-import" "^7.18.6" "@babel/plugin-proposal-export-namespace-from" "^7.18.9" "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.20.2" + "@babel/plugin-proposal-object-rest-spread" "^7.20.7" "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" "@babel/plugin-proposal-private-methods" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.21.0" "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" @@ -827,6 +826,7 @@ "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -836,40 +836,40 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.18.6" - "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.21.5" + "@babel/plugin-transform-async-to-generator" "^7.20.7" "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.20.2" - "@babel/plugin-transform-classes" "^7.20.2" - "@babel/plugin-transform-computed-properties" "^7.18.9" - "@babel/plugin-transform-destructuring" "^7.20.2" + "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-classes" "^7.21.0" + "@babel/plugin-transform-computed-properties" "^7.21.5" + "@babel/plugin-transform-destructuring" "^7.21.3" "@babel/plugin-transform-dotall-regex" "^7.18.6" "@babel/plugin-transform-duplicate-keys" "^7.18.9" "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-for-of" "^7.21.5" "@babel/plugin-transform-function-name" "^7.18.9" "@babel/plugin-transform-literals" "^7.18.9" "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.19.6" - "@babel/plugin-transform-modules-commonjs" "^7.19.6" - "@babel/plugin-transform-modules-systemjs" "^7.19.6" + "@babel/plugin-transform-modules-amd" "^7.20.11" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" + "@babel/plugin-transform-modules-systemjs" "^7.20.11" "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" "@babel/plugin-transform-new-target" "^7.18.6" "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.20.1" + "@babel/plugin-transform-parameters" "^7.21.3" "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.21.5" "@babel/plugin-transform-reserved-words" "^7.18.6" "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.19.0" + "@babel/plugin-transform-spread" "^7.20.7" "@babel/plugin-transform-sticky-regex" "^7.18.6" "@babel/plugin-transform-template-literals" "^7.18.9" "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-escapes" "^7.21.5" "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.20.2" + "@babel/types" "^7.21.5" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -887,44 +887,49 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@^7.8.4": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" - integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== - dependencies: - regenerator-runtime "^0.13.10" +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/template@^7.18.10", "@babel/template@^7.3.3": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== +"@babel/runtime@^7.8.4": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" + integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" + regenerator-runtime "^0.13.11" -"@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" - integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.1" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/traverse@^7.20.5", "@babel/traverse@^7.21.5", "@babel/traverse@^7.7.2": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" + integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/parser" "^7.21.5" + "@babel/types" "^7.21.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" - integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" + integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== dependencies: - "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-string-parser" "^7.21.5" "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" @@ -933,53 +938,55 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@commitlint/config-validator@^17.1.0": - version "17.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.1.0.tgz#51d09ca53d7a0d19736abf34eb18a66efce0f97a" - integrity sha512-Q1rRRSU09ngrTgeTXHq6ePJs2KrI+axPTgkNYDWSJIuS1Op4w3J30vUfSXjwn5YEJHklK3fSqWNHmBhmTR7Vdg== +"@commitlint/config-validator@^17.4.4": + version "17.4.4" + resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.4.4.tgz#d0742705719559a101d2ee49c0c514044af6d64d" + integrity sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg== dependencies: - "@commitlint/types" "^17.0.0" + "@commitlint/types" "^17.4.4" ajv "^8.11.0" -"@commitlint/execute-rule@^17.0.0": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-17.0.0.tgz#186e9261fd36733922ae617497888c4bdb6e5c92" - integrity sha512-nVjL/w/zuqjCqSJm8UfpNaw66V9WzuJtQvEnCrK4jDw6qKTmZB+1JQ8m6BQVZbNBcwfYdDNKnhIhqI0Rk7lgpQ== +"@commitlint/execute-rule@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz#4518e77958893d0a5835babe65bf87e2638f6939" + integrity sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA== "@commitlint/load@>6.1.1": - version "17.2.0" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.2.0.tgz#11c9fb23a967ff1507a28931c91aae7f978ea4ae" - integrity sha512-HDD57qSqNrk399R4TIjw31AWBG8dBjNj1MrDKZKmC/wvimtnIFlqzcu1+sxfXIOHj/+M6tcMWDtvknGUd7SU+g== - dependencies: - "@commitlint/config-validator" "^17.1.0" - "@commitlint/execute-rule" "^17.0.0" - "@commitlint/resolve-extends" "^17.1.0" - "@commitlint/types" "^17.0.0" - "@types/node" "^14.0.0" + version "17.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.5.0.tgz#be45dbbb50aaf5eb7e8e940e1e0d6171d1426bab" + integrity sha512-l+4W8Sx4CD5rYFsrhHH8HP01/8jEP7kKf33Xlx2Uk2out/UKoKPYMOIRcDH5ppT8UXLMV+x6Wm5osdRKKgaD1Q== + dependencies: + "@commitlint/config-validator" "^17.4.4" + "@commitlint/execute-rule" "^17.4.0" + "@commitlint/resolve-extends" "^17.4.4" + "@commitlint/types" "^17.4.4" + "@types/node" "*" chalk "^4.1.0" - cosmiconfig "^7.0.0" + cosmiconfig "^8.0.0" cosmiconfig-typescript-loader "^4.0.0" - lodash "^4.17.19" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + lodash.uniq "^4.5.0" resolve-from "^5.0.0" ts-node "^10.8.1" - typescript "^4.6.4" + typescript "^4.6.4 || ^5.0.0" -"@commitlint/resolve-extends@^17.1.0": - version "17.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.1.0.tgz#7cf04fa13096c8a6544a4af13321fdf8d0d50694" - integrity sha512-jqKm00LJ59T0O8O4bH4oMa4XyJVEOK4GzH8Qye9XKji+Q1FxhZznxMV/bDLyYkzbTodBt9sL0WLql8wMtRTbqQ== +"@commitlint/resolve-extends@^17.4.4": + version "17.4.4" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.4.4.tgz#8f931467dea8c43b9fe38373e303f7c220de6fdc" + integrity sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A== dependencies: - "@commitlint/config-validator" "^17.1.0" - "@commitlint/types" "^17.0.0" + "@commitlint/config-validator" "^17.4.4" + "@commitlint/types" "^17.4.4" import-fresh "^3.0.0" - lodash "^4.17.19" + lodash.mergewith "^4.6.2" resolve-from "^5.0.0" resolve-global "^1.0.0" -"@commitlint/types@^17.0.0": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-17.0.0.tgz#3b4604c1a0f06c340ce976e6c6903d4f56e3e690" - integrity sha512-hBAw6U+SkAT5h47zDMeOu3HSiD0SODw4Aq7rRNh1ceUmL7GyLKYhPbUvlRWqZ65XjBLPHZhFyQlRaPNz8qvUyQ== +"@commitlint/types@^17.4.4": + version "17.4.4" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-17.4.4.tgz#1416df936e9aad0d6a7bbc979ecc31e55dade662" + integrity sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ== dependencies: chalk "^4.1.0" @@ -1574,38 +1581,40 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -1614,14 +1623,22 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== dependencies: "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@morgan-stanley/ts-mocking-bird@^0.6.2": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@morgan-stanley/ts-mocking-bird/-/ts-mocking-bird-0.6.4.tgz#2e4b60d42957bab3b50b67dbf14c3da2f62a39f7" + integrity sha512-57VJIflP8eR2xXa9cD1LUawh+Gh+BVQfVu0n6GALyg/AqV/Nz25kDRvws3i9kIe1PTrbsZZOYpsYp6bXPd6nVA== + dependencies: + lodash "^4.17.16" + uuid "^7.0.3" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1655,10 +1672,10 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== "@sideway/pinpoint@^2.0.0": version "2.0.0" @@ -1666,9 +1683,9 @@ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinonjs/commons@^1.7.0": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.5.tgz#e280c94c95f206dcfd5aca00a43f2156b758c764" - integrity sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA== + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== dependencies: type-detect "4.0.8" @@ -1731,12 +1748,12 @@ ts-essentials "^7.0.1" "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.20" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.20.tgz#e168cdd612c92a2d335029ed62ac94c95b362359" - integrity sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ== + version "7.20.0" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" + integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" "@types/babel__generator" "*" "@types/babel__template" "*" "@types/babel__traverse" "*" @@ -1757,16 +1774,16 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.2.tgz#235bf339d17185bdec25e024ca19cce257cc7309" - integrity sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg== + version "7.18.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.5.tgz#c107216842905afafd3b6e774f6f935da6f5db80" + integrity sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q== dependencies: "@babel/types" "^7.3.0" "@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== dependencies: "@types/node" "*" @@ -1813,29 +1830,19 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "18.11.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" - integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== - -"@types/node@^14.0.0": - version "14.18.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.33.tgz#8c29a0036771569662e4635790ffa9e057db379b" - integrity sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg== + version "18.16.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" + integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== -"@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== - "@types/prettier@^2.1.1", "@types/prettier@^2.1.5": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" - integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow== + version "2.7.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== "@types/qs@^6.9.7": version "6.9.7" @@ -1853,16 +1860,16 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^15.0.0": - version "15.0.14" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" - integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + version "15.0.15" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.15.tgz#e609a2b1ef9e05d90489c2f5f45bbfb2be092158" + integrity sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg== dependencies: "@types/yargs-parser" "*" "@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + version "16.0.5" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" + integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== dependencies: "@types/yargs-parser" "*" @@ -1978,9 +1985,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: - version "8.8.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" - integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== add-stream@^1.0.0: version "1.0.0" @@ -2010,9 +2017,9 @@ ajv@^6.10.0, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1, ajv@^8.11.0: - version "8.11.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" - integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2056,9 +2063,9 @@ ansi-styles@^5.0.0: integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== anymatch@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -2075,6 +2082,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + array-back@^3.0.1, array-back@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" @@ -2085,12 +2097,20 @@ array-back@^4.0.1, array-back@^4.0.2: resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== -array-includes@^3.1.4: +array-includes@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== @@ -2118,7 +2138,7 @@ array-uniq@^1.0.1: resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== -array.prototype.flat@^1.2.5: +array.prototype.flat@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== @@ -2128,6 +2148,16 @@ array.prototype.flat@^1.2.5: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -2155,6 +2185,11 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + axios@^0.21.1: version "0.21.4" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" @@ -2313,15 +2348,15 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.21.3, browserslist@^4.21.4: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== +browserslist@^4.21.3, browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" bs-logger@0.x: version "0.2.6" @@ -2387,10 +2422,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001400: - version "1.0.30001431" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz#e7c59bd1bc518fae03a4656be442ce6c4887a795" - integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ== +caniuse-lite@^1.0.30001449: + version "1.0.30001482" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001482.tgz#8b3fad73dc35b2674a5c96df2d4f9f1c561435de" + integrity sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ== chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" @@ -2420,9 +2455,9 @@ chardet@^0.7.0: integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== ci-info@^3.2.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.6.1.tgz#7594f1c95cb7fdfddee7af95a13af7dbc67afdcf" - integrity sha512-up5ggbaDqOqJ4UqLKZ2naVkyqSJQgJi5lwD6b6mM748ysrghDBX0bx/qJTUHzw7zu6Mq4gycviSF5hJnwceD8w== + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -2437,9 +2472,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-spinners@^2.5.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + version "2.9.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" + integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== cli-width@^3.0.0: version "3.0.0" @@ -2527,9 +2562,9 @@ commander@^2.18.0: integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commitizen@^4.0.3: - version "4.2.5" - resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.2.5.tgz#48e5a5c28334c6e8ed845cc24fc9f072efd3961e" - integrity sha512-9sXju8Qrz1B4Tw7kC5KhnvwYQN88qs2zbiB8oyMsnXZyJ24PPGiNM3nHr73d32dnE3i8VJEXddBFIbOgYSEXtQ== + version "4.3.0" + resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.3.0.tgz#0d056c542a2d2b1f9b9aba981aa32575b2849924" + integrity sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw== dependencies: cachedir "2.3.0" cz-conventional-changelog "3.3.0" @@ -2539,10 +2574,10 @@ commitizen@^4.0.3: find-root "1.1.0" fs-extra "9.1.0" glob "7.2.3" - inquirer "8.2.4" + inquirer "8.2.5" is-utf8 "^0.2.1" lodash "4.17.21" - minimist "1.2.6" + minimist "1.2.7" strip-bom "4.0.0" strip-json-comments "3.1.1" @@ -2748,11 +2783,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== core-js-compat@^3.25.1: - version "3.26.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.1.tgz#0e710b09ebf689d719545ac36e49041850f943df" - integrity sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A== + version "3.30.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.1.tgz#961541e22db9c27fc48bfc13a3cafa8734171dfe" + integrity sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw== dependencies: - browserslist "^4.21.4" + browserslist "^4.21.5" core-util-is@~1.0.0: version "1.0.3" @@ -2760,20 +2795,19 @@ core-util-is@~1.0.0: integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cosmiconfig-typescript-loader@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.2.0.tgz#a3cfd0dd9dac86be7dbe5f53eb46ad03abdf417b" - integrity sha512-NkANeMnaHrlaSSlpKGyvn2R4rqUDeE/9E5YHx+b4nwo0R8dZyAqcih8/gxpCZvqWP9Vf6xuLpMSzSgdVEIM78g== + version "4.3.0" + resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz#c4259ce474c9df0f32274ed162c0447c951ef073" + integrity sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q== -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== +cosmiconfig@^8.0.0: + version "8.1.3" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.3.tgz#0e614a118fcc2d9e5afc2f87d53cd09931015689" + integrity sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw== dependencies: - "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" + js-yaml "^4.1.0" parse-json "^5.0.0" path-type "^4.0.0" - yaml "^1.10.0" create-require@^1.1.0: version "1.1.1" @@ -2857,13 +2891,6 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "2.1.2" -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -2885,9 +2912,9 @@ decamelize@^1.1.0, decamelize@^1.2.0: integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js@^10.2.1: - version "10.4.2" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" - integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA== + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== dedent@0.7.0, dedent@^0.7.0: version "0.7.0" @@ -2905,9 +2932,9 @@ deep-is@^0.1.3, deep-is@~0.1.3: integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== defaults@^1.0.3: version "1.0.4" @@ -2916,10 +2943,10 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== dependencies: has-property-descriptors "^1.0.0" object-keys "^1.1.1" @@ -3002,10 +3029,10 @@ dotgitignore@^2.1.0: find-up "^3.0.0" minimatch "^3.0.4" -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.4.284: + version "1.4.382" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.382.tgz#87e659b0f0d5f7b19759038871bac0a327191f82" + integrity sha512-czMavlW52VIPgutbVL9JnZIZuFijzsG1ww/1z2Otu1r1q+9Qe2bTsH3My3sZarlvwyqHM6+mnZfEnt2Vr4dsIg== elliptic@6.5.4: version "6.5.4" @@ -3050,34 +3077,53 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.20.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" - integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== dependencies: + array-buffer-byte-length "^1.0.0" + available-typed-arrays "^1.0.5" call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" has "^1.0.3" has-property-descriptors "^1.0.0" + has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" + is-typed-array "^1.1.10" is-weakref "^1.0.2" - object-inspect "^1.12.2" + object-inspect "^1.12.3" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" es-shim-unscopables@^1.0.0: version "1.0.0" @@ -3134,18 +3180,19 @@ eslint-config-prettier@^6.11.0: dependencies: get-stdin "^6.0.0" -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" - resolve "^1.20.0" + is-core-module "^2.11.0" + resolve "^1.22.1" -eslint-module-utils@^2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== +eslint-module-utils@^2.7.4: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" @@ -3158,22 +3205,24 @@ eslint-plugin-eslint-comments@^3.2.0: ignore "^5.0.5" eslint-plugin-import@^2.22.0: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" has "^1.0.3" - is-core-module "^2.8.1" + is-core-module "^2.11.0" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" tsconfig-paths "^3.14.1" eslint-scope@^5.1.1: @@ -3269,9 +3318,9 @@ esprima@^4.0.0, esprima@^4.0.1: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -3406,9 +3455,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" @@ -3419,10 +3468,10 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -ferveo-wasm@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ferveo-wasm/-/ferveo-wasm-0.1.0.tgz#03fa787ed1978f457d4847282d864778a3799f21" - integrity sha512-KcPU0QVU/GkXoDeL8mG63Mm6kKyCECmMyF4QGnPqcCSeEXVY+sno67Vk98I/1b372QWsjye/6Rm5J5HBI8icCQ== +ferveo-wasm@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ferveo-wasm/-/ferveo-wasm-0.1.1.tgz#40419ed3eeab795abb929d5fb5806e300e363847" + integrity sha512-xaYOIrX6VT1rdjbMvykLH9exU9mmlxa8z26LHnnD2HYMoH14/fpC3R7QBFWsOXi2xlHbQd2x28iD7/p9bdBo4A== figures@^3.0.0, figures@^3.1.0: version "3.2.0" @@ -3556,6 +3605,13 @@ follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -3623,7 +3679,7 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== -functions-have-names@^1.2.2: +functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -3638,10 +3694,10 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -3752,9 +3808,9 @@ glob@7.2.3, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glo path-is-absolute "^1.0.0" glob@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" - integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ== + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -3795,12 +3851,19 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: type-fest "^0.20.2" +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globby@^11.0.3: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -3824,10 +3887,17 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== handlebars@^4.7.7: version "4.7.7" @@ -3868,6 +3938,11 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -3980,9 +4055,9 @@ ignore@^4.0.6: integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.0.5, ignore@^5.1.8, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" @@ -4028,10 +4103,10 @@ ini@^1.3.2, ini@^1.3.4: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inquirer@8.2.4: - version "8.2.4" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" - integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== +inquirer@8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" + integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -4049,15 +4124,24 @@ inquirer@8.2.4: through "^2.3.6" wrap-ansi "^7.0.0" -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: - get-intrinsic "^1.1.0" + get-intrinsic "^1.2.0" has "^1.0.3" side-channel "^1.0.4" +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -4078,15 +4162,15 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-callable@^1.1.4, is-callable@^1.2.7: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.11.0, is-core-module@^2.5.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== dependencies: has "^1.0.3" @@ -4202,6 +4286,17 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -4528,9 +4623,9 @@ jest-mock@^27.5.1: "@types/node" "*" jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@^27.5.1: version "27.5.1" @@ -4709,14 +4804,14 @@ jest@^27.0.6: jest-cli "^27.5.1" joi@^17.7.0: - version "17.7.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" - integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== + version "17.9.2" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.2.tgz#8b2e4724188369f55451aebd1d0b1d9482470690" + integrity sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" "@sideway/address" "^4.1.3" - "@sideway/formula" "^3.0.0" + "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" js-sha3@0.8.0, js-sha3@^0.8.0: @@ -4737,6 +4832,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsdom@^16.6.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" @@ -4810,12 +4912,12 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@2.x, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@2.x, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -json5@^1.0.1: +json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== @@ -4944,6 +5046,11 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g== +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + lodash.map@^4.5.1: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" @@ -4959,12 +5066,22 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4982,6 +5099,13 @@ longest@^2.0.1: resolved "https://registry.yarnpkg.com/longest/-/longest-2.0.1.tgz#781e183296aa94f6d4d916dc335d0d17aefa23f8" integrity sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -5024,9 +5148,9 @@ map-obj@^4.0.0: integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== marked@^4.0.16: - version "4.2.2" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.2.tgz#1d2075ad6cdfe42e651ac221c32d949a26c0672a" - integrity sha512-JjBTFTAvuTgANXx82a5vzK9JLSMoV6V3LBVn4Uhdso6t7vXrGx7g1Cd2r6NYSsxrYbQGFCMqBDhFHyK5q2UvcQ== + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== memorystream@^0.3.1: version "0.3.1" @@ -5130,9 +5254,9 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: brace-expansion "^1.1.7" minimatch@^5.0.1, minimatch@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" - integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" @@ -5145,16 +5269,16 @@ minimist-options@4.1.0, minimist-options@^4.0.2: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -5165,11 +5289,6 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -5205,10 +5324,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" @@ -5258,19 +5377,19 @@ npm-run-path@^4.0.1: path-key "^3.0.0" nwsapi@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + version "2.2.4" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" + integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== +object-inspect@^1.12.3, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== object-keys@^1.1.1: version "1.1.1" @@ -5287,7 +5406,7 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.values@^1.1.5: +object.values@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== @@ -5574,9 +5693,9 @@ prelude-ls@~1.1.2: integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== prettier@^2.1.1, prettier@^2.1.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" @@ -5621,9 +5740,9 @@ psl@^1.1.33: integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== q@^1.5.1: version "1.5.1" @@ -5631,9 +5750,9 @@ q@^1.5.1: integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== qs@^6.10.1: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + version "6.11.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" + integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== dependencies: side-channel "^1.0.4" @@ -5694,18 +5813,18 @@ read-pkg@^5.2.0: type-fest "^0.6.0" readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -5745,48 +5864,43 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.10: - version "0.13.10" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" - integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== dependencies: "@babel/runtime" "^7.8.4" regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" regexpp@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.1.tgz#a69c26f324c1e962e9ffd0b88b055caba8089139" - integrity sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ== +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== dependencies: + "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" regenerate-unicode-properties "^10.1.0" - regjsgen "^0.7.1" regjsparser "^0.9.1" unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" - -regjsgen@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" - integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== + unicode-match-property-value-ecmascript "^2.1.0" regjsparser@^0.9.1: version "0.9.1" @@ -5843,16 +5957,16 @@ resolve-global@^1.0.0: global-dirs "^0.1.1" resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== -resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.11.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -5889,9 +6003,9 @@ run-parallel@^1.1.9: queue-microtask "^1.2.2" rxjs@^7.5.5: - version "7.5.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" - integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA== + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" @@ -5937,9 +6051,9 @@ scrypt-js@3.0.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.x, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== dependencies: lru-cache "^6.0.0" @@ -5973,9 +6087,9 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.6.1: - version "1.7.4" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" - integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== shiki@^0.10.1: version "0.10.1" @@ -6038,9 +6152,9 @@ source-map@^0.7.3: integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -6059,9 +6173,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.12" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" - integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== + version "3.0.13" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" + integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== split2@^3.0.0: version "3.2.2" @@ -6140,7 +6254,16 @@ string.prototype.padend@^3.0.0: define-properties "^1.1.4" es-abstract "^1.20.4" -string.prototype.trimend@^1.0.5: +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== @@ -6149,7 +6272,7 @@ string.prototype.trimend@^1.0.5: define-properties "^1.1.4" es-abstract "^1.20.4" -string.prototype.trimstart@^1.0.5: +string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== @@ -6330,9 +6453,9 @@ text-table@^0.2.0: integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== throat@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== + version "6.0.2" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" + integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== through2@^2.0.0: version "2.0.5" @@ -6416,10 +6539,11 @@ trim-repeated@^1.0.0: escape-string-regexp "^1.0.2" ts-command-line-args@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz#b6188e42efc6cf7a8898e438a873fbb15505ddd6" - integrity sha512-FR3y7pLl/fuUNSmnPhfLArGqRrpojQgIEEOVzYx9DhTmfIN7C9RWSfpkJEF4J+Gk7aVx5pak8I7vWZsaN4N84g== + version "2.5.0" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.0.tgz#7eeed3a6937b2612ea08a0794cf9d43fbbea89c4" + integrity sha512-Ff7Xt04WWCjj/cmPO9eWTJX3qpBZWuPWyQYG1vnxJao+alWWYjwJBc5aYz3h5p5dE08A6AnpkgiCtP/0KXXBYw== dependencies: + "@morgan-stanley/ts-mocking-bird" "^0.6.2" chalk "^4.1.0" command-line-args "^5.1.1" command-line-usage "^6.1.0" @@ -6464,20 +6588,20 @@ ts-node@^10.8.1: yn "3.1.1" ts-unused-exports@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/ts-unused-exports/-/ts-unused-exports-8.0.0.tgz#6dd15ff26286e0b7e5663cda3b98c77ea6f3ffe7" - integrity sha512-gylHFyJqC80PSb4zy35KTckykEW1vmKjnOHjBeX9iKBo4b/SzqQIcXXbYSuif4YMgNm6ewFF62VM1C9z0bGZPw== + version "8.0.5" + resolved "https://registry.yarnpkg.com/ts-unused-exports/-/ts-unused-exports-8.0.5.tgz#43b1ac25822ec9445b5fc3e57862c0d1f23471cb" + integrity sha512-ewEHxTtQsYQFQCI12CnMx8D0xxn1/Uui1Wr5jbiX4me+4LlSiOGvt4mGz0hxtIC3eElVaLrKEPbeT3TPrm4JvA== dependencies: chalk "^4.0.0" tsconfig-paths "^3.9.0" tsconfig-paths@^3.14.1, tsconfig-paths@^3.9.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" @@ -6487,9 +6611,9 @@ tslib@^1.8.1: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.1.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== tsutils@^3.21.0: version "3.21.0" @@ -6563,6 +6687,15 @@ typechain@^7.0.0: ts-command-line-args "^2.2.0" ts-essentials "^7.0.1" +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -6591,15 +6724,15 @@ typedoc@^0.22.11: minimatch "^5.1.0" shiki "^0.10.1" -typescript@^4.6.4: - version "4.8.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" - integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== +"typescript@^4.6.4 || ^5.0.0": + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== typescript@^4.7.0: - version "4.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db" - integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA== + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== typical@^4.0.0: version "4.0.0" @@ -6639,10 +6772,10 @@ unicode-match-property-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript "^2.0.0" unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== unicode-property-aliases-ecmascript@^2.0.0: version "2.1.0" @@ -6664,10 +6797,10 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -update-browserslist-db@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== +update-browserslist-db@^1.0.10: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -6697,6 +6830,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -6725,9 +6863,9 @@ validate-npm-package-license@^3.0.1: spdx-expression-parse "^3.0.0" vscode-oniguruma@^1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz#aeb9771a2f1dbfc9083c8a7fdd9cccaa3f386607" - integrity sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" + integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== vscode-textmate@5.2.0: version "5.2.0" @@ -6804,6 +6942,18 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^1.2.14, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -6890,16 +7040,16 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" From 58755d9edecae1b048ba6a748342db83e34d573a Mon Sep 17 00:00:00 2001 From: piotr-roslaniec <39299780+piotr-roslaniec@users.noreply.github.com> Date: Thu, 4 May 2023 12:15:04 +0100 Subject: [PATCH 07/98] Bump `nucypher-core` version (#193) feat!: bump nucypher-core to 0.7.0 --- README.md | 7 ++-- package.json | 4 +- src/characters/bob.ts | 4 +- src/characters/porter.ts | 10 +++-- src/characters/universal-bob.ts | 13 +++--- src/keyring.ts | 2 +- src/policies/policy.ts | 2 +- src/sdk/strategy.ts | 22 +++++----- test/acceptance/alice-grants.test.ts | 25 +++++++++--- test/acceptance/delay-enact.test.ts | 6 +-- test/docs/cbd.test.ts | 8 ++-- test/integration/conditions.test.ts | 2 +- test/integration/enrico.test.ts | 4 +- test/integration/pre.test.ts | 4 +- test/unit/cohort.test.ts | 10 ++--- test/unit/strategy.test.ts | 57 +++++++++++++------------- test/unit/testVariables.ts | 60 ++++++++++++++++++++++++---- test/utils.ts | 27 +++++++------ yarn.lock | 8 ++-- 19 files changed, 173 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 5cd089265..c85ae1b19 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ Full documentation can be found [here](https://docs.threshold.network/app-develo > **Warning** > > `nucypher-ts` is under [active development](https://github.com/nucypher/nucypher-ts/pulls): -> -> - SDK does not support policy revocation. -> - We expect breaking changes. +> +> - SDK does not support policy revocation. +> - We expect breaking changes. ## Installation @@ -26,6 +26,7 @@ To learn more, follow the tutorial at Threshold Network's [docs](https://docs.th See [`nucypher-ts/examples`](https://github.com/nucypher/nucypher-ts/tree/main/examples) to find out how to integrate `nucypher-ts` into your favorite web framework. We also provide two code samples of TAC applications: + - [nucypher/tdec-sandbox](https://github.com/nucypher/tdec-sandbox) - [nucypher/tdec-nft-example](https://github.com/nucypher/tdec-nft-example) diff --git a/package.json b/package.json index 7bcc725b3..23a48e6e2 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "test:lint": "eslint src test --ext .ts", "test:exports": "ts-unused-exports tsconfig.json --ignoreFiles src/index.ts", "test:prettier": "prettier \"src/**/*.ts\" \"test/**/*.ts\" --list-different", - "test:unit": "jest", + "test:unit": "jest --detectOpenHandles --forceExit --runInBand", "watch:build": "tsc -p tsconfig.json -w", "watch:test": "jest --watch", "cov": "run-s build test:unit && open-cli coverage/index.html", @@ -52,7 +52,7 @@ "prebuild": "yarn typechain" }, "dependencies": { - "@nucypher/nucypher-core": "0.4.0", + "@nucypher/nucypher-core": "^0.7.0", "axios": "^0.21.1", "ethers": "^5.4.1", "joi": "^17.7.0", diff --git a/src/characters/bob.ts b/src/characters/bob.ts index 5d4c67101..1ee8606af 100644 --- a/src/characters/bob.ts +++ b/src/characters/bob.ts @@ -27,11 +27,11 @@ export class RemoteBob { const dk = decryptingKey instanceof PublicKey ? decryptingKey - : PublicKey.fromBytes(decryptingKey); + : PublicKey.fromCompressedBytes(decryptingKey); const vk = verifyingKey instanceof PublicKey ? verifyingKey - : PublicKey.fromBytes(verifyingKey); + : PublicKey.fromCompressedBytes(verifyingKey); return new RemoteBob(dk, vk); } } diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 7834ab491..ee0df9a40 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -93,7 +93,9 @@ export class Porter { return resp.data.result.ursulas.map((u: UrsulaResponse) => ({ checksumAddress: u.checksum_address, uri: u.uri, - encryptingKey: PublicKey.fromBytes(fromHexString(u.encrypting_key)), + encryptingKey: PublicKey.fromCompressedBytes( + fromHexString(u.encrypting_key) + ), })); } @@ -111,9 +113,9 @@ export class Porter { const data: PostRetrieveCFragsRequest = { treasure_map: toBase64(treasureMap.toBytes()), retrieval_kits: retrievalKits.map((rk) => toBase64(rk.toBytes())), - alice_verifying_key: toHexString(aliceVerifyingKey.toBytes()), - bob_encrypting_key: toHexString(bobEncryptingKey.toBytes()), - bob_verifying_key: toHexString(bobVerifyingKey.toBytes()), + alice_verifying_key: toHexString(aliceVerifyingKey.toCompressedBytes()), + bob_encrypting_key: toHexString(bobEncryptingKey.toCompressedBytes()), + bob_verifying_key: toHexString(bobVerifyingKey.toCompressedBytes()), context, }; const resp: AxiosResponse = await axios.post( diff --git a/src/characters/universal-bob.ts b/src/characters/universal-bob.ts index be8b6afc6..3bdbb95d7 100644 --- a/src/characters/universal-bob.ts +++ b/src/characters/universal-bob.ts @@ -140,10 +140,11 @@ export class tDecDecrypter { public toObj(): decrypterJSON { return { porterUri: this.porter.porterUrl.toString(), - policyEncryptingKeyBytes: this.policyEncryptingKey.toBytes(), + policyEncryptingKeyBytes: this.policyEncryptingKey.toCompressedBytes(), encryptedTreasureMapBytes: this.encryptedTreasureMap.toBytes(), - publisherVerifyingKeyBytes: this.publisherVerifyingKey.toBytes(), - bobSecretKeyBytes: this.keyring.secretKey.toSecretBytes(), + publisherVerifyingKeyBytes: + this.publisherVerifyingKey.toCompressedBytes(), + bobSecretKeyBytes: this.keyring.secretKey.toBEBytes(), }; } @@ -160,10 +161,10 @@ export class tDecDecrypter { }: decrypterJSON) { return new tDecDecrypter( porterUri, - PublicKey.fromBytes(policyEncryptingKeyBytes), + PublicKey.fromCompressedBytes(policyEncryptingKeyBytes), EncryptedTreasureMap.fromBytes(encryptedTreasureMapBytes), - PublicKey.fromBytes(publisherVerifyingKeyBytes), - SecretKey.fromBytes(bobSecretKeyBytes) + PublicKey.fromCompressedBytes(publisherVerifyingKeyBytes), + SecretKey.fromBEBytes(bobSecretKeyBytes) ); } diff --git a/src/keyring.ts b/src/keyring.ts index 4e0f06b99..4b769b95e 100644 --- a/src/keyring.ts +++ b/src/keyring.ts @@ -55,7 +55,7 @@ export class Keyring { private getSecretKeyFromLabel(label: string): SecretKey { return SecretKeyFactory.fromSecureRandomness( - this.secretKey.toSecretBytes() + this.secretKey.toBEBytes() ).makeKey(toBytes(label)); } diff --git a/src/policies/policy.ts b/src/policies/policy.ts index 54d95719e..dbb23eccb 100644 --- a/src/policies/policy.ts +++ b/src/policies/policy.ts @@ -131,7 +131,7 @@ export class BlockchainPolicy { this.delegatingKey, encryptedTreasureMap, // revocationKit, - this.publisher.verifyingKey.toBytes(), + this.publisher.verifyingKey.toCompressedBytes(), this.shares, this.startDate, this.endDate diff --git a/src/sdk/strategy.ts b/src/sdk/strategy.ts index 15190f8b8..490e44d57 100644 --- a/src/sdk/strategy.ts +++ b/src/sdk/strategy.ts @@ -136,8 +136,8 @@ export class Strategy { }: StrategyJSON) { return new Strategy( Cohort.fromObj(cohort), - SecretKey.fromBytes(aliceSecretKeyBytes), - SecretKey.fromBytes(bobSecretKeyBytes), + SecretKey.fromBEBytes(aliceSecretKeyBytes), + SecretKey.fromBEBytes(bobSecretKeyBytes), startDate, endDate, conditionSet @@ -147,8 +147,8 @@ export class Strategy { public toObj(): StrategyJSON { return { cohort: this.cohort.toObj(), - aliceSecretKeyBytes: this.aliceSecretKey.toSecretBytes(), - bobSecretKeyBytes: this.bobSecretKey.toSecretBytes(), + aliceSecretKeyBytes: this.aliceSecretKey.toBEBytes(), + bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), conditionSet: this.conditionSet, startDate: this.startDate, endDate: this.endDate, @@ -183,23 +183,25 @@ export class DeployedStrategy { conditionSet, }: DeployedStrategyJSON) { const id = HRAC.fromBytes(policy.id); - const policyKey = PublicKey.fromBytes(policy.policyKey); + const policyKey = PublicKey.fromCompressedBytes(policy.policyKey); const encryptedTreasureMap = EncryptedTreasureMap.fromBytes( policy.encryptedTreasureMap ); - const aliceVerifyingKey = PublicKey.fromBytes(policy.aliceVerifyingKey); + const aliceVerifyingKey = PublicKey.fromCompressedBytes( + policy.aliceVerifyingKey + ); const newPolicy = { id, label: policy.label, policyKey, encryptedTreasureMap, - aliceVerifyingKey: aliceVerifyingKey.toBytes(), + aliceVerifyingKey: aliceVerifyingKey.toCompressedBytes(), size: policy.size, startTimestamp: policy.startTimestamp, endTimestamp: policy.endTimestamp, txHash: policy.txHash, }; - const bobSecretKey = SecretKey.fromBytes(bobSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); const label = newPolicy.label; const cohort = Cohort.fromObj(cohortConfig); const encrypter = new Enrico(newPolicy.policyKey, undefined, conditionSet); @@ -226,13 +228,13 @@ export class DeployedStrategy { const policy = { ...this.policy, id: this.policy.id.toBytes(), - policyKey: this.policy.policyKey.toBytes(), + policyKey: this.policy.policyKey.toCompressedBytes(), encryptedTreasureMap: this.policy.encryptedTreasureMap.toBytes(), }; return { policy, cohortConfig: this.cohort.toObj(), - bobSecretKeyBytes: this.bobSecretKey.toSecretBytes(), + bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), conditionSet: this.conditionSet, }; } diff --git a/test/acceptance/alice-grants.test.ts b/test/acceptance/alice-grants.test.ts index 3e1f661aa..f4414e2bb 100644 --- a/test/acceptance/alice-grants.test.ts +++ b/test/acceptance/alice-grants.test.ts @@ -12,6 +12,7 @@ import { toBytes } from '../../src/utils'; import { bytesEqual, fromBytes, + makeTestUrsulas, mockAlice, mockBob, mockEncryptTreasureMap, @@ -21,7 +22,6 @@ import { mockPublishToBlockchain, mockRemoteBob, mockRetrieveCFragsRequest, - mockUrsulas, reencryptKFrags, } from '../utils'; @@ -31,7 +31,7 @@ describe('story: alice shares message with bob through policy', () => { const shares = 3; const startDate = new Date(); const endDate = new Date(Date.now() + 60 * 1000); - const mockedUrsulas = mockUrsulas().slice(0, shares); + const mockedUrsulas = makeTestUrsulas().slice(0, shares); // Intermediate variables used for mocking let encryptedTreasureMap: EncryptedTreasureMap; @@ -64,7 +64,9 @@ describe('story: alice shares message with bob through policy', () => { }; policy = await alice.grant(policyParams); - expect(policy.aliceVerifyingKey).toEqual(alice.verifyingKey.toBytes()); + expect(policy.aliceVerifyingKey).toEqual( + alice.verifyingKey.toCompressedBytes() + ); expect(policy.label).toBe(label); expect(getUrsulasSpy).toHaveBeenCalled(); expect(generateKFragsSpy).toHaveBeenCalled(); @@ -121,12 +123,23 @@ describe('story: alice shares message with bob through policy', () => { bobVerifyingKey_, ] = retrieveCFragsSpy.mock.calls[0]; expect( - bytesEqual(aliceVerifyingKey_.toBytes(), aliceVerifyingKey.toBytes()) + bytesEqual( + aliceVerifyingKey_.toCompressedBytes(), + aliceVerifyingKey.toCompressedBytes() + ) + ); + expect( + bytesEqual( + bobEncryptingKey_.toCompressedBytes(), + bob.decryptingKey.toCompressedBytes() + ) ); expect( - bytesEqual(bobEncryptingKey_.toBytes(), bob.decryptingKey.toBytes()) + bytesEqual( + bobVerifyingKey_.toCompressedBytes(), + bob.verifyingKey.toCompressedBytes() + ) ); - expect(bytesEqual(bobVerifyingKey_.toBytes(), bob.verifyingKey.toBytes())); const { verifiedCFrags } = reencryptKFrags( verifiedKFrags, diff --git a/test/acceptance/delay-enact.test.ts b/test/acceptance/delay-enact.test.ts index 6ca4cc623..151719f6f 100644 --- a/test/acceptance/delay-enact.test.ts +++ b/test/acceptance/delay-enact.test.ts @@ -1,11 +1,11 @@ import { + makeTestUrsulas, mockAlice, mockEncryptTreasureMap, mockGenerateKFrags, mockGetUrsulas, mockPublishToBlockchain, mockRemoteBob, - mockUrsulas, } from '../utils'; describe('story: alice1 creates a policy but alice2 enacts it', () => { @@ -13,7 +13,7 @@ describe('story: alice1 creates a policy but alice2 enacts it', () => { const shares = 3; const startDate = new Date(); const endDate = new Date(Date.now() + 60 * 1000); // 60s later - const mockedUrsulas = mockUrsulas().slice(0, shares); + const mockedUrsulas = makeTestUrsulas().slice(0, shares); const label = 'fake-data-label'; it('alice generates a new policy', async () => { @@ -38,7 +38,7 @@ describe('story: alice1 creates a policy but alice2 enacts it', () => { policyParams ); expect(preEnactedPolicy.aliceVerifyingKey).toEqual( - alice1.verifyingKey.toBytes() + alice1.verifyingKey.toCompressedBytes() ); expect(preEnactedPolicy.label).toBe(label); diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 650ad1f2f..956448355 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -11,6 +11,7 @@ import { import { Ursula } from '../../src/characters/porter'; import { toBytes } from '../../src/utils'; import { + makeTestUrsulas, mockDetectEthereumProvider, mockEncryptTreasureMap, mockGenerateKFrags, @@ -18,7 +19,6 @@ import { mockMakeTreasureMap, mockPublishToBlockchain, mockRetrieveCFragsRequest, - mockUrsulas, mockWeb3Provider, } from '../utils'; @@ -42,7 +42,7 @@ describe('Get Started (CBD PoC)', () => { it('can run the get started example', async () => { const detectEthereumProvider = mockDetectEthereumProvider(); - const mockedUrsulas = mockUrsulas(); + const mockedUrsulas = makeTestUrsulas(); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -52,7 +52,7 @@ describe('Get Started (CBD PoC)', () => { jest .spyOn(providers, 'Web3Provider') .mockImplementation(() => - mockWeb3Provider(SecretKey.random().toSecretBytes()) + mockWeb3Provider(SecretKey.random().toBEBytes()) ); // @@ -126,7 +126,7 @@ describe('Get Started (CBD PoC)', () => { // End of the code example // - const expectedAddresses = mockUrsulas().map((u) => u.checksumAddress); + const expectedAddresses = makeTestUrsulas().map((u) => u.checksumAddress); const condObj = conditions.conditions[0].toObj(); expect(newCohort.ursulaAddresses).toEqual(expectedAddresses); expect(condObj.parameters).toEqual([5954]); diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 5fe41aa84..373a2d54e 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -239,7 +239,7 @@ describe('produce context parameters from conditions', () => { describe('condition context', () => { it('should serialize to JSON with context params', async () => { - const web3Provider = mockWeb3Provider(SecretKey.random().toSecretBytes()); + const web3Provider = mockWeb3Provider(SecretKey.random().toBEBytes()); const rpcCondition = new Conditions.RpcCondition({ chain: 5, diff --git a/test/integration/enrico.test.ts b/test/integration/enrico.test.ts index f3281b0ad..fb57894c4 100644 --- a/test/integration/enrico.test.ts +++ b/test/integration/enrico.test.ts @@ -51,7 +51,9 @@ describe('enrico', () => { threshold, shares ); - expect(delegatingKey.toBytes()).toEqual(policyEncryptingKey.toBytes()); + expect(delegatingKey.toCompressedBytes()).toEqual( + policyEncryptingKey.toCompressedBytes() + ); // Bob can decrypt re-encrypted ciphertext const { verifiedCFrags } = reencryptKFrags( diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index c896c7b6c..b5bf3c311 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -10,13 +10,13 @@ import { } from '../../src'; import { RetrievalResult } from '../../src/kits/retrieval'; import { toBytes, zip } from '../../src/utils'; -import { mockAlice, mockBob, mockUrsulas, reencryptKFrags } from '../utils'; +import { makeTestUrsulas, mockAlice, mockBob, reencryptKFrags } from '../utils'; describe('proxy reencryption', () => { const plaintext = toBytes('plaintext-message'); const threshold = 2; const shares = 3; - const ursulas = mockUrsulas().slice(0, shares); + const ursulas = makeTestUrsulas().slice(0, shares); const label = 'fake-data-label'; const alice = mockAlice(); const bob = mockBob(); diff --git a/test/unit/cohort.test.ts b/test/unit/cohort.test.ts index 54ed84457..7cfd66eea 100644 --- a/test/unit/cohort.test.ts +++ b/test/unit/cohort.test.ts @@ -1,9 +1,9 @@ -import { Cohort } from '../../src/sdk/cohort'; -import { mockGetUrsulas, mockUrsulas } from '../utils'; +import { Cohort } from '../../src'; +import { makeTestUrsulas, mockGetUrsulas } from '../utils'; describe('Cohort', () => { it('can create Cohort from configuration', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const config = { @@ -23,7 +23,7 @@ describe('Cohort', () => { }); it('can export to JSON', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const config = { threshold: 2, @@ -39,7 +39,7 @@ describe('Cohort', () => { }); it('can import from JSON', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const configJSON = diff --git a/test/unit/strategy.test.ts b/test/unit/strategy.test.ts index be450cc69..38ada25b3 100644 --- a/test/unit/strategy.test.ts +++ b/test/unit/strategy.test.ts @@ -15,13 +15,13 @@ import { import { Ursula } from '../../src/characters/porter'; import { fromBase64, toBytes } from '../../src/utils'; import { + makeTestUrsulas, mockEncryptTreasureMap, mockGenerateKFrags, mockGetUrsulas, mockMakeTreasureMap, mockPublishToBlockchain, mockRetrieveCFragsRequest, - mockUrsulas, mockWeb3Provider, } from '../utils'; @@ -40,17 +40,17 @@ describe('Strategy', () => { shares: 3, porterUri: 'https://_this.should.crash', }; - const aliceSecretKey = SecretKey.fromBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBytes(bobSecretKeyBytes); - const aliceProvider = mockWeb3Provider(aliceSecretKey.toSecretBytes()); + const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); + const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); Date.now = jest.fn(() => 1487076708000); afterEach(() => { - jest.clearAllMocks(); + jest.restoreAllMocks(); }); it('can create Strategy from configuration', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const testCohort = await Cohort.create(cohortConfig); @@ -61,26 +61,29 @@ describe('Strategy', () => { bobSecretKey ); - const expectedUrsulas = [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ]; + const expectedUrsulas = mockedUrsulas.map( + (ursula) => ursula.checksumAddress + ); expect(getUrsulasSpy).toHaveBeenCalled(); expect(testStrategy.cohort.ursulaAddresses).toEqual(expectedUrsulas); }); it('can export to JSON', async () => { + const mockedUrsulas = makeTestUrsulas().slice(0, 3); + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const testCohort = await Cohort.create(cohortConfig); const testStrategy = Strategy.create( testCohort, undefined, aliceSecretKey, - bobSecretKey + bobSecretKey, + new Date('2017-02-14T12:51:48.000Z'), + new Date('2123-03-16T12:51:48.000Z') ); const configJSON = testStrategy.toJSON(); expect(configJSON).toEqual(strategyJSON); + expect(getUrsulasSpy).toHaveBeenCalled(); }); it('can import from JSON', async () => { @@ -94,7 +97,7 @@ describe('Strategy', () => { }); it('can deploy and return DeployedStrategy', async () => { - const mockedUrsulas = mockUrsulas().slice(0, 3); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -120,16 +123,16 @@ describe('Deployed Strategy', () => { shares: 3, porterUri: 'https://_this.should.crash', }; - const aliceSecretKey = SecretKey.fromBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBytes(bobSecretKeyBytes); + const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); afterEach(() => { - jest.clearAllMocks(); + jest.restoreAllMocks(); }); it('can export to JSON', async () => { - const aliceProvider = mockWeb3Provider(aliceSecretKey.toSecretBytes()); - const mockedUrsulas = mockUrsulas().slice(0, 3); + const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -143,7 +146,9 @@ describe('Deployed Strategy', () => { testCohort, undefined, aliceSecretKey, - bobSecretKey + bobSecretKey, + new Date('2017-02-14T12:51:48.000Z'), + new Date('2123-03-16T12:51:48.000Z') ); const testDeployed = await testStrategy.deploy('test', aliceProvider); expect(getUrsulasSpy).toHaveBeenCalled(); @@ -163,9 +168,9 @@ describe('Deployed Strategy', () => { }); it('can encrypt and decrypt', async () => { - const aliceProvider = mockWeb3Provider(aliceSecretKey.toSecretBytes()); - const bobProvider = mockWeb3Provider(bobSecretKey.toSecretBytes()); - const mockedUrsulas = mockUrsulas().slice(0, 3); + const aliceProvider = mockWeb3Provider(aliceSecretKey.toBEBytes()); + const bobProvider = mockWeb3Provider(bobSecretKey.toBEBytes()); + const mockedUrsulas = makeTestUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -195,11 +200,10 @@ describe('Deployed Strategy', () => { parameters: [3591], chain: 5, }); - const plaintext = 'this is a secret'; - const conditions = new ConditionSet([ownsNFT]); - encrypter.conditions = conditions; + encrypter.conditions = new ConditionSet([ownsNFT]); const encryptedMessageKit = encrypter.encryptMessage(plaintext); + // Setup mocks for `retrieveAndDecrypt` const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); const ursulaAddresses = ( @@ -227,8 +231,7 @@ describe('tDecDecrypter', () => { const importedStrategy = DeployedStrategy.fromJSON(deployedStrategyJSON); it('can export to JSON', () => { - const decrypter = importedStrategy.decrypter; - const configJSON = decrypter.toJSON(); + const configJSON = importedStrategy.decrypter.toJSON(); expect(configJSON).toEqual(decrypterJSON); }); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index 643c853c9..34c1cc099 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -1,12 +1,48 @@ -export const strategyJSON = - '{"cohort":{"ursulaAddresses":["0x5cf1703a1c99a4b42eb056535840e93118177232","0x7fff551249d223f723557a96a0e1a469c79cc934","0x9c7c824239d3159327024459ad69bb215859bd25"],"threshold":2,"shares":3,"porterUri":"https://_this.should.crash"},"aliceSecretKeyBytes":"base64:N1K+vcukPJQkVi57P5jXca5W9CwX48VEBVV0H9CYWDU=","bobSecretKeyBytes":"base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=","startDate":"2017-02-14T12:51:48.000Z","endDate":"2017-03-16T12:51:48.000Z"}'; +export const strategyJSON = JSON.stringify({ + cohort: { + ursulaAddresses: [ + '0x5cf1703a1c99a4b42eb056535840e93118177232', + '0x7fff551249d223f723557a96a0e1a469c79cc934', + '0x9c7c824239d3159327024459ad69bb215859bd25', + ], + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }, + aliceSecretKeyBytes: 'base64:N1K+vcukPJQkVi57P5jXca5W9CwX48VEBVV0H9CYWDU=', + bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', + startDate: '2017-02-14T12:51:48.000Z', + endDate: '2123-03-16T12:51:48.000Z', +}); export const encryptedTreasureMapBase64 = - 'RU1hcAABAACSxGIDdzg9r81RRC+kQGy0C0s8LdQXwcDf+MJtuJ9+i3Zp+bICp+WRepaHInVIh7O3hiiz0Frs4Xr7sct2nNaXHrbFLgzKHdNnmhg7EIpPWs2HUJMjwh4Ny0rKVyx2/Ilvh24t5sUGtg2tnZKEMnf1Z9E8wIEv1itZmsjjXmnlrshHxPdQ/1cJneXIU+ch+n7t+BJQkOJSfLE74++KZJ+mUnPpCh8QWssHeeGNBeB2oB9CtpmcBzDoLdJJClTVte4OmADofiQ6RJswQfGCM6Gw21rJOqm5q7/6jaR47gvMlu/A94Y3WkiUAFbaGcr4vQzeAflRsR0N9LPfKIIqHfajWNCEagp0GgoaG6DsQVeEnaL7z+meg8HeQdO+m09OFh4b8Ic0m2BtYSscrlUBlU5JWrxKtLjPuxBTIQyX8FQV6gYnLyzkAEBAOgPgjuwHsTZeFUatTDYYhA/Y2hlfdmLeYW5pZ74/+uNJBof/9QB1beCn+TfrTbVyjGADn2JU8NwhnsNdDM3XFQy6WUoHLrnTs4lYj16unC7Ir6DnI6UXj84ixjli2ge8wgHpZkmQJKrMc1HvfDlM3TWG18rvEB0fE70sSMGhhegp/cjh52C3eTUACaEkO127r1kF/NjCDtbOsk9CnD2mwGyOMdr17B5TojWINwJ0SsT9gCgnZjEuxbX45NAr+M+Nz4TwbPF8ap67mj3dbGvuokNHsBfjn8YCRAIDgP83SYnR/Y4VqqKL7uzRbfmu2ljCkTohe9rJUNhwaVYpl0PODuVZQo1jAmSBZ9/HzVNbkqGwiTAtjz5CHXW7oKl8SwXiBUHXXOu0jvi8RgOP9BZeoL48sFLiSWDIxI2XAxEEuaD16vgvvthl0mxklP7R5p7vvAfAdP/lNCy28B5r7PPsXh6MmYyyaObetm2HzcJhB3sijfXHM5iEoQ41PKXRTRxrgAw1eVAjOnNW3Ld9LJEOqB6hh/U3UYUPhrXkk4kdc7EAm4gTMbq5wmEsZQRnWIn5IsBEAMFlh3z6PvtrcuwRJkIwQSF96Ai0kE776e9pjsiuEUyZFyn4wRTfg2mxdteiC4vKTBxh2BJNVxzRONtTMgcZ0JHZpCf/ZRct2Bke8F4eWn+AmtYDQEdASHnJQtjQPe5Fl5LoePZ1uIVaKgWywz6pV4zvX4hBAZ2gK/wo8hzJNRtAaKoAr8FQxG9HUlY2htZmCtI60m2a2y8Vj3IKTa2X/2PqB/OnuZEq18FCve91TmKqghCHqteB1ON6dMl+1XK4WpTlyELbha6cni8er2w4dDO+P6e40lWzQJKUlfyYfCoB8uRDCLw82a58kG80dYGZFZAmGhqItL9RC6Ofm2TBq0F+jfdSS3DVxrJG5AldhuKbIK73ojAaLV8FVjRyT6uwo6M4JAj12krCgbvOXUaHM3jT08ZFmOI0ldmzG/I6I73LHEGm7Hey6a+K4ry5EiXA+KgLMNA8pct6HihOKBws7ckClqYOos6nAyUkABpbASdfmyat8jtUNyIXprEtZr4wvB/2E011cf9EsnIVhEovpPmvMp7IdgID0Qxs219MFFr7sJ5XjHeOINA4jb6gh5r5uQzuYtpmxv7ppi8fs/x0co8pOsgHDfy4zRGDys/Z9f/QrEB6VHms9xh7Kwpp3HLAtCcEAkCg+AsnC++6VMwWwvcLbjfE2uIYsVJJ+ciX7rSYZyDzC0IayyqNJLgS30li1xPb2u5YHMA+2vzSQGx9Zae2QSXFwDQ5Qui94RfQP//x6pqZuutXI8fqGDey50DYtPYd8J6AiKcoOPBivqSTMXj2Nb9FItbDm5DYTRM7CmQkmbYLR+/hord5AFSYauEB3zsqME3vD3mBY5OvWfPqnSpUUGe2RTF+u3mtmcfptaO9fwWSr6ckBwBMB42DuoyXVuYsdXA6WCtqCjQGqCBG0qsvOJFA3Rd2q5cy4Q+V537LlOJAkhSu/9/1WDma7JbELCSq5fCl9hOVJ96c8CfJacSVhNM4LRT71qDEf9wI2AVQrjYeMGdSXDEtidXyz1oPZpCoc8mPALPLOvgwBhLYK175NteQ9GQ+9ySMI1NWBbv/FP1jwKrFtT46FdFQHKv8xc/z29muPNPmgev3ht1tvPzrrvyCtQLdASaaHTc1FlZcMlYopgOxvz8q3MUki0wjwpVudTSaowf8Bfduj3r6TSPo11+k97P49fy3LLxOsTrvVaetD0rNIs704MI0/DmNZd0FkqyX6JzFILUCZunK9azQoJX9vr9BzsQJ2r3vmlSYNA+n9C2+YIcZqE7nHjO38XTbnfCcrZvWohBZ+Q9gDWPD7Y378w3OgIrkzNEUJhFHCahLVj41l3Y3HIMI8GM98uejNjEVZLW6NEMrjhGh+aimmvgFCAIJYXFMFe8oSfeFRQBQnvOc0y+XJl5Mbm6g5oKp'; - -export const deployedStrategyJSON = - '{"policy":{"id":"base64:tKuR/HipMeU4trCdytbZCQ==","label":"test","policyKey":"base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C","encryptedTreasureMap":"base64:RU1hcAABAACSxGIDdzg9r81RRC+kQGy0C0s8LdQXwcDf+MJtuJ9+i3Zp+bICp+WRepaHInVIh7O3hiiz0Frs4Xr7sct2nNaXHrbFLgzKHdNnmhg7EIpPWs2HUJMjwh4Ny0rKVyx2/Ilvh24t5sUGtg2tnZKEMnf1Z9E8wIEv1itZmsjjXmnlrshHxPdQ/1cJneXIU+ch+n7t+BJQkOJSfLE74++KZJ+mUnPpCh8QWssHeeGNBeB2oB9CtpmcBzDoLdJJClTVte4OmADofiQ6RJswQfGCM6Gw21rJOqm5q7/6jaR47gvMlu/A94Y3WkiUAFbaGcr4vQzeAflRsR0N9LPfKIIqHfajWNCEagp0GgoaG6DsQVeEnaL7z+meg8HeQdO+m09OFh4b8Ic0m2BtYSscrlUBlU5JWrxKtLjPuxBTIQyX8FQV6gYnLyzkAEBAOgPgjuwHsTZeFUatTDYYhA/Y2hlfdmLeYW5pZ74/+uNJBof/9QB1beCn+TfrTbVyjGADn2JU8NwhnsNdDM3XFQy6WUoHLrnTs4lYj16unC7Ir6DnI6UXj84ixjli2ge8wgHpZkmQJKrMc1HvfDlM3TWG18rvEB0fE70sSMGhhegp/cjh52C3eTUACaEkO127r1kF/NjCDtbOsk9CnD2mwGyOMdr17B5TojWINwJ0SsT9gCgnZjEuxbX45NAr+M+Nz4TwbPF8ap67mj3dbGvuokNHsBfjn8YCRAIDgP83SYnR/Y4VqqKL7uzRbfmu2ljCkTohe9rJUNhwaVYpl0PODuVZQo1jAmSBZ9/HzVNbkqGwiTAtjz5CHXW7oKl8SwXiBUHXXOu0jvi8RgOP9BZeoL48sFLiSWDIxI2XAxEEuaD16vgvvthl0mxklP7R5p7vvAfAdP/lNCy28B5r7PPsXh6MmYyyaObetm2HzcJhB3sijfXHM5iEoQ41PKXRTRxrgAw1eVAjOnNW3Ld9LJEOqB6hh/U3UYUPhrXkk4kdc7EAm4gTMbq5wmEsZQRnWIn5IsBEAMFlh3z6PvtrcuwRJkIwQSF96Ai0kE776e9pjsiuEUyZFyn4wRTfg2mxdteiC4vKTBxh2BJNVxzRONtTMgcZ0JHZpCf/ZRct2Bke8F4eWn+AmtYDQEdASHnJQtjQPe5Fl5LoePZ1uIVaKgWywz6pV4zvX4hBAZ2gK/wo8hzJNRtAaKoAr8FQxG9HUlY2htZmCtI60m2a2y8Vj3IKTa2X/2PqB/OnuZEq18FCve91TmKqghCHqteB1ON6dMl+1XK4WpTlyELbha6cni8er2w4dDO+P6e40lWzQJKUlfyYfCoB8uRDCLw82a58kG80dYGZFZAmGhqItL9RC6Ofm2TBq0F+jfdSS3DVxrJG5AldhuKbIK73ojAaLV8FVjRyT6uwo6M4JAj12krCgbvOXUaHM3jT08ZFmOI0ldmzG/I6I73LHEGm7Hey6a+K4ry5EiXA+KgLMNA8pct6HihOKBws7ckClqYOos6nAyUkABpbASdfmyat8jtUNyIXprEtZr4wvB/2E011cf9EsnIVhEovpPmvMp7IdgID0Qxs219MFFr7sJ5XjHeOINA4jb6gh5r5uQzuYtpmxv7ppi8fs/x0co8pOsgHDfy4zRGDys/Z9f/QrEB6VHms9xh7Kwpp3HLAtCcEAkCg+AsnC++6VMwWwvcLbjfE2uIYsVJJ+ciX7rSYZyDzC0IayyqNJLgS30li1xPb2u5YHMA+2vzSQGx9Zae2QSXFwDQ5Qui94RfQP//x6pqZuutXI8fqGDey50DYtPYd8J6AiKcoOPBivqSTMXj2Nb9FItbDm5DYTRM7CmQkmbYLR+/hord5AFSYauEB3zsqME3vD3mBY5OvWfPqnSpUUGe2RTF+u3mtmcfptaO9fwWSr6ckBwBMB42DuoyXVuYsdXA6WCtqCjQGqCBG0qsvOJFA3Rd2q5cy4Q+V537LlOJAkhSu/9/1WDma7JbELCSq5fCl9hOVJ96c8CfJacSVhNM4LRT71qDEf9wI2AVQrjYeMGdSXDEtidXyz1oPZpCoc8mPALPLOvgwBhLYK175NteQ9GQ+9ySMI1NWBbv/FP1jwKrFtT46FdFQHKv8xc/z29muPNPmgev3ht1tvPzrrvyCtQLdASaaHTc1FlZcMlYopgOxvz8q3MUki0wjwpVudTSaowf8Bfduj3r6TSPo11+k97P49fy3LLxOsTrvVaetD0rNIs704MI0/DmNZd0FkqyX6JzFILUCZunK9azQoJX9vr9BzsQJ2r3vmlSYNA+n9C2+YIcZqE7nHjO38XTbnfCcrZvWohBZ+Q9gDWPD7Y378w3OgIrkzNEUJhFHCahLVj41l3Y3HIMI8GM98uejNjEVZLW6NEMrjhGh+aimmvgFCAIJYXFMFe8oSfeFRQBQnvOc0y+XJl5Mbm6g5oKp","aliceVerifyingKey":"base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj","size":3,"startTimestamp":"2017-02-14T12:51:48.000Z","endTimestamp":"2017-03-16T12:51:48.000Z","txHash":"0x1234567890123456789012345678901234567890"},"cohortConfig":{"ursulaAddresses":["0x5cf1703a1c99a4b42eb056535840e93118177232","0x7fff551249d223f723557a96a0e1a469c79cc934","0x9c7c824239d3159327024459ad69bb215859bd25"],"threshold":2,"shares":3,"porterUri":"https://_this.should.crash"},"bobSecretKeyBytes":"base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk="}'; + 'RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw='; +export const deployedStrategyJSON = JSON.stringify({ + policy: { + id: 'base64:tKuR/HipMeU4trCdytbZCQ==', + label: 'test', + policyKey: 'base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C', + encryptedTreasureMap: + 'base64:RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw=', + aliceVerifyingKey: 'base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj', + size: 3, + startTimestamp: '2017-02-14T12:51:48.000Z', + endTimestamp: '2123-03-16T12:51:48.000Z', + txHash: '0x1234567890123456789012345678901234567890', + }, + cohortConfig: { + ursulaAddresses: [ + '0x5cf1703a1c99a4b42eb056535840e93118177232', + '0x7fff551249d223f723557a96a0e1a469c79cc934', + '0x9c7c824239d3159327024459ad69bb215859bd25', + ], + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }, + bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', +}); export const aliceSecretKeyBytes = new Uint8Array([ 55, 82, 190, 189, 203, 164, 60, 148, 36, 86, 46, 123, 63, 152, 215, 113, 174, 86, 244, 44, 23, 227, 197, 68, 5, 85, 116, 31, 208, 152, 88, 53, @@ -17,5 +53,13 @@ export const bobSecretKeyBytes = new Uint8Array([ 229, 2, 106, 176, 205, 33, 168, 23, 213, 233, 200, 238, 11, 193, 153, ]); -export const decrypterJSON = - '{"porterUri":"https://_this.should.crash/","policyEncryptingKeyBytes":"base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C","encryptedTreasureMapBytes":"base64:RU1hcAABAACSxGIDdzg9r81RRC+kQGy0C0s8LdQXwcDf+MJtuJ9+i3Zp+bICp+WRepaHInVIh7O3hiiz0Frs4Xr7sct2nNaXHrbFLgzKHdNnmhg7EIpPWs2HUJMjwh4Ny0rKVyx2/Ilvh24t5sUGtg2tnZKEMnf1Z9E8wIEv1itZmsjjXmnlrshHxPdQ/1cJneXIU+ch+n7t+BJQkOJSfLE74++KZJ+mUnPpCh8QWssHeeGNBeB2oB9CtpmcBzDoLdJJClTVte4OmADofiQ6RJswQfGCM6Gw21rJOqm5q7/6jaR47gvMlu/A94Y3WkiUAFbaGcr4vQzeAflRsR0N9LPfKIIqHfajWNCEagp0GgoaG6DsQVeEnaL7z+meg8HeQdO+m09OFh4b8Ic0m2BtYSscrlUBlU5JWrxKtLjPuxBTIQyX8FQV6gYnLyzkAEBAOgPgjuwHsTZeFUatTDYYhA/Y2hlfdmLeYW5pZ74/+uNJBof/9QB1beCn+TfrTbVyjGADn2JU8NwhnsNdDM3XFQy6WUoHLrnTs4lYj16unC7Ir6DnI6UXj84ixjli2ge8wgHpZkmQJKrMc1HvfDlM3TWG18rvEB0fE70sSMGhhegp/cjh52C3eTUACaEkO127r1kF/NjCDtbOsk9CnD2mwGyOMdr17B5TojWINwJ0SsT9gCgnZjEuxbX45NAr+M+Nz4TwbPF8ap67mj3dbGvuokNHsBfjn8YCRAIDgP83SYnR/Y4VqqKL7uzRbfmu2ljCkTohe9rJUNhwaVYpl0PODuVZQo1jAmSBZ9/HzVNbkqGwiTAtjz5CHXW7oKl8SwXiBUHXXOu0jvi8RgOP9BZeoL48sFLiSWDIxI2XAxEEuaD16vgvvthl0mxklP7R5p7vvAfAdP/lNCy28B5r7PPsXh6MmYyyaObetm2HzcJhB3sijfXHM5iEoQ41PKXRTRxrgAw1eVAjOnNW3Ld9LJEOqB6hh/U3UYUPhrXkk4kdc7EAm4gTMbq5wmEsZQRnWIn5IsBEAMFlh3z6PvtrcuwRJkIwQSF96Ai0kE776e9pjsiuEUyZFyn4wRTfg2mxdteiC4vKTBxh2BJNVxzRONtTMgcZ0JHZpCf/ZRct2Bke8F4eWn+AmtYDQEdASHnJQtjQPe5Fl5LoePZ1uIVaKgWywz6pV4zvX4hBAZ2gK/wo8hzJNRtAaKoAr8FQxG9HUlY2htZmCtI60m2a2y8Vj3IKTa2X/2PqB/OnuZEq18FCve91TmKqghCHqteB1ON6dMl+1XK4WpTlyELbha6cni8er2w4dDO+P6e40lWzQJKUlfyYfCoB8uRDCLw82a58kG80dYGZFZAmGhqItL9RC6Ofm2TBq0F+jfdSS3DVxrJG5AldhuKbIK73ojAaLV8FVjRyT6uwo6M4JAj12krCgbvOXUaHM3jT08ZFmOI0ldmzG/I6I73LHEGm7Hey6a+K4ry5EiXA+KgLMNA8pct6HihOKBws7ckClqYOos6nAyUkABpbASdfmyat8jtUNyIXprEtZr4wvB/2E011cf9EsnIVhEovpPmvMp7IdgID0Qxs219MFFr7sJ5XjHeOINA4jb6gh5r5uQzuYtpmxv7ppi8fs/x0co8pOsgHDfy4zRGDys/Z9f/QrEB6VHms9xh7Kwpp3HLAtCcEAkCg+AsnC++6VMwWwvcLbjfE2uIYsVJJ+ciX7rSYZyDzC0IayyqNJLgS30li1xPb2u5YHMA+2vzSQGx9Zae2QSXFwDQ5Qui94RfQP//x6pqZuutXI8fqGDey50DYtPYd8J6AiKcoOPBivqSTMXj2Nb9FItbDm5DYTRM7CmQkmbYLR+/hord5AFSYauEB3zsqME3vD3mBY5OvWfPqnSpUUGe2RTF+u3mtmcfptaO9fwWSr6ckBwBMB42DuoyXVuYsdXA6WCtqCjQGqCBG0qsvOJFA3Rd2q5cy4Q+V537LlOJAkhSu/9/1WDma7JbELCSq5fCl9hOVJ96c8CfJacSVhNM4LRT71qDEf9wI2AVQrjYeMGdSXDEtidXyz1oPZpCoc8mPALPLOvgwBhLYK175NteQ9GQ+9ySMI1NWBbv/FP1jwKrFtT46FdFQHKv8xc/z29muPNPmgev3ht1tvPzrrvyCtQLdASaaHTc1FlZcMlYopgOxvz8q3MUki0wjwpVudTSaowf8Bfduj3r6TSPo11+k97P49fy3LLxOsTrvVaetD0rNIs704MI0/DmNZd0FkqyX6JzFILUCZunK9azQoJX9vr9BzsQJ2r3vmlSYNA+n9C2+YIcZqE7nHjO38XTbnfCcrZvWohBZ+Q9gDWPD7Y378w3OgIrkzNEUJhFHCahLVj41l3Y3HIMI8GM98uejNjEVZLW6NEMrjhGh+aimmvgFCAIJYXFMFe8oSfeFRQBQnvOc0y+XJl5Mbm6g5oKp","publisherVerifyingKeyBytes":"base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj","bobSecretKeyBytes":"base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk="}'; +export const decrypterJSON = JSON.stringify({ + porterUri: 'https://_this.should.crash/', + policyEncryptingKeyBytes: + 'base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C', + encryptedTreasureMapBytes: + 'base64:RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw=', + publisherVerifyingKeyBytes: + 'base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj', + bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', +}); diff --git a/test/utils.ts b/test/utils.ts index c27b5da7b..f4a0bac71 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -6,6 +6,7 @@ import { Capsule, CapsuleFrag, EncryptedTreasureMap, + PublicKey, reencrypt, SecretKey, VerifiedCapsuleFrag, @@ -37,7 +38,7 @@ const mockConfig: Configuration = { }; export const mockBob = (): Bob => { - const secretKey = SecretKey.fromBytes( + const secretKey = SecretKey.fromBEBytes( toBytes('fake-secret-key-32-bytes-bob-xxx') ); return Bob.fromSecretKey(mockConfig, secretKey); @@ -49,8 +50,8 @@ export const mockRemoteBob = (): RemoteBob => { }; export const mockAlice = (aliceKey = 'fake-secret-key-32-bytes-alice-x') => { - const secretKey = SecretKey.fromBytes(toBytes(aliceKey)); - const provider = mockWeb3Provider(secretKey.toSecretBytes()); + const secretKey = SecretKey.fromBEBytes(toBytes(aliceKey)); + const provider = mockWeb3Provider(secretKey.toBEBytes()); return Alice.fromSecretKey(mockConfig, secretKey, provider); }; @@ -80,7 +81,7 @@ export const mockWeb3Provider = ( } as unknown as ethers.providers.Web3Provider; }; -export const mockUrsulas = (): readonly Ursula[] => { +export const makeTestUrsulas = (): readonly Ursula[] => { return [ { encryptingKey: SecretKey.random().publicKey(), @@ -123,7 +124,7 @@ export const mockGetUrsulas = (ursulas: readonly Ursula[]) => { return { result: { ursulas: mockUrsulas.map(({ encryptingKey, uri, checksumAddress }) => ({ - encrypting_key: toHexString(encryptingKey.toBytes()), + encrypting_key: toHexString(encryptingKey.toCompressedBytes()), uri: uri, checksum_address: checksumAddress, })), @@ -148,11 +149,6 @@ export const mockCFragResponse = ( verifiedKFrags: readonly VerifiedKeyFrag[], capsule: Capsule ): readonly RetrieveCFragsResponse[] => { - if (ursulas.length !== verifiedKFrags.length) { - throw new Error( - 'Number of verifiedKFrags must match the number of Ursulas' - ); - } const reencrypted = verifiedKFrags .map((kFrag) => reencrypt(capsule, kFrag)) .map((cFrag) => CapsuleFrag.fromBytes(cFrag.toBytes())); @@ -179,8 +175,15 @@ export const mockRetrieveCFragsRequestThrows = () => { .mockRejectedValue(new Error('fake-reencryption-request-failed-error')); }; -export const mockGenerateKFrags = () => { - return jest.spyOn(Alice.prototype as any, 'generateKFrags'); +export const mockGenerateKFrags = (withValue?: { + delegatingKey: PublicKey; + verifiedKFrags: VerifiedKeyFrag[]; +}) => { + const spy = jest.spyOn(Alice.prototype as any, 'generateKFrags'); + if (withValue) { + return spy.mockImplementation(() => withValue); + } + return spy; }; export const mockEncryptTreasureMap = (withValue?: EncryptedTreasureMap) => { diff --git a/yarn.lock b/yarn.lock index bf7e3e148..98cab979a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1643,10 +1643,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nucypher/nucypher-core@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.4.0.tgz#1388abde9de61ed848959b86bc96e3d12ef97ae4" - integrity sha512-Pu4tRCwf7lnhZSoMjp889uTIUM7bNmYEe05TOOCuMYbJKmQWe7tYKeH0+rflUBISzGtMx2+xDe2CYYTyEGkvSw== +"@nucypher/nucypher-core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.7.0.tgz#2549364f39087a32be3abd8510baa45c1089d205" + integrity sha512-L8jDHIkEPgK4rPOwf7UkePGI/AHnP77TyE7iktO1G/bMGPZzsZ4YVg0Tw13T/3mPLEVjFOFZqA+h75wK3rHU6g== "@sideway/address@^4.1.3": version "4.1.4" From 89a50de8c6dff3ff2dfef22f2724c0425f45daac Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 4 May 2023 13:36:11 +0200 Subject: [PATCH 08/98] handle missing contracts --- src/agents/contracts.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/agents/contracts.ts b/src/agents/contracts.ts index 79c90bdaf..92c0b12f1 100644 --- a/src/agents/contracts.ts +++ b/src/agents/contracts.ts @@ -1,22 +1,22 @@ import { ChainId, ChecksumAddress } from '../types'; type Contracts = { - readonly SUBSCRIPTION_MANAGER: ChecksumAddress; - readonly COORDINATOR: ChecksumAddress; + readonly SUBSCRIPTION_MANAGER: ChecksumAddress | undefined; + readonly COORDINATOR: ChecksumAddress | undefined; }; const POLYGON: Contracts = { SUBSCRIPTION_MANAGER: '0xB0194073421192F6Cf38d72c791Be8729721A0b3', - COORDINATOR: '0x0', + COORDINATOR: undefined, }; const MUMBAI: Contracts = { SUBSCRIPTION_MANAGER: '0xb9015d7b35ce7c81dde38ef7136baa3b1044f313', - COORDINATOR: '0x0', + COORDINATOR: undefined, }; const GOERLI: Contracts = { - SUBSCRIPTION_MANAGER: '0x0', + SUBSCRIPTION_MANAGER: undefined, COORDINATOR: '0x2cf19429168a0943992D8e7dE534E9b802C687B6', }; @@ -36,7 +36,11 @@ export const getContract = ( if (!Object.keys(CONTRACTS[chainId as ChainId]).includes(contract)) { throw new Error(`No contract found for name: ${contract}`); } - return CONTRACTS[chainId as ChainId][contract]; + const address = CONTRACTS[chainId as ChainId][contract]; + if (!address) { + throw new Error(`No address found for contract: ${contract}`); + } + return address; }; export const DEFAULT_WAIT_N_CONFIRMATIONS = 1; From 390a3abdddfddc03a3efb4302593c9d42b7f0fba Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 10 May 2023 16:07:37 +0200 Subject: [PATCH 09/98] add index to returnValueTest --- src/policies/conditions.ts | 3 +++ test/integration/conditions.test.ts | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/policies/conditions.ts b/src/policies/conditions.ts index 80ac8258c..4a6b8dcbf 100644 --- a/src/policies/conditions.ts +++ b/src/policies/conditions.ts @@ -118,6 +118,7 @@ export class Condition { protected makeReturnValueTest() { return Joi.object({ + index: Joi.number().required(), comparator: Joi.string() .valid(...Condition.COMPARATOR_OPERATORS) .required(), @@ -303,6 +304,7 @@ class ERC721Ownership extends EvmCondition { parameters: [], standardContractType: 'ERC721', returnValueTest: { + index: 0, comparator: '==', value: ':userAddress', }, @@ -315,6 +317,7 @@ class ERC721Balance extends EvmCondition { parameters: [':userAddress'], standardContractType: 'ERC721', returnValueTest: { + index: 0, comparator: '>', value: '0', }, diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 373a2d54e..179c2d836 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -78,6 +78,7 @@ describe('conditions set to/from json', () => { describe('standard conditions types validation', () => { const returnValueTest = { + index: 0, comparator: '>', value: '100', }; @@ -123,6 +124,7 @@ describe('standard conditions types validation', () => { const badTimelockCondition = { // Intentionally replacing `returnValueTest` with an invalid test returnValueTest: { + index: 0, comparator: 'not-a-comparator', value: '100', }, @@ -191,6 +193,7 @@ describe('produce context parameters from conditions', () => { method, parameters: [contextParam], returnValueTest: { + index: 0, comparator: '==', value: contextParam, }, @@ -224,6 +227,7 @@ describe('produce context parameters from conditions', () => { method: 'balanceOf', parameters: [contextParam], returnValueTest: { + index: 0, comparator: '==', value: contextParam, }, @@ -246,6 +250,7 @@ describe('condition context', () => { method: 'eth_getBalance', parameters: [':userAddress'], returnValueTest: { + index: 0, comparator: '==', value: ':userAddress', }, @@ -269,6 +274,7 @@ describe('evm condition', () => { method: 'balanceOf', parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], returnValueTest: { + index: 0, comparator: '==', value: ':userAddress', }, From ad52ec174de3ad0ce1118b2fba7df47a96c262fb Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 10 May 2023 17:51:16 +0200 Subject: [PATCH 10/98] feat: make index in return value test optional --- src/policies/conditions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/policies/conditions.ts b/src/policies/conditions.ts index 4a6b8dcbf..bfa3ca97f 100644 --- a/src/policies/conditions.ts +++ b/src/policies/conditions.ts @@ -118,7 +118,7 @@ export class Condition { protected makeReturnValueTest() { return Joi.object({ - index: Joi.number().required(), + index: Joi.number().optional(), comparator: Joi.string() .valid(...Condition.COMPARATOR_OPERATORS) .required(), From fa287d6132155fa9a453b0f6e3afc76bd36b57eb Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 10 May 2023 17:47:33 +0200 Subject: [PATCH 11/98] feat: support user-provided params in condition context --- src/policies/conditions.ts | 64 +++++++++++++++------ test/integration/conditions.test.ts | 89 ++++++++++++++++++++++++----- 2 files changed, 122 insertions(+), 31 deletions(-) diff --git a/src/policies/conditions.ts b/src/policies/conditions.ts index bfa3ca97f..64a262c1f 100644 --- a/src/policies/conditions.ts +++ b/src/policies/conditions.ts @@ -192,6 +192,9 @@ const makeGuard = ( return schema; }; +export const USER_ADDRESS_PARAM = ':userAddress'; +export const SPECIAL_CONTEXT_PARAMS = [USER_ADDRESS_PARAM]; + class TimelockCondition extends Condition { public static readonly CONDITION_TYPE = 'timelock'; @@ -216,8 +219,8 @@ class RpcCondition extends Condition { string, string[] > = { - eth_getBalance: [':userAddress'], - balanceOf: [':userAddress'], + eth_getBalance: [USER_ADDRESS_PARAM], + balanceOf: [USER_ADDRESS_PARAM], }; public readonly schema = Joi.object({ @@ -258,7 +261,7 @@ class EvmCondition extends Condition { public static readonly METHODS_PER_CONTRACT_TYPE: Record = { ERC20: ['balanceOf'], ERC721: ['balanceOf', 'ownerOf'], - ERC1155: ['balanceOf'], + // ERC1155: ['balanceOf'], // TODO(#131) }; public static readonly PARAMETERS_PER_METHOD: Record = { balanceOf: ['address'], @@ -268,8 +271,8 @@ class EvmCondition extends Condition { string, string[] > = { - balanceOf: [':userAddress'], - ownerOf: [':userAddress'], + balanceOf: [USER_ADDRESS_PARAM], + ownerOf: [USER_ADDRESS_PARAM], }; private makeMethod = () => @@ -306,7 +309,7 @@ class ERC721Ownership extends EvmCondition { returnValueTest: { index: 0, comparator: '==', - value: ':userAddress', + value: USER_ADDRESS_PARAM, }, }; } @@ -314,7 +317,7 @@ class ERC721Ownership extends EvmCondition { class ERC721Balance extends EvmCondition { readonly defaults = { method: 'balanceOf', - parameters: [':userAddress'], + parameters: [USER_ADDRESS_PARAM], standardContractType: 'ERC721', returnValueTest: { index: 0, @@ -330,13 +333,24 @@ interface TypedSignature { address: string; } +export type CustomContextParam = string | number | boolean; + export class ConditionContext { private walletSignature?: Record; constructor( private readonly conditions: WASMConditions, - private readonly web3Provider: ethers.providers.Web3Provider - ) {} + private readonly web3Provider: ethers.providers.Web3Provider, + public readonly customParameters: Record = {} + ) { + Object.keys(customParameters).forEach((key) => { + if (SPECIAL_CONTEXT_PARAMS.includes(key)) { + throw new Error( + `Cannot use reserved parameter name ${key} as custom parameter` + ); + } + }); + } public async getOrCreateWalletSignature(): Promise { const address = await this.web3Provider.getSigner().getAddress(); @@ -443,16 +457,34 @@ export class ConditionContext { } public toJson = async (): Promise => { - const userAddressParam = this.conditions - .toString() - .includes(':userAddress'); - if (!userAddressParam) { - return JSON.stringify({}); + const payload: Record = {}; + if (this.conditions.toString().includes(USER_ADDRESS_PARAM)) { + payload[USER_ADDRESS_PARAM] = await this.getOrCreateWalletSignature(); } - const typedSignature = await this.getOrCreateWalletSignature(); - const payload = { ':userAddress': typedSignature }; + + const conditions = JSON.parse(this.conditions.toString()); + conditions.forEach((cond: { parameters: string[] }) => { + cond.parameters.forEach((key) => { + if ( + !(key in this.customParameters) && + !SPECIAL_CONTEXT_PARAMS.includes(key) + ) { + throw new Error(`Missing custom context parameter ${key}`); + } + }); + }); + + Object.keys(this.customParameters).forEach((key) => { + payload[key] = this.customParameters[key]; + }); return JSON.stringify(payload); }; + + public withCustomParams = ( + params: Record + ): ConditionContext => { + return new ConditionContext(this.conditions, this.web3Provider, params); + }; } const OR = new Operator('or'); diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 881b0cb10..d65b842f5 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -6,8 +6,13 @@ import { ConditionSet, Operator, } from '../../src'; +import { USER_ADDRESS_PARAM } from '../../src/policies/conditions'; import { fakeWeb3Provider } from '../utils'; +const TEST_CONTRACT_ADDR = '0xC36442b4a4522E871399CD717aBDD847Ab11FE88'; +const TEST_CONTRACT_ADDR_2 = '0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA'; +const TEST_CHAIN_ID = 5; + describe('operator', () => { it('should validate Operator', async () => { const op = new Operator('or'); @@ -21,20 +26,18 @@ describe('operator', () => { describe('conditions schema', () => { const condition = new Conditions.ERC721Balance(); let result = condition.validate({ - contractAddress: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88', + contractAddress: TEST_CONTRACT_ADDR, }); it('should validate', async () => { expect(result.error).toEqual(undefined); - expect(result.value.contractAddress).toEqual( - '0xC36442b4a4522E871399CD717aBDD847Ab11FE88' - ); + expect(result.value.contractAddress).toEqual(TEST_CONTRACT_ADDR); }); - result = condition.validate({ chain: 5 }); + result = condition.validate({ chain: TEST_CHAIN_ID }); it('should update the value of "chain"', async () => { expect(result.error).toEqual(undefined); - expect(result.value.chain).toEqual(5); + expect(result.value.chain).toEqual(TEST_CHAIN_ID); }); it('should validate chain id', async () => { @@ -47,10 +50,10 @@ describe('conditions schema', () => { describe('condition set', () => { const genuineUndead = new Conditions.ERC721Balance({ - contractAddress: '0x209e639a0EC166Ac7a1A4bA41968fa967dB30221', + contractAddress: TEST_CONTRACT_ADDR, }); const gnomePals = new Conditions.ERC721Balance({ - contractAddress: '0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA', + contractAddress: TEST_CONTRACT_ADDR_2, }); const conditions = new ConditionSet([ genuineUndead, @@ -64,13 +67,12 @@ describe('condition set', () => { }); describe('conditions set to/from json', () => { - const json = - '[{"chain":5,"method":"ownerOf","parameters":["3591"],"standardContractType":"ERC721","returnValueTest":{"comparator":"==","value":":userAddress"},"contractAddress":"0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77"}]'; + const json = `[{"chain":${TEST_CHAIN_ID},"method":"ownerOf","parameters":["3591"],"standardContractType":"ERC721","returnValueTest":{"comparator":"==","value":":userAddress"},"contractAddress":"${TEST_CONTRACT_ADDR}"}]`; const conditionSet = ConditionSet.fromJSON(json); it('should be a ConditionSet', async () => { expect(conditionSet.conditions[0].toObj().contractAddress).toEqual( - '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77' + TEST_CONTRACT_ADDR ); expect(conditionSet.toJson()).toEqual(json); }); @@ -248,11 +250,11 @@ describe('condition context', () => { const rpcCondition = new Conditions.RpcCondition({ chain: 5, method: 'eth_getBalance', - parameters: [':userAddress'], + parameters: [USER_ADDRESS_PARAM], returnValueTest: { index: 0, comparator: '==', - value: ':userAddress', + value: USER_ADDRESS_PARAM, }, }); const conditionSet = new ConditionSet([rpcCondition]); @@ -262,7 +264,64 @@ describe('condition context', () => { web3Provider ); const asJson = await conditionContext.toJson(); - expect(asJson).toBeDefined(); + expect(asJson).toContain(USER_ADDRESS_PARAM); + }); + + describe('supports user-defined parameters', () => { + const fakeFunctionAbi = { + name: 'myFunction', + type: 'function', + inputs: [ + { + name: 'account', + type: 'address', + }, + { + name: 'myCustomParam', + type: 'uint256', + }, + ], + outputs: [ + { + name: 'someValue', + type: 'uint256', + }, + ], + }; + const evmCondition = new Conditions.EvmCondition({ + chain: 5, + functionAbi: fakeFunctionAbi, + method: 'balanceOf', + contractAddress: '0x0000000000000000000000000000000000000000', + parameters: [USER_ADDRESS_PARAM, ':customParam'], + returnValueTest: { + index: 0, + comparator: '==', + value: USER_ADDRESS_PARAM, + }, + }); + const web3Provider = mockWeb3Provider(SecretKey.random().toBEBytes()); + const conditionSet = new ConditionSet([evmCondition]); + const conditionContext = new ConditionContext( + conditionSet.toWASMConditions(), + web3Provider + ); + + it('parses user-provided context parameters', async () => { + const customParams = { ':customParam': 0 }; + const asJson = await conditionContext + .withCustomParams(customParams) + .toJson(); + expect(asJson).toBeDefined(); + expect(asJson).toContain(USER_ADDRESS_PARAM); + expect(asJson).toContain(':customParam'); + }); + + it('throws on missing custom context param', async () => { + await expect(conditionContext.toJson()).rejects.toThrow( + 'Missing custom context parameter :customParam' + ); + }); }); }); @@ -276,7 +335,7 @@ describe('evm condition', () => { returnValueTest: { index: 0, comparator: '==', - value: ':userAddress', + value: USER_ADDRESS_PARAM, }, }; const standardContractType = 'ERC20'; From 9ebd7475a16fa020a580760b0b3b0cf76d3d27ac Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 10 May 2023 17:53:29 +0200 Subject: [PATCH 12/98] fix after rebase --- test/integration/conditions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index d65b842f5..a6edd52d5 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -300,7 +300,7 @@ describe('condition context', () => { value: USER_ADDRESS_PARAM, }, }); - const web3Provider = mockWeb3Provider(SecretKey.random().toBEBytes()); + const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); const conditionSet = new ConditionSet([evmCondition]); const conditionContext = new ConditionContext( conditionSet.toWASMConditions(), From 97e61b2fdc86ac3132073b111eb4e792e99018a3 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 10 May 2023 18:07:50 +0200 Subject: [PATCH 13/98] test custom param validation --- test/integration/conditions.test.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index a6edd52d5..3083a996e 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -6,7 +6,10 @@ import { ConditionSet, Operator, } from '../../src'; -import { USER_ADDRESS_PARAM } from '../../src/policies/conditions'; +import { + CustomContextParam, + USER_ADDRESS_PARAM, +} from '../../src/policies/conditions'; import { fakeWeb3Provider } from '../utils'; const TEST_CONTRACT_ADDR = '0xC36442b4a4522E871399CD717aBDD847Ab11FE88'; @@ -306,20 +309,31 @@ describe('condition context', () => { conditionSet.toWASMConditions(), web3Provider ); + const myCustomParam = ':customParam'; it('parses user-provided context parameters', async () => { - const customParams = { ':customParam': 0 }; + const customParams: Record = {}; + customParams[myCustomParam] = 1234; const asJson = await conditionContext .withCustomParams(customParams) .toJson(); expect(asJson).toBeDefined(); expect(asJson).toContain(USER_ADDRESS_PARAM); - expect(asJson).toContain(':customParam'); + expect(asJson).toContain(myCustomParam); }); it('throws on missing custom context param', async () => { await expect(conditionContext.toJson()).rejects.toThrow( - 'Missing custom context parameter :customParam' + `Missing custom context parameter ${myCustomParam}` + ); + }); + + it('throws on using reserved context parameter identifiers', () => { + const badCustomParams: Record = {}; + badCustomParams[USER_ADDRESS_PARAM] = 'this-will-throw'; + + expect(() => conditionContext.withCustomParams(badCustomParams)).toThrow( + `Cannot use reserved parameter name ${USER_ADDRESS_PARAM} as custom parameter` ); }); }); From c71b37a65dc2e76efb109b9b46ab51a44006a591 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 10 May 2023 19:59:49 +0200 Subject: [PATCH 14/98] refactor conditions dir structure --- src/characters/enrico.ts | 2 +- src/characters/porter.ts | 2 +- src/characters/universal-bob.ts | 2 +- src/conditions/condition-context.ts | 167 +++++++++ src/conditions/condition-set.ts | 64 ++++ src/conditions/conditions.ts | 248 ++++++++++++++ src/conditions/operator.ts | 20 ++ src/index.ts | 10 +- src/policies/conditions.ts | 502 ---------------------------- src/sdk/strategy.ts | 2 +- test/integration/conditions.test.ts | 12 +- test/integration/pre.test.ts | 2 +- 12 files changed, 512 insertions(+), 521 deletions(-) create mode 100644 src/conditions/condition-context.ts create mode 100644 src/conditions/condition-set.ts create mode 100644 src/conditions/conditions.ts create mode 100644 src/conditions/operator.ts delete mode 100644 src/policies/conditions.ts diff --git a/src/characters/enrico.ts b/src/characters/enrico.ts index bf79b269b..cd8bed5bc 100644 --- a/src/characters/enrico.ts +++ b/src/characters/enrico.ts @@ -1,7 +1,7 @@ import { MessageKit, PublicKey, SecretKey } from '@nucypher/nucypher-core'; +import { ConditionSet } from '../conditions/condition-set'; import { Keyring } from '../keyring'; -import { ConditionSet } from '../policies/conditions'; import { toBytes } from '../utils'; export class Enrico { diff --git a/src/characters/porter.ts b/src/characters/porter.ts index ee0df9a40..243262919 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -7,7 +7,7 @@ import { import axios, { AxiosResponse } from 'axios'; import qs from 'qs'; -import { ConditionContext } from '../policies/conditions'; +import { ConditionContext } from '../conditions/condition-context'; import { Base64EncodedBytes, ChecksumAddress, HexEncodedBytes } from '../types'; import { fromBase64, fromHexString, toBase64, toHexString } from '../utils'; diff --git a/src/characters/universal-bob.ts b/src/characters/universal-bob.ts index 3bdbb95d7..68c8bdfcd 100644 --- a/src/characters/universal-bob.ts +++ b/src/characters/universal-bob.ts @@ -8,10 +8,10 @@ import { } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; +import { ConditionSet } from '../conditions/condition-set'; import { Keyring } from '../keyring'; import { PolicyMessageKit } from '../kits/message'; import { RetrievalResult } from '../kits/retrieval'; -import { ConditionSet } from '../policies/conditions'; import { base64ToU8Receiver, u8ToBase64Replacer, zip } from '../utils'; import { Porter } from './porter'; diff --git a/src/conditions/condition-context.ts b/src/conditions/condition-context.ts new file mode 100644 index 000000000..3e52e6f19 --- /dev/null +++ b/src/conditions/condition-context.ts @@ -0,0 +1,167 @@ +import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; +import { ethers } from 'ethers'; +import { utils as ethersUtils } from 'ethers/lib/ethers'; + +import { Eip712TypedData, FormattedTypedData } from '../web3'; + +import { SPECIAL_CONTEXT_PARAMS, USER_ADDRESS_PARAM } from './conditions'; + +interface TypedSignature { + signature: string; + typedData: Eip712TypedData; + address: string; +} + +export type CustomContextParam = string | number | boolean; + +export class ConditionContext { + private walletSignature?: Record; + + constructor( + private readonly conditions: WASMConditions, + private readonly web3Provider: ethers.providers.Web3Provider, + public readonly customParameters: Record = {} + ) { + Object.keys(customParameters).forEach((key) => { + if (SPECIAL_CONTEXT_PARAMS.includes(key)) { + throw new Error( + `Cannot use reserved parameter name ${key} as custom parameter` + ); + } + }); + } + + public async getOrCreateWalletSignature(): Promise { + const address = await this.web3Provider.getSigner().getAddress(); + const storageKey = `wallet-signature-${address}`; + + // If we have a signature in localStorage, return it + const isLocalStorage = typeof localStorage !== 'undefined'; + if (isLocalStorage) { + const maybeSignature = localStorage.getItem(storageKey); + if (maybeSignature) { + return JSON.parse(maybeSignature); + } + } + + // If not, try returning from memory + const maybeSignature = this.walletSignature?.[address]; + if (maybeSignature) { + if (isLocalStorage) { + localStorage.setItem(storageKey, maybeSignature); + } + return JSON.parse(maybeSignature); + } + + // If at this point we didn't return, we need to create a new signature + const typedSignature = await this.createWalletSignature(); + + // Persist where you can + if (isLocalStorage) { + localStorage.setItem(storageKey, JSON.stringify(typedSignature)); + } + if (!this.walletSignature) { + this.walletSignature = {}; + } + this.walletSignature[address] = JSON.stringify(typedSignature); + return typedSignature; + } + + private async createWalletSignature(): Promise { + // Ensure freshness of the signature + const { blockNumber, blockHash, chainId } = await this.getChainData(); + const address = await this.web3Provider.getSigner().getAddress(); + const signatureText = `I'm the owner of address ${address} as of block number ${blockNumber}`; + const salt = ethersUtils.hexlify(ethersUtils.randomBytes(32)); + + const typedData: Eip712TypedData = { + types: { + Wallet: [ + { name: 'address', type: 'address' }, + { name: 'signatureText', type: 'string' }, + { name: 'blockNumber', type: 'uint256' }, + { name: 'blockHash', type: 'bytes32' }, + ], + }, + domain: { + name: 'tDec', + version: '1', + chainId, + salt, + }, + message: { + address, + signatureText, + blockNumber, + blockHash, + }, + }; + const signature = await this.web3Provider + .getSigner() + ._signTypedData(typedData.domain, typedData.types, typedData.message); + + const formattedTypedData: FormattedTypedData = { + ...typedData, + primaryType: 'Wallet', + types: { + ...typedData.types, + EIP712Domain: [ + { + name: 'name', + type: 'string', + }, + { + name: 'version', + type: 'string', + }, + { + name: 'chainId', + type: 'uint256', + }, + { + name: 'salt', + type: 'bytes32', + }, + ], + }, + }; + return { signature, typedData: formattedTypedData, address }; + } + + private async getChainData() { + const blockNumber = await this.web3Provider.getBlockNumber(); + const blockHash = (await this.web3Provider.getBlock(blockNumber)).hash; + const chainId = (await this.web3Provider.getNetwork()).chainId; + return { blockNumber, blockHash, chainId }; + } + + public toJson = async (): Promise => { + const payload: Record = {}; + if (this.conditions.toString().includes(USER_ADDRESS_PARAM)) { + payload[USER_ADDRESS_PARAM] = await this.getOrCreateWalletSignature(); + } + + const conditions = JSON.parse(this.conditions.toString()); + conditions.forEach((cond: { parameters: string[] }) => { + cond.parameters.forEach((key) => { + if ( + !(key in this.customParameters) && + !SPECIAL_CONTEXT_PARAMS.includes(key) + ) { + throw new Error(`Missing custom context parameter ${key}`); + } + }); + }); + + Object.keys(this.customParameters).forEach((key) => { + payload[key] = this.customParameters[key]; + }); + return JSON.stringify(payload); + }; + + public withCustomParams = ( + params: Record + ): ConditionContext => { + return new ConditionContext(this.conditions, this.web3Provider, params); + }; +} diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts new file mode 100644 index 000000000..f8d21e966 --- /dev/null +++ b/src/conditions/condition-set.ts @@ -0,0 +1,64 @@ +import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; +import { ethers } from 'ethers'; + +import { ConditionContext } from './condition-context'; +import { Condition } from './conditions'; +import { Operator } from './operator'; + +export class ConditionSet { + constructor( + public readonly conditions: ReadonlyArray + ) {} + + public validate() { + if (this.conditions.length % 2 === 0) { + throw new Error( + 'conditions must be odd length, ever other element being an operator' + ); + } + this.conditions.forEach((cnd: Condition | Operator, index) => { + if (index % 2 && cnd.constructor.name !== 'Operator') + throw new Error( + `${index} element must be an Operator; Got ${cnd.constructor.name}.` + ); + if (!(index % 2) && cnd.constructor.name === 'Operator') + throw new Error( + `${index} element must be a Condition; Got ${cnd.constructor.name}.` + ); + }); + return true; + } + + public toList() { + return this.conditions.map((cnd) => { + return cnd.toObj(); + }); + } + + public static fromList(list: ReadonlyArray>) { + return new ConditionSet( + list.map((ele: Record) => { + if ('operator' in ele) return Operator.fromObj(ele); + return Condition.fromObj(ele); + }) + ); + } + + public toJson() { + return JSON.stringify(this.toList()); + } + + public static fromJSON(json: string) { + return ConditionSet.fromList(JSON.parse(json)); + } + + public toWASMConditions() { + return new WASMConditions(this.toJson()); + } + + public buildContext( + provider: ethers.providers.Web3Provider + ): ConditionContext { + return new ConditionContext(this.toWASMConditions(), provider); + } +} diff --git a/src/conditions/conditions.ts b/src/conditions/conditions.ts new file mode 100644 index 000000000..1dcf5765d --- /dev/null +++ b/src/conditions/conditions.ts @@ -0,0 +1,248 @@ +import Joi, { ValidationError } from 'joi'; + +export class Condition { + public static readonly COMPARATOR_OPERATORS = [ + '==', + '>', + '<', + '>=', + '<=', + '!=', + ]; + + public static readonly SUPPORTED_CHAINS = [ + 1, // ethereum/mainnet + 5, // ethereum/goerli + 137, // polygon/mainnet + 80001, // polygon/mumbai + ]; + + public readonly schema = Joi.object(); + public readonly defaults = {}; + private validationError?: ValidationError; + + constructor(private readonly value: Record = {}) {} + + public get error(): string | undefined { + return this.validationError?.message; + } + + protected makeReturnValueTest() { + return Joi.object({ + index: Joi.number().optional(), + comparator: Joi.string() + .valid(...Condition.COMPARATOR_OPERATORS) + .required(), + value: Joi.alternatives(Joi.string(), Joi.number()).required(), + }); + } + + public toObj(): Record { + const { error, value } = this.validate(); + if (error) { + throw Error(error.message); + } + return value; + } + + public static fromObj(obj: Record) { + return new Condition(obj); + } + + public validate(data: Record = {}) { + const newValue = Object.assign(this.defaults, this.value, data); + return this.schema.validate(newValue); + } + + public getContextParameters(): string[] { + // Check all the places where context parameters may be hiding + const asObject = this.toObj(); + let paramsToCheck: string[] = []; + + // They may be hiding in the method parameters + const method = asObject['method'] as string; + if (method) { + const contextParams = RpcCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; + paramsToCheck = [...(contextParams ?? [])]; + } + + // Or in the ReturnValue test + const returnValueTest = asObject['returnValueTest'] as Record< + string, + string + >; + if (returnValueTest) { + paramsToCheck.push(returnValueTest['value']); + } + + // Merge & deduplicate found context parameters + paramsToCheck = [ + ...paramsToCheck, + ...((asObject['parameters'] as string[]) ?? []), + ]; + const withoutDuplicates = new Set( + paramsToCheck.filter((p) => paramsToCheck.includes(p)) + ); + return [...withoutDuplicates]; + } +} + +// A helper method for making complex Joi types +// It says "allow these `types` when `parent` value is given" +const makeGuard = ( + schema: Joi.StringSchema | Joi.ArraySchema, + types: Record, + parent: string +) => { + Object.entries(types).forEach(([key, value]) => { + schema = schema.when(parent, { + is: key, + then: value, + }); + }); + return schema; +}; + +export const USER_ADDRESS_PARAM = ':userAddress'; +export const SPECIAL_CONTEXT_PARAMS = [USER_ADDRESS_PARAM]; + +class TimelockCondition extends Condition { + public static readonly CONDITION_TYPE = 'timelock'; + + defaults = { + method: 'timelock', + }; + + public readonly schema = Joi.object({ + returnValueTest: this.makeReturnValueTest(), + method: 'timelock', + }); +} + +class RpcCondition extends Condition { + public static readonly CONDITION_TYPE = 'rpc'; + public static readonly RPC_METHODS = ['eth_getBalance', 'balanceOf']; + public static readonly PARAMETERS_PER_METHOD: Record = { + eth_getBalance: ['address'], + balanceOf: ['address'], + }; + public static readonly CONTEXT_PARAMETERS_PER_METHOD: Record< + string, + string[] + > = { + eth_getBalance: [USER_ADDRESS_PARAM], + balanceOf: [USER_ADDRESS_PARAM], + }; + + public readonly schema = Joi.object({ + chain: Joi.number() + .valid(...Condition.SUPPORTED_CHAINS) + .required(), + method: Joi.string() + .valid(...RpcCondition.RPC_METHODS) + .required(), + parameters: Joi.array().required(), + returnValueTest: this.makeReturnValueTest(), + }); + + public getContextParameters = (): string[] => { + const asObject = this.toObj(); + + const method = asObject['method'] as string; + const parameters = (asObject['parameters'] ?? []) as string[]; + + const context = RpcCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; + const returnValueTest = asObject['returnValueTest'] as Record< + string, + string + >; + + const maybeParams = [...(context ?? []), returnValueTest['value']]; + return parameters.filter((p) => maybeParams.includes(p)); + }; +} + +class EvmCondition extends Condition { + public static readonly CONDITION_TYPE = 'evm'; + public static readonly STANDARD_CONTRACT_TYPES = [ + 'ERC20', + 'ERC721', + // 'ERC1155', // TODO(#131) + ]; + public static readonly METHODS_PER_CONTRACT_TYPE: Record = { + ERC20: ['balanceOf'], + ERC721: ['balanceOf', 'ownerOf'], + // ERC1155: ['balanceOf'], // TODO(#131) + }; + public static readonly PARAMETERS_PER_METHOD: Record = { + balanceOf: ['address'], + ownerOf: ['tokenId'], + }; + public static readonly CONTEXT_PARAMETERS_PER_METHOD: Record< + string, + string[] + > = { + balanceOf: [USER_ADDRESS_PARAM], + ownerOf: [USER_ADDRESS_PARAM], + }; + + private makeMethod = () => + makeGuard( + Joi.string(), + EvmCondition.METHODS_PER_CONTRACT_TYPE, + 'standardContractType' + ); + + public readonly schema = Joi.object({ + contractAddress: Joi.string() + .pattern(new RegExp('^0x[a-fA-F0-9]{40}$')) + .required(), + chain: Joi.string() + .valid(...Condition.SUPPORTED_CHAINS) + .required(), + standardContractType: Joi.string() + .valid(...EvmCondition.STANDARD_CONTRACT_TYPES) + .optional(), + functionAbi: Joi.object().optional(), + method: this.makeMethod().required(), + parameters: Joi.array().required(), + returnValueTest: this.makeReturnValueTest(), + }) + // At most one of these keys needs to be present + .xor('standardContractType', 'functionAbi'); +} + +class ERC721Ownership extends EvmCondition { + readonly defaults = { + method: 'ownerOf', + parameters: [], + standardContractType: 'ERC721', + returnValueTest: { + index: 0, + comparator: '==', + value: USER_ADDRESS_PARAM, + }, + }; +} + +class ERC721Balance extends EvmCondition { + readonly defaults = { + method: 'balanceOf', + parameters: [USER_ADDRESS_PARAM], + standardContractType: 'ERC721', + returnValueTest: { + index: 0, + comparator: '>', + value: '0', + }, + }; +} + +export const Conditions = { + ERC721Ownership, + ERC721Balance, + EvmCondition, + TimelockCondition, + RpcCondition, + Condition, +}; diff --git a/src/conditions/operator.ts b/src/conditions/operator.ts new file mode 100644 index 000000000..e7fdca994 --- /dev/null +++ b/src/conditions/operator.ts @@ -0,0 +1,20 @@ +export class Operator { + static readonly LOGICAL_OPERATORS: ReadonlyArray = ['and', 'or']; + static readonly AND = new Operator('and'); + static readonly OR = new Operator('or'); + + public constructor(public readonly operator: string) { + if (!Operator.LOGICAL_OPERATORS.includes(operator)) { + throw `"${operator}" is not a valid operator`; + } + this.operator = operator; + } + + toObj() { + return { operator: this.operator }; + } + + static fromObj(obj: Record) { + return new Operator(obj.operator); + } +} diff --git a/src/index.ts b/src/index.ts index 5aa9d2005..83dffc869 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,13 +12,13 @@ export { Keyring } from './keyring'; export { Configuration, defaultConfiguration } from './config'; export { RevocationKit } from './kits/revocation'; export { PolicyMessageKit } from './kits/message'; +export { Conditions, Condition } from './conditions/conditions'; +export { Operator } from './conditions/operator'; +export { ConditionSet } from './conditions/condition-set'; export { - Conditions, - ConditionSet, ConditionContext, - Operator, - Condition, -} from './policies/conditions'; + CustomContextParam, +} from './conditions/condition-context'; export { Cohort } from './sdk/cohort'; export { DeployedStrategy, RevokedStrategy, Strategy } from './sdk/strategy'; diff --git a/src/policies/conditions.ts b/src/policies/conditions.ts deleted file mode 100644 index 64a262c1f..000000000 --- a/src/policies/conditions.ts +++ /dev/null @@ -1,502 +0,0 @@ -import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; -import { ethers, utils as ethersUtils } from 'ethers'; -import Joi, { ValidationError } from 'joi'; - -import { Eip712TypedData, FormattedTypedData } from '../web3'; - -export class Operator { - static readonly LOGICAL_OPERATORS: ReadonlyArray = ['and', 'or']; - - public constructor(public readonly operator: string) { - if (!Operator.LOGICAL_OPERATORS.includes(operator)) { - throw `"${operator}" is not a valid operator`; - } - this.operator = operator; - } - - toObj() { - return { operator: this.operator }; - } - - static fromObj(obj: Record) { - return new Operator(obj.operator); - } - - public static AND() { - return new Operator('and'); - } - - public static OR() { - return new Operator('or'); - } -} - -export class ConditionSet { - constructor( - public readonly conditions: ReadonlyArray - ) {} - - public validate() { - if (this.conditions.length % 2 === 0) { - throw new Error( - 'conditions must be odd length, ever other element being an operator' - ); - } - this.conditions.forEach((cnd: Condition | Operator, index) => { - if (index % 2 && cnd.constructor.name !== 'Operator') - throw new Error( - `${index} element must be an Operator; Got ${cnd.constructor.name}.` - ); - if (!(index % 2) && cnd.constructor.name === 'Operator') - throw new Error( - `${index} element must be a Condition; Got ${cnd.constructor.name}.` - ); - }); - return true; - } - - public toList() { - return this.conditions.map((cnd) => { - return cnd.toObj(); - }); - } - - public static fromList(list: ReadonlyArray>) { - return new ConditionSet( - list.map((ele: Record) => { - if ('operator' in ele) return Operator.fromObj(ele); - return Condition.fromObj(ele); - }) - ); - } - - public toJson() { - return JSON.stringify(this.toList()); - } - - public static fromJSON(json: string) { - return ConditionSet.fromList(JSON.parse(json)); - } - - public toWASMConditions() { - return new WASMConditions(this.toJson()); - } - - public buildContext( - provider: ethers.providers.Web3Provider - ): ConditionContext { - return new ConditionContext(this.toWASMConditions(), provider); - } -} - -export class Condition { - public static readonly COMPARATOR_OPERATORS = [ - '==', - '>', - '<', - '>=', - '<=', - '!=', - ]; - - public static readonly SUPPORTED_CHAINS = [ - 1, // ethereum/mainnet - 5, // ethereum/goerli - 137, // polygon/mainnet - 80001, // polygon/mumbai - ]; - - public readonly schema = Joi.object(); - public readonly defaults = {}; - private validationError?: ValidationError; - - constructor(private readonly value: Record = {}) {} - - public get error(): string | undefined { - return this.validationError?.message; - } - - protected makeReturnValueTest() { - return Joi.object({ - index: Joi.number().optional(), - comparator: Joi.string() - .valid(...Condition.COMPARATOR_OPERATORS) - .required(), - value: Joi.alternatives(Joi.string(), Joi.number()).required(), - }); - } - - public toObj(): Record { - const { error, value } = this.validate(); - if (error) { - throw Error(error.message); - } - return value; - } - - public static fromObj(obj: Record) { - return new Condition(obj); - } - - public validate(data: Record = {}) { - const newValue = Object.assign(this.defaults, this.value, data); - return this.schema.validate(newValue); - } - - public getContextParameters(): string[] { - // Check all the places where context parameters may be hiding - const asObject = this.toObj(); - let paramsToCheck: string[] = []; - - // They may be hiding in the method parameters - const method = asObject['method'] as string; - if (method) { - const contextParams = RpcCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; - paramsToCheck = [...(contextParams ?? [])]; - } - - // Or in the ReturnValue test - const returnValueTest = asObject['returnValueTest'] as Record< - string, - string - >; - if (returnValueTest) { - paramsToCheck.push(returnValueTest['value']); - } - - // Merge & deduplicate found context parameters - paramsToCheck = [ - ...paramsToCheck, - ...((asObject['parameters'] as string[]) ?? []), - ]; - const withoutDuplicates = new Set( - paramsToCheck.filter((p) => paramsToCheck.includes(p)) - ); - return [...withoutDuplicates]; - } -} - -// A helper method for making complex Joi types -// It says "allow these `types` when `parent` value is given" -const makeGuard = ( - schema: Joi.StringSchema | Joi.ArraySchema, - types: Record, - parent: string -) => { - Object.entries(types).forEach(([key, value]) => { - schema = schema.when(parent, { - is: key, - then: value, - }); - }); - return schema; -}; - -export const USER_ADDRESS_PARAM = ':userAddress'; -export const SPECIAL_CONTEXT_PARAMS = [USER_ADDRESS_PARAM]; - -class TimelockCondition extends Condition { - public static readonly CONDITION_TYPE = 'timelock'; - - defaults = { - method: 'timelock', - }; - - public readonly schema = Joi.object({ - returnValueTest: this.makeReturnValueTest(), - method: 'timelock', - }); -} - -class RpcCondition extends Condition { - public static readonly CONDITION_TYPE = 'rpc'; - public static readonly RPC_METHODS = ['eth_getBalance', 'balanceOf']; - public static readonly PARAMETERS_PER_METHOD: Record = { - eth_getBalance: ['address'], - balanceOf: ['address'], - }; - public static readonly CONTEXT_PARAMETERS_PER_METHOD: Record< - string, - string[] - > = { - eth_getBalance: [USER_ADDRESS_PARAM], - balanceOf: [USER_ADDRESS_PARAM], - }; - - public readonly schema = Joi.object({ - chain: Joi.number() - .valid(...Condition.SUPPORTED_CHAINS) - .required(), - method: Joi.string() - .valid(...RpcCondition.RPC_METHODS) - .required(), - parameters: Joi.array().required(), - returnValueTest: this.makeReturnValueTest(), - }); - - public getContextParameters = (): string[] => { - const asObject = this.toObj(); - - const method = asObject['method'] as string; - const parameters = (asObject['parameters'] ?? []) as string[]; - - const context = RpcCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; - const returnValueTest = asObject['returnValueTest'] as Record< - string, - string - >; - - const maybeParams = [...(context ?? []), returnValueTest['value']]; - return parameters.filter((p) => maybeParams.includes(p)); - }; -} - -class EvmCondition extends Condition { - public static readonly CONDITION_TYPE = 'evm'; - public static readonly STANDARD_CONTRACT_TYPES = [ - 'ERC20', - 'ERC721', - // 'ERC1155', // TODO(#131) - ]; - public static readonly METHODS_PER_CONTRACT_TYPE: Record = { - ERC20: ['balanceOf'], - ERC721: ['balanceOf', 'ownerOf'], - // ERC1155: ['balanceOf'], // TODO(#131) - }; - public static readonly PARAMETERS_PER_METHOD: Record = { - balanceOf: ['address'], - ownerOf: ['tokenId'], - }; - public static readonly CONTEXT_PARAMETERS_PER_METHOD: Record< - string, - string[] - > = { - balanceOf: [USER_ADDRESS_PARAM], - ownerOf: [USER_ADDRESS_PARAM], - }; - - private makeMethod = () => - makeGuard( - Joi.string(), - EvmCondition.METHODS_PER_CONTRACT_TYPE, - 'standardContractType' - ); - - public readonly schema = Joi.object({ - contractAddress: Joi.string() - .pattern(new RegExp('^0x[a-fA-F0-9]{40}$')) - .required(), - chain: Joi.string() - .valid(...Condition.SUPPORTED_CHAINS) - .required(), - standardContractType: Joi.string() - .valid(...EvmCondition.STANDARD_CONTRACT_TYPES) - .optional(), - functionAbi: Joi.object().optional(), - method: this.makeMethod().required(), - parameters: Joi.array().required(), - returnValueTest: this.makeReturnValueTest(), - }) - // At most one of these keys needs to be present - .xor('standardContractType', 'functionAbi'); -} - -class ERC721Ownership extends EvmCondition { - readonly defaults = { - method: 'ownerOf', - parameters: [], - standardContractType: 'ERC721', - returnValueTest: { - index: 0, - comparator: '==', - value: USER_ADDRESS_PARAM, - }, - }; -} - -class ERC721Balance extends EvmCondition { - readonly defaults = { - method: 'balanceOf', - parameters: [USER_ADDRESS_PARAM], - standardContractType: 'ERC721', - returnValueTest: { - index: 0, - comparator: '>', - value: '0', - }, - }; -} - -interface TypedSignature { - signature: string; - typedData: Eip712TypedData; - address: string; -} - -export type CustomContextParam = string | number | boolean; - -export class ConditionContext { - private walletSignature?: Record; - - constructor( - private readonly conditions: WASMConditions, - private readonly web3Provider: ethers.providers.Web3Provider, - public readonly customParameters: Record = {} - ) { - Object.keys(customParameters).forEach((key) => { - if (SPECIAL_CONTEXT_PARAMS.includes(key)) { - throw new Error( - `Cannot use reserved parameter name ${key} as custom parameter` - ); - } - }); - } - - public async getOrCreateWalletSignature(): Promise { - const address = await this.web3Provider.getSigner().getAddress(); - const storageKey = `wallet-signature-${address}`; - - // If we have a signature in localStorage, return it - const isLocalStorage = typeof localStorage !== 'undefined'; - if (isLocalStorage) { - const maybeSignature = localStorage.getItem(storageKey); - if (maybeSignature) { - return JSON.parse(maybeSignature); - } - } - - // If not, try returning from memory - const maybeSignature = this.walletSignature?.[address]; - if (maybeSignature) { - if (isLocalStorage) { - localStorage.setItem(storageKey, maybeSignature); - } - return JSON.parse(maybeSignature); - } - - // If at this point we didn't return, we need to create a new signature - const typedSignature = await this.createWalletSignature(); - - // Persist where you can - if (isLocalStorage) { - localStorage.setItem(storageKey, JSON.stringify(typedSignature)); - } - if (!this.walletSignature) { - this.walletSignature = {}; - } - this.walletSignature[address] = JSON.stringify(typedSignature); - return typedSignature; - } - - private async createWalletSignature(): Promise { - // Ensure freshness of the signature - const { blockNumber, blockHash, chainId } = await this.getChainData(); - const address = await this.web3Provider.getSigner().getAddress(); - const signatureText = `I'm the owner of address ${address} as of block number ${blockNumber}`; - const salt = ethersUtils.hexlify(ethersUtils.randomBytes(32)); - - const typedData: Eip712TypedData = { - types: { - Wallet: [ - { name: 'address', type: 'address' }, - { name: 'signatureText', type: 'string' }, - { name: 'blockNumber', type: 'uint256' }, - { name: 'blockHash', type: 'bytes32' }, - ], - }, - domain: { - name: 'tDec', - version: '1', - chainId, - salt, - }, - message: { - address, - signatureText, - blockNumber, - blockHash, - }, - }; - const signature = await this.web3Provider - .getSigner() - ._signTypedData(typedData.domain, typedData.types, typedData.message); - - const formattedTypedData: FormattedTypedData = { - ...typedData, - primaryType: 'Wallet', - types: { - ...typedData.types, - EIP712Domain: [ - { - name: 'name', - type: 'string', - }, - { - name: 'version', - type: 'string', - }, - { - name: 'chainId', - type: 'uint256', - }, - { - name: 'salt', - type: 'bytes32', - }, - ], - }, - }; - return { signature, typedData: formattedTypedData, address }; - } - - private async getChainData() { - const blockNumber = await this.web3Provider.getBlockNumber(); - const blockHash = (await this.web3Provider.getBlock(blockNumber)).hash; - const chainId = (await this.web3Provider.getNetwork()).chainId; - return { blockNumber, blockHash, chainId }; - } - - public toJson = async (): Promise => { - const payload: Record = {}; - if (this.conditions.toString().includes(USER_ADDRESS_PARAM)) { - payload[USER_ADDRESS_PARAM] = await this.getOrCreateWalletSignature(); - } - - const conditions = JSON.parse(this.conditions.toString()); - conditions.forEach((cond: { parameters: string[] }) => { - cond.parameters.forEach((key) => { - if ( - !(key in this.customParameters) && - !SPECIAL_CONTEXT_PARAMS.includes(key) - ) { - throw new Error(`Missing custom context parameter ${key}`); - } - }); - }); - - Object.keys(this.customParameters).forEach((key) => { - payload[key] = this.customParameters[key]; - }); - return JSON.stringify(payload); - }; - - public withCustomParams = ( - params: Record - ): ConditionContext => { - return new ConditionContext(this.conditions, this.web3Provider, params); - }; -} - -const OR = new Operator('or'); -const AND = new Operator('and'); - -export const Conditions = { - ERC721Ownership, - ERC721Balance, - EvmCondition, - TimelockCondition, - RpcCondition, - Condition, - OR, - AND, -}; diff --git a/src/sdk/strategy.ts b/src/sdk/strategy.ts index 490e44d57..db713a304 100644 --- a/src/sdk/strategy.ts +++ b/src/sdk/strategy.ts @@ -10,7 +10,7 @@ import { Alice } from '../characters/alice'; import { Bob } from '../characters/bob'; import { Enrico } from '../characters/enrico'; import { tDecDecrypter } from '../characters/universal-bob'; -import { ConditionSet } from '../policies/conditions'; +import { ConditionSet } from '../conditions/condition-set'; import { EnactedPolicy, EnactedPolicyJSON } from '../policies/policy'; import { base64ToU8Receiver, u8ToBase64Replacer } from '../utils'; diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 3083a996e..141c8c2a0 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -6,10 +6,8 @@ import { ConditionSet, Operator, } from '../../src'; -import { - CustomContextParam, - USER_ADDRESS_PARAM, -} from '../../src/policies/conditions'; +import { CustomContextParam } from '../../src/conditions/condition-context'; +import { USER_ADDRESS_PARAM } from '../../src/conditions/conditions'; import { fakeWeb3Provider } from '../utils'; const TEST_CONTRACT_ADDR = '0xC36442b4a4522E871399CD717aBDD847Ab11FE88'; @@ -58,11 +56,7 @@ describe('condition set', () => { const gnomePals = new Conditions.ERC721Balance({ contractAddress: TEST_CONTRACT_ADDR_2, }); - const conditions = new ConditionSet([ - genuineUndead, - Conditions.OR, - gnomePals, - ]); + const conditions = new ConditionSet([genuineUndead, Operator.AND, gnomePals]); it('should validate', async () => { expect(conditions.validate()).toEqual(true); diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index ef3565a42..106814b19 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -95,7 +95,7 @@ describe('proxy reencryption', () => { }); const conditions = new ConditionSet([ genuineUndead, - Operator.OR(), + Operator.OR, gnomePals, ]); From 04e87729bf611a60106e08fafe7888a562dbb853 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 10 May 2023 20:12:11 +0200 Subject: [PATCH 15/98] test: accept custom params in predefined methods --- test/integration/conditions.test.ts | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 3083a996e..8866bb972 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -291,7 +291,7 @@ describe('condition context', () => { }, ], }; - const evmCondition = new Conditions.EvmCondition({ + const evmConditionObj = { chain: 5, functionAbi: fakeFunctionAbi, method: 'balanceOf', @@ -302,7 +302,8 @@ describe('condition context', () => { comparator: '==', value: USER_ADDRESS_PARAM, }, - }); + }; + const evmCondition = new Conditions.EvmCondition(evmConditionObj); const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); const conditionSet = new ConditionSet([evmCondition]); const conditionContext = new ConditionContext( @@ -310,10 +311,10 @@ describe('condition context', () => { web3Provider ); const myCustomParam = ':customParam'; + const customParams: Record = {}; + customParams[myCustomParam] = 1234; it('parses user-provided context parameters', async () => { - const customParams: Record = {}; - customParams[myCustomParam] = 1234; const asJson = await conditionContext .withCustomParams(customParams) .toJson(); @@ -336,6 +337,24 @@ describe('condition context', () => { `Cannot use reserved parameter name ${USER_ADDRESS_PARAM} as custom parameter` ); }); + + it('accepts custom parameters in predefined methods', async () => { + const customEvmCondition = new Conditions.EvmCondition({ + ...evmConditionObj, + parameters: [myCustomParam], + }); + const conditionSet = new ConditionSet([customEvmCondition]); + const conditionContext = new ConditionContext( + conditionSet.toWASMConditions(), + web3Provider + ); + + const asJson = await conditionContext + .withCustomParams(customParams) + .toJson(); + expect(asJson).toBeDefined(); + expect(asJson).toContain(myCustomParam); + }); }); }); From 6f4e6ffc30c383049db1c2f4cda540fbca828ef1 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 11 May 2023 09:04:47 +0200 Subject: [PATCH 16/98] refactor wallet auth into a seperate file --- src/conditions/condition-context.ts | 114 ++------------------------ src/conditions/providers.ts | 119 ++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 108 deletions(-) create mode 100644 src/conditions/providers.ts diff --git a/src/conditions/condition-context.ts b/src/conditions/condition-context.ts index 3e52e6f19..a2498d214 100644 --- a/src/conditions/condition-context.ts +++ b/src/conditions/condition-context.ts @@ -1,10 +1,10 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { utils as ethersUtils } from 'ethers/lib/ethers'; -import { Eip712TypedData, FormattedTypedData } from '../web3'; +import { Eip712TypedData } from '../web3'; import { SPECIAL_CONTEXT_PARAMS, USER_ADDRESS_PARAM } from './conditions'; +import { WalletAuthenticationProvider } from './providers'; interface TypedSignature { signature: string; @@ -15,7 +15,7 @@ interface TypedSignature { export type CustomContextParam = string | number | boolean; export class ConditionContext { - private walletSignature?: Record; + private readonly walletAuthProvider: WalletAuthenticationProvider; constructor( private readonly conditions: WASMConditions, @@ -29,116 +29,14 @@ export class ConditionContext { ); } }); - } - - public async getOrCreateWalletSignature(): Promise { - const address = await this.web3Provider.getSigner().getAddress(); - const storageKey = `wallet-signature-${address}`; - - // If we have a signature in localStorage, return it - const isLocalStorage = typeof localStorage !== 'undefined'; - if (isLocalStorage) { - const maybeSignature = localStorage.getItem(storageKey); - if (maybeSignature) { - return JSON.parse(maybeSignature); - } - } - - // If not, try returning from memory - const maybeSignature = this.walletSignature?.[address]; - if (maybeSignature) { - if (isLocalStorage) { - localStorage.setItem(storageKey, maybeSignature); - } - return JSON.parse(maybeSignature); - } - - // If at this point we didn't return, we need to create a new signature - const typedSignature = await this.createWalletSignature(); - - // Persist where you can - if (isLocalStorage) { - localStorage.setItem(storageKey, JSON.stringify(typedSignature)); - } - if (!this.walletSignature) { - this.walletSignature = {}; - } - this.walletSignature[address] = JSON.stringify(typedSignature); - return typedSignature; - } - - private async createWalletSignature(): Promise { - // Ensure freshness of the signature - const { blockNumber, blockHash, chainId } = await this.getChainData(); - const address = await this.web3Provider.getSigner().getAddress(); - const signatureText = `I'm the owner of address ${address} as of block number ${blockNumber}`; - const salt = ethersUtils.hexlify(ethersUtils.randomBytes(32)); - - const typedData: Eip712TypedData = { - types: { - Wallet: [ - { name: 'address', type: 'address' }, - { name: 'signatureText', type: 'string' }, - { name: 'blockNumber', type: 'uint256' }, - { name: 'blockHash', type: 'bytes32' }, - ], - }, - domain: { - name: 'tDec', - version: '1', - chainId, - salt, - }, - message: { - address, - signatureText, - blockNumber, - blockHash, - }, - }; - const signature = await this.web3Provider - .getSigner() - ._signTypedData(typedData.domain, typedData.types, typedData.message); - - const formattedTypedData: FormattedTypedData = { - ...typedData, - primaryType: 'Wallet', - types: { - ...typedData.types, - EIP712Domain: [ - { - name: 'name', - type: 'string', - }, - { - name: 'version', - type: 'string', - }, - { - name: 'chainId', - type: 'uint256', - }, - { - name: 'salt', - type: 'bytes32', - }, - ], - }, - }; - return { signature, typedData: formattedTypedData, address }; - } - - private async getChainData() { - const blockNumber = await this.web3Provider.getBlockNumber(); - const blockHash = (await this.web3Provider.getBlock(blockNumber)).hash; - const chainId = (await this.web3Provider.getNetwork()).chainId; - return { blockNumber, blockHash, chainId }; + this.walletAuthProvider = new WalletAuthenticationProvider(web3Provider); } public toJson = async (): Promise => { const payload: Record = {}; if (this.conditions.toString().includes(USER_ADDRESS_PARAM)) { - payload[USER_ADDRESS_PARAM] = await this.getOrCreateWalletSignature(); + payload[USER_ADDRESS_PARAM] = + await this.walletAuthProvider.getOrCreateWalletSignature(); } const conditions = JSON.parse(this.conditions.toString()); diff --git a/src/conditions/providers.ts b/src/conditions/providers.ts new file mode 100644 index 000000000..d7b72210f --- /dev/null +++ b/src/conditions/providers.ts @@ -0,0 +1,119 @@ +import { ethers } from 'ethers'; +import { utils as ethersUtils } from 'ethers/lib/ethers'; + +import { Eip712TypedData, FormattedTypedData } from '../web3'; + +interface TypedSignature { + signature: string; + typedData: Eip712TypedData; + address: string; +} +export class WalletAuthenticationProvider { + private walletSignature?: Record; + + constructor(private readonly web3Provider: ethers.providers.Web3Provider) {} + + public async getOrCreateWalletSignature(): Promise { + const address = await this.web3Provider.getSigner().getAddress(); + const storageKey = `wallet-signature-${address}`; + + // If we have a signature in localStorage, return it + const isLocalStorage = typeof localStorage !== 'undefined'; + if (isLocalStorage) { + const maybeSignature = localStorage.getItem(storageKey); + if (maybeSignature) { + return JSON.parse(maybeSignature); + } + } + + // If not, try returning from memory + const maybeSignature = this.walletSignature?.[address]; + if (maybeSignature) { + if (isLocalStorage) { + localStorage.setItem(storageKey, maybeSignature); + } + return JSON.parse(maybeSignature); + } + + // If at this point we didn't return, we need to create a new signature + const typedSignature = await this.createWalletSignature(); + + // Persist where you can + if (isLocalStorage) { + localStorage.setItem(storageKey, JSON.stringify(typedSignature)); + } + if (!this.walletSignature) { + this.walletSignature = {}; + } + this.walletSignature[address] = JSON.stringify(typedSignature); + return typedSignature; + } + + private async createWalletSignature(): Promise { + // Ensure freshness of the signature + const { blockNumber, blockHash, chainId } = await this.getChainData(); + const address = await this.web3Provider.getSigner().getAddress(); + const signatureText = `I'm the owner of address ${address} as of block number ${blockNumber}`; + const salt = ethersUtils.hexlify(ethersUtils.randomBytes(32)); + + const typedData: Eip712TypedData = { + types: { + Wallet: [ + { name: 'address', type: 'address' }, + { name: 'signatureText', type: 'string' }, + { name: 'blockNumber', type: 'uint256' }, + { name: 'blockHash', type: 'bytes32' }, + ], + }, + domain: { + name: 'tDec', + version: '1', + chainId, + salt, + }, + message: { + address, + signatureText, + blockNumber, + blockHash, + }, + }; + const signature = await this.web3Provider + .getSigner() + ._signTypedData(typedData.domain, typedData.types, typedData.message); + + const formattedTypedData: FormattedTypedData = { + ...typedData, + primaryType: 'Wallet', + types: { + ...typedData.types, + EIP712Domain: [ + { + name: 'name', + type: 'string', + }, + { + name: 'version', + type: 'string', + }, + { + name: 'chainId', + type: 'uint256', + }, + { + name: 'salt', + type: 'bytes32', + }, + ], + }, + }; + return { signature, typedData: formattedTypedData, address }; + } + + private async getChainData() { + const blockNumber = await this.web3Provider.getBlockNumber(); + const blockHash = (await this.web3Provider.getBlock(blockNumber)).hash; + const chainId = (await this.web3Provider.getNetwork()).chainId; + return { blockNumber, blockHash, chainId }; + } +} From 055a17f590c6583be1477b76def0442863306767 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 11 May 2023 09:10:47 +0200 Subject: [PATCH 17/98] refactor supported chains out of condition class --- src/agents/contracts.ts | 8 +++++++- src/conditions/conditions.ts | 23 ++++++++++++----------- src/types.ts | 1 + 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/agents/contracts.ts b/src/agents/contracts.ts index 92c0b12f1..19b6059d3 100644 --- a/src/agents/contracts.ts +++ b/src/agents/contracts.ts @@ -20,15 +20,21 @@ const GOERLI: Contracts = { COORDINATOR: '0x2cf19429168a0943992D8e7dE534E9b802C687B6', }; +const MAINNET: Contracts = { + SUBSCRIPTION_MANAGER: undefined, + COORDINATOR: 'undefined', +}; + const CONTRACTS: { readonly [key in ChainId]: Contracts } = { [ChainId.POLYGON]: POLYGON, [ChainId.MUMBAI]: MUMBAI, [ChainId.GOERLI]: GOERLI, + [ChainId.MAINNET]: MAINNET, }; export const getContract = ( chainId: number, - contract: keyof Contracts + contract: keyof Contracts, ): ChecksumAddress => { if (!Object.values(ChainId).includes(chainId)) { throw new Error(`No contracts found for chainId: ${chainId}`); diff --git a/src/conditions/conditions.ts b/src/conditions/conditions.ts index 1dcf5765d..b591f5c8d 100644 --- a/src/conditions/conditions.ts +++ b/src/conditions/conditions.ts @@ -1,7 +1,15 @@ import Joi, { ValidationError } from 'joi'; +import { ChainId } from '../types'; + +export const SUPPORTED_CHAINS = [ + ChainId.POLYGON, + ChainId.MUMBAI, + ChainId.GOERLI, + ChainId.MAINNET, +] export class Condition { - public static readonly COMPARATOR_OPERATORS = [ + public static readonly COMPARATORS = [ '==', '>', '<', @@ -10,13 +18,6 @@ export class Condition { '!=', ]; - public static readonly SUPPORTED_CHAINS = [ - 1, // ethereum/mainnet - 5, // ethereum/goerli - 137, // polygon/mainnet - 80001, // polygon/mumbai - ]; - public readonly schema = Joi.object(); public readonly defaults = {}; private validationError?: ValidationError; @@ -31,7 +32,7 @@ export class Condition { return Joi.object({ index: Joi.number().optional(), comparator: Joi.string() - .valid(...Condition.COMPARATOR_OPERATORS) + .valid(...Condition.COMPARATORS) .required(), value: Joi.alternatives(Joi.string(), Joi.number()).required(), }); @@ -136,7 +137,7 @@ class RpcCondition extends Condition { public readonly schema = Joi.object({ chain: Joi.number() - .valid(...Condition.SUPPORTED_CHAINS) + .valid(...SUPPORTED_CHAINS) .required(), method: Joi.string() .valid(...RpcCondition.RPC_METHODS) @@ -198,7 +199,7 @@ class EvmCondition extends Condition { .pattern(new RegExp('^0x[a-fA-F0-9]{40}$')) .required(), chain: Joi.string() - .valid(...Condition.SUPPORTED_CHAINS) + .valid(...SUPPORTED_CHAINS) .required(), standardContractType: Joi.string() .valid(...EvmCondition.STANDARD_CONTRACT_TYPES) diff --git a/src/types.ts b/src/types.ts index 5fedf560e..2440dcc53 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,4 +6,5 @@ export enum ChainId { POLYGON = 137, MUMBAI = 80001, GOERLI = 5, + MAINNET = 1, } From b02b0632652801facd1d7a94684717bfd4efae31 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 11 May 2023 09:32:28 +0200 Subject: [PATCH 18/98] replace tdec domain name in eip sig with cbd --- src/conditions/providers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conditions/providers.ts b/src/conditions/providers.ts index d7b72210f..907ba87ec 100644 --- a/src/conditions/providers.ts +++ b/src/conditions/providers.ts @@ -66,7 +66,7 @@ export class WalletAuthenticationProvider { ], }, domain: { - name: 'tDec', + name: 'cbd', version: '1', chainId, salt, From 5415ad8095c8b8a58776089bf750e77ed2acdfb3 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 11 May 2023 09:33:07 +0200 Subject: [PATCH 19/98] fix prettier --- src/agents/contracts.ts | 2 +- src/conditions/conditions.ts | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/agents/contracts.ts b/src/agents/contracts.ts index 19b6059d3..1af9f6aba 100644 --- a/src/agents/contracts.ts +++ b/src/agents/contracts.ts @@ -34,7 +34,7 @@ const CONTRACTS: { readonly [key in ChainId]: Contracts } = { export const getContract = ( chainId: number, - contract: keyof Contracts, + contract: keyof Contracts ): ChecksumAddress => { if (!Object.values(ChainId).includes(chainId)) { throw new Error(`No contracts found for chainId: ${chainId}`); diff --git a/src/conditions/conditions.ts b/src/conditions/conditions.ts index b591f5c8d..39e226688 100644 --- a/src/conditions/conditions.ts +++ b/src/conditions/conditions.ts @@ -7,16 +7,9 @@ export const SUPPORTED_CHAINS = [ ChainId.MUMBAI, ChainId.GOERLI, ChainId.MAINNET, -] +]; export class Condition { - public static readonly COMPARATORS = [ - '==', - '>', - '<', - '>=', - '<=', - '!=', - ]; + public static readonly COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; public readonly schema = Joi.object(); public readonly defaults = {}; From 63fabdb813b542fa227e168b4e51ef003ea7ba6c Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 11 May 2023 09:35:27 +0200 Subject: [PATCH 20/98] fix tests --- src/conditions/conditions.ts | 4 ++-- src/config.ts | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/conditions/conditions.ts b/src/conditions/conditions.ts index 39e226688..1985723d3 100644 --- a/src/conditions/conditions.ts +++ b/src/conditions/conditions.ts @@ -3,10 +3,10 @@ import Joi, { ValidationError } from 'joi'; import { ChainId } from '../types'; export const SUPPORTED_CHAINS = [ + ChainId.MAINNET, + ChainId.GOERLI, ChainId.POLYGON, ChainId.MUMBAI, - ChainId.GOERLI, - ChainId.MAINNET, ]; export class Condition { public static readonly COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; diff --git a/src/config.ts b/src/config.ts index 99bfb02cf..7835c14f1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -15,6 +15,10 @@ const CONFIGS: { readonly [key in ChainId]: Configuration } = { // TODO: Confirm this is correct porterUri: 'https://porter-tapir.nucypher.community', }, + [ChainId.MAINNET]: { + // TODO: Confirm this is correct + porterUri: 'https://porter.nucypher.community', + }, }; export const defaultConfiguration = (chainId: number): Configuration => { From 85ff684efe3e4a9b32d628e65606770459d96a7c Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 11 May 2023 11:48:55 +0200 Subject: [PATCH 21/98] feat!: refactor conditions into a module --- src/conditions/base/evm.ts | 70 ++++++++ src/conditions/base/index.ts | 3 + src/conditions/base/rpc.ts | 49 ++++++ src/conditions/base/timelock.ts | 16 ++ src/conditions/condition-context.ts | 4 +- src/conditions/condition-set.ts | 2 +- src/conditions/condition.ts | 75 +++++++++ src/conditions/conditions.ts | 242 ---------------------------- src/conditions/const.ts | 10 ++ src/conditions/index.ts | 9 ++ src/conditions/predefined/erc721.ts | 28 ++++ src/conditions/predefined/index.ts | 1 + src/index.ts | 33 ++-- test/docs/cbd.test.ts | 22 +-- test/integration/conditions.test.ts | 68 ++++---- test/integration/enrico.test.ts | 14 +- test/integration/pre.test.ts | 23 ++- test/unit/strategy.test.ts | 10 +- tsconfig.json | 1 + 19 files changed, 362 insertions(+), 318 deletions(-) create mode 100644 src/conditions/base/evm.ts create mode 100644 src/conditions/base/index.ts create mode 100644 src/conditions/base/rpc.ts create mode 100644 src/conditions/base/timelock.ts create mode 100644 src/conditions/condition.ts delete mode 100644 src/conditions/conditions.ts create mode 100644 src/conditions/const.ts create mode 100644 src/conditions/index.ts create mode 100644 src/conditions/predefined/erc721.ts create mode 100644 src/conditions/predefined/index.ts diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts new file mode 100644 index 000000000..ef6aa6bc3 --- /dev/null +++ b/src/conditions/base/evm.ts @@ -0,0 +1,70 @@ +import Joi from 'joi'; + +import { Condition } from '../condition'; +import { SUPPORTED_CHAINS, USER_ADDRESS_PARAM } from '../const'; + +// A helper method for making complex Joi types +// It says "allow these `types` when `parent` value is given" +const makeGuard = ( + schema: Joi.StringSchema | Joi.ArraySchema, + types: Record, + parent: string +) => { + Object.entries(types).forEach(([key, value]) => { + schema = schema.when(parent, { + is: key, + then: value, + }); + }); + return schema; +}; + +export class EvmCondition extends Condition { + public static readonly CONDITION_TYPE = 'evm'; + public static readonly STANDARD_CONTRACT_TYPES = [ + 'ERC20', + 'ERC721', + // 'ERC1155', // TODO(#131) + ]; + public static readonly METHODS_PER_CONTRACT_TYPE: Record = { + ERC20: ['balanceOf'], + ERC721: ['balanceOf', 'ownerOf'], + // ERC1155: ['balanceOf'], // TODO(#131) + }; + public static readonly PARAMETERS_PER_METHOD: Record = { + balanceOf: ['address'], + ownerOf: ['tokenId'], + }; + public static readonly CONTEXT_PARAMETERS_PER_METHOD: Record< + string, + string[] + > = { + balanceOf: [USER_ADDRESS_PARAM], + ownerOf: [USER_ADDRESS_PARAM], + }; + + private makeMethod = () => + makeGuard( + Joi.string(), + EvmCondition.METHODS_PER_CONTRACT_TYPE, + 'standardContractType' + ); + + public readonly schema = Joi.object({ + contractAddress: Joi.string() + .pattern(new RegExp('^0x[a-fA-F0-9]{40}$')) + .required(), + chain: Joi.string() + .valid(...SUPPORTED_CHAINS) + .required(), + standardContractType: Joi.string() + .valid(...EvmCondition.STANDARD_CONTRACT_TYPES) + .optional(), + functionAbi: Joi.object().optional(), + method: this.makeMethod().required(), + parameters: Joi.array().required(), + returnValueTest: this.makeReturnValueTest(), + }) + // At most one of these keys needs to be present + .xor('standardContractType', 'functionAbi'); +} diff --git a/src/conditions/base/index.ts b/src/conditions/base/index.ts new file mode 100644 index 000000000..0942d8b6a --- /dev/null +++ b/src/conditions/base/index.ts @@ -0,0 +1,3 @@ +export { EvmCondition } from './evm'; +export { RpcCondition } from './rpc'; +export { TimelockCondition } from './timelock'; diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts new file mode 100644 index 000000000..6aeffccf7 --- /dev/null +++ b/src/conditions/base/rpc.ts @@ -0,0 +1,49 @@ +import Joi from 'joi'; + +import { Condition } from '../condition'; +import { SUPPORTED_CHAINS, USER_ADDRESS_PARAM } from '../const'; + +export const RpcConditionConfig = { + CONDITION_TYPE: 'rpc', + RPC_METHODS: ['eth_getBalance', 'balanceOf'], + PARAMETERS_PER_METHOD: { + eth_getBalance: ['address'], + balanceOf: ['address'], + }, + CONTEXT_PARAMETERS_PER_METHOD: { + eth_getBalance: [USER_ADDRESS_PARAM], + balanceOf: [USER_ADDRESS_PARAM], + }, +}; + +export class RpcCondition extends Condition { + public readonly schema = Joi.object({ + chain: Joi.number() + .valid(...SUPPORTED_CHAINS) + .required(), + method: Joi.string() + .valid(...RpcConditionConfig.RPC_METHODS) + .required(), + parameters: Joi.array().required(), + returnValueTest: this.makeReturnValueTest(), + }); + + public getContextParameters = (): string[] => { + const asObject = this.toObj(); + + const method = asObject['method'] as string; + const parameters = (asObject['parameters'] ?? []) as string[]; + + const context: string[] = + RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[ + method as keyof typeof RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD + ]; + const returnValueTest = asObject['returnValueTest'] as Record< + string, + string + >; + + const maybeParams = [...(context ?? []), returnValueTest['value']]; + return parameters.filter((p) => maybeParams.includes(p)); + }; +} diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/timelock.ts new file mode 100644 index 000000000..a42e7431e --- /dev/null +++ b/src/conditions/base/timelock.ts @@ -0,0 +1,16 @@ +import Joi from 'joi'; + +import { Condition } from '../condition'; + +export class TimelockCondition extends Condition { + public static readonly CONDITION_TYPE = 'timelock'; + + defaults = { + method: 'timelock', + }; + + public readonly schema = Joi.object({ + returnValueTest: this.makeReturnValueTest(), + method: 'timelock', + }); +} diff --git a/src/conditions/condition-context.ts b/src/conditions/condition-context.ts index a2498d214..c63b3dc8a 100644 --- a/src/conditions/condition-context.ts +++ b/src/conditions/condition-context.ts @@ -3,7 +3,7 @@ import { ethers } from 'ethers'; import { Eip712TypedData } from '../web3'; -import { SPECIAL_CONTEXT_PARAMS, USER_ADDRESS_PARAM } from './conditions'; +import { USER_ADDRESS_PARAM } from './const'; import { WalletAuthenticationProvider } from './providers'; interface TypedSignature { @@ -14,6 +14,8 @@ interface TypedSignature { export type CustomContextParam = string | number | boolean; +const SPECIAL_CONTEXT_PARAMS = [USER_ADDRESS_PARAM]; + export class ConditionContext { private readonly walletAuthProvider: WalletAuthenticationProvider; diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index f8d21e966..8165f30a8 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -1,8 +1,8 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; +import { Condition } from './condition'; import { ConditionContext } from './condition-context'; -import { Condition } from './conditions'; import { Operator } from './operator'; export class ConditionSet { diff --git a/src/conditions/condition.ts b/src/conditions/condition.ts new file mode 100644 index 000000000..59b0c962c --- /dev/null +++ b/src/conditions/condition.ts @@ -0,0 +1,75 @@ +import Joi, { ValidationError } from 'joi'; + +export class Condition { + public static readonly COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; + + public readonly schema = Joi.object(); + public readonly defaults = {}; + private validationError?: ValidationError; + + constructor(private readonly value: Record = {}) {} + + public get error(): string | undefined { + return this.validationError?.message; + } + + protected makeReturnValueTest() { + return Joi.object({ + index: Joi.number().optional(), + comparator: Joi.string() + .valid(...Condition.COMPARATORS) + .required(), + value: Joi.alternatives(Joi.string(), Joi.number()).required(), + }); + } + + public toObj(): Record { + const { error, value } = this.validate(); + if (error) { + throw Error(error.message); + } + return value; + } + + public static fromObj(obj: Record) { + return new Condition(obj); + } + + public validate(data: Record = {}) { + const newValue = Object.assign(this.defaults, this.value, data); + return this.schema.validate(newValue); + } + + protected getContextParametersConfig(): Record { + // Empty configuration object by default, to be implemented by subclasses + return {}; + } + + public getContextParameters(): string[] { + const asObject = this.toObj(); + let paramsToCheck: string[] = []; + + const method = asObject['method'] as string; + if (method) { + const contextParams = this.getContextParametersConfig()[method]; + paramsToCheck = [...(contextParams ?? [])]; + } + + const returnValueTest = asObject['returnValueTest'] as Record< + string, + string + >; + if (returnValueTest) { + paramsToCheck.push(returnValueTest['value']); + } + + paramsToCheck = [ + ...paramsToCheck, + ...((asObject['parameters'] as string[]) ?? []), + ]; + const withoutDuplicates = new Set( + paramsToCheck.filter((p) => paramsToCheck.includes(p)) + ); + return [...withoutDuplicates]; + } +} diff --git a/src/conditions/conditions.ts b/src/conditions/conditions.ts deleted file mode 100644 index 1985723d3..000000000 --- a/src/conditions/conditions.ts +++ /dev/null @@ -1,242 +0,0 @@ -import Joi, { ValidationError } from 'joi'; - -import { ChainId } from '../types'; - -export const SUPPORTED_CHAINS = [ - ChainId.MAINNET, - ChainId.GOERLI, - ChainId.POLYGON, - ChainId.MUMBAI, -]; -export class Condition { - public static readonly COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; - - public readonly schema = Joi.object(); - public readonly defaults = {}; - private validationError?: ValidationError; - - constructor(private readonly value: Record = {}) {} - - public get error(): string | undefined { - return this.validationError?.message; - } - - protected makeReturnValueTest() { - return Joi.object({ - index: Joi.number().optional(), - comparator: Joi.string() - .valid(...Condition.COMPARATORS) - .required(), - value: Joi.alternatives(Joi.string(), Joi.number()).required(), - }); - } - - public toObj(): Record { - const { error, value } = this.validate(); - if (error) { - throw Error(error.message); - } - return value; - } - - public static fromObj(obj: Record) { - return new Condition(obj); - } - - public validate(data: Record = {}) { - const newValue = Object.assign(this.defaults, this.value, data); - return this.schema.validate(newValue); - } - - public getContextParameters(): string[] { - // Check all the places where context parameters may be hiding - const asObject = this.toObj(); - let paramsToCheck: string[] = []; - - // They may be hiding in the method parameters - const method = asObject['method'] as string; - if (method) { - const contextParams = RpcCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; - paramsToCheck = [...(contextParams ?? [])]; - } - - // Or in the ReturnValue test - const returnValueTest = asObject['returnValueTest'] as Record< - string, - string - >; - if (returnValueTest) { - paramsToCheck.push(returnValueTest['value']); - } - - // Merge & deduplicate found context parameters - paramsToCheck = [ - ...paramsToCheck, - ...((asObject['parameters'] as string[]) ?? []), - ]; - const withoutDuplicates = new Set( - paramsToCheck.filter((p) => paramsToCheck.includes(p)) - ); - return [...withoutDuplicates]; - } -} - -// A helper method for making complex Joi types -// It says "allow these `types` when `parent` value is given" -const makeGuard = ( - schema: Joi.StringSchema | Joi.ArraySchema, - types: Record, - parent: string -) => { - Object.entries(types).forEach(([key, value]) => { - schema = schema.when(parent, { - is: key, - then: value, - }); - }); - return schema; -}; - -export const USER_ADDRESS_PARAM = ':userAddress'; -export const SPECIAL_CONTEXT_PARAMS = [USER_ADDRESS_PARAM]; - -class TimelockCondition extends Condition { - public static readonly CONDITION_TYPE = 'timelock'; - - defaults = { - method: 'timelock', - }; - - public readonly schema = Joi.object({ - returnValueTest: this.makeReturnValueTest(), - method: 'timelock', - }); -} - -class RpcCondition extends Condition { - public static readonly CONDITION_TYPE = 'rpc'; - public static readonly RPC_METHODS = ['eth_getBalance', 'balanceOf']; - public static readonly PARAMETERS_PER_METHOD: Record = { - eth_getBalance: ['address'], - balanceOf: ['address'], - }; - public static readonly CONTEXT_PARAMETERS_PER_METHOD: Record< - string, - string[] - > = { - eth_getBalance: [USER_ADDRESS_PARAM], - balanceOf: [USER_ADDRESS_PARAM], - }; - - public readonly schema = Joi.object({ - chain: Joi.number() - .valid(...SUPPORTED_CHAINS) - .required(), - method: Joi.string() - .valid(...RpcCondition.RPC_METHODS) - .required(), - parameters: Joi.array().required(), - returnValueTest: this.makeReturnValueTest(), - }); - - public getContextParameters = (): string[] => { - const asObject = this.toObj(); - - const method = asObject['method'] as string; - const parameters = (asObject['parameters'] ?? []) as string[]; - - const context = RpcCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; - const returnValueTest = asObject['returnValueTest'] as Record< - string, - string - >; - - const maybeParams = [...(context ?? []), returnValueTest['value']]; - return parameters.filter((p) => maybeParams.includes(p)); - }; -} - -class EvmCondition extends Condition { - public static readonly CONDITION_TYPE = 'evm'; - public static readonly STANDARD_CONTRACT_TYPES = [ - 'ERC20', - 'ERC721', - // 'ERC1155', // TODO(#131) - ]; - public static readonly METHODS_PER_CONTRACT_TYPE: Record = { - ERC20: ['balanceOf'], - ERC721: ['balanceOf', 'ownerOf'], - // ERC1155: ['balanceOf'], // TODO(#131) - }; - public static readonly PARAMETERS_PER_METHOD: Record = { - balanceOf: ['address'], - ownerOf: ['tokenId'], - }; - public static readonly CONTEXT_PARAMETERS_PER_METHOD: Record< - string, - string[] - > = { - balanceOf: [USER_ADDRESS_PARAM], - ownerOf: [USER_ADDRESS_PARAM], - }; - - private makeMethod = () => - makeGuard( - Joi.string(), - EvmCondition.METHODS_PER_CONTRACT_TYPE, - 'standardContractType' - ); - - public readonly schema = Joi.object({ - contractAddress: Joi.string() - .pattern(new RegExp('^0x[a-fA-F0-9]{40}$')) - .required(), - chain: Joi.string() - .valid(...SUPPORTED_CHAINS) - .required(), - standardContractType: Joi.string() - .valid(...EvmCondition.STANDARD_CONTRACT_TYPES) - .optional(), - functionAbi: Joi.object().optional(), - method: this.makeMethod().required(), - parameters: Joi.array().required(), - returnValueTest: this.makeReturnValueTest(), - }) - // At most one of these keys needs to be present - .xor('standardContractType', 'functionAbi'); -} - -class ERC721Ownership extends EvmCondition { - readonly defaults = { - method: 'ownerOf', - parameters: [], - standardContractType: 'ERC721', - returnValueTest: { - index: 0, - comparator: '==', - value: USER_ADDRESS_PARAM, - }, - }; -} - -class ERC721Balance extends EvmCondition { - readonly defaults = { - method: 'balanceOf', - parameters: [USER_ADDRESS_PARAM], - standardContractType: 'ERC721', - returnValueTest: { - index: 0, - comparator: '>', - value: '0', - }, - }; -} - -export const Conditions = { - ERC721Ownership, - ERC721Balance, - EvmCondition, - TimelockCondition, - RpcCondition, - Condition, -}; diff --git a/src/conditions/const.ts b/src/conditions/const.ts new file mode 100644 index 000000000..f0d3f2ced --- /dev/null +++ b/src/conditions/const.ts @@ -0,0 +1,10 @@ +import { ChainId } from '../types'; + +export const SUPPORTED_CHAINS = [ + ChainId.MAINNET, + ChainId.GOERLI, + ChainId.POLYGON, + ChainId.MUMBAI, +]; + +export const USER_ADDRESS_PARAM = ':userAddress'; diff --git a/src/conditions/index.ts b/src/conditions/index.ts new file mode 100644 index 000000000..2e3759a57 --- /dev/null +++ b/src/conditions/index.ts @@ -0,0 +1,9 @@ +import * as base from './base'; +import * as predefined from './predefined'; + +export { predefined, base }; +export { Condition } from './condition'; +export { ConditionSet } from './condition-set'; +export { Operator } from './operator'; +export type { CustomContextParam } from './condition-context'; +export { ConditionContext } from './condition-context'; diff --git a/src/conditions/predefined/erc721.ts b/src/conditions/predefined/erc721.ts new file mode 100644 index 000000000..211a0b11b --- /dev/null +++ b/src/conditions/predefined/erc721.ts @@ -0,0 +1,28 @@ +import { EvmCondition } from '../base'; +import { USER_ADDRESS_PARAM } from '../const'; + +export class ERC721Ownership extends EvmCondition { + readonly defaults = { + method: 'ownerOf', + parameters: [], + standardContractType: 'ERC721', + returnValueTest: { + index: 0, + comparator: '==', + value: USER_ADDRESS_PARAM, + }, + }; +} + +export class ERC721Balance extends EvmCondition { + readonly defaults = { + method: 'balanceOf', + parameters: [USER_ADDRESS_PARAM], + standardContractType: 'ERC721', + returnValueTest: { + index: 0, + comparator: '>', + value: '0', + }, + }; +} diff --git a/src/conditions/predefined/index.ts b/src/conditions/predefined/index.ts new file mode 100644 index 000000000..2c08b2da4 --- /dev/null +++ b/src/conditions/predefined/index.ts @@ -0,0 +1 @@ +export { ERC721Balance, ERC721Ownership } from './erc721'; diff --git a/src/index.ts b/src/index.ts index 83dffc869..b187a9e8d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,28 +1,39 @@ +// Characters export { Alice } from './characters/alice'; export { Bob, RemoteBob } from './characters/bob'; export { Enrico } from './characters/enrico'; export { tDecDecrypter } from './characters/universal-bob'; -export { +export { Porter } from './characters/porter'; + +// Policies +export type { BlockchainPolicyParameters, EnactedPolicy, - PreEnactedPolicy, } from './policies/policy'; -export { Porter } from './characters/porter'; +export { PreEnactedPolicy } from './policies/policy'; + +// Keyring export { Keyring } from './keyring'; -export { Configuration, defaultConfiguration } from './config'; + +// Configuration +export type { Configuration } from './config'; +export { defaultConfiguration } from './config'; + +// Kits export { RevocationKit } from './kits/revocation'; export { PolicyMessageKit } from './kits/message'; -export { Conditions, Condition } from './conditions/conditions'; -export { Operator } from './conditions/operator'; -export { ConditionSet } from './conditions/condition-set'; -export { - ConditionContext, - CustomContextParam, -} from './conditions/condition-context'; +// Conditions +import type { CustomContextParam } from './conditions'; +import * as conditions from './conditions'; +// TODO: Not sure how to re-export this type from the conditions module +export { conditions, CustomContextParam }; + +// SDK export { Cohort } from './sdk/cohort'; export { DeployedStrategy, RevokedStrategy, Strategy } from './sdk/strategy'; +// Re-exports export { PublicKey, SecretKey, diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 630091eb1..a938959e3 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -1,13 +1,7 @@ import { MessageKit, VerifiedKeyFrag } from '@nucypher/nucypher-core'; import { providers } from 'ethers'; -import { - Cohort, - Conditions, - ConditionSet, - SecretKey, - Strategy, -} from '../../src'; +import { Cohort, conditions, SecretKey, Strategy } from '../../src'; import { Ursula } from '../../src/characters/porter'; import { toBytes } from '../../src/utils'; import { @@ -22,6 +16,12 @@ import { mockRetrieveCFragsRequest, } from '../utils'; +const { + predefined: { ERC721Ownership }, + ConditionSet, + Condition, +} = conditions; + describe('Get Started (CBD PoC)', () => { function mockRetrieveAndDecrypt( makeTreasureMapSpy: jest.SpyInstance, @@ -67,8 +67,8 @@ describe('Get Started (CBD PoC)', () => { }; const newCohort = await Cohort.create(config); - // 3. Specify default Conditions - const NFTOwnership = new Conditions.ERC721Ownership({ + // 3. Specify default predefined + const NFTOwnership = new ERC721Ownership({ contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', chain: 5, // Tapir network uses Görli testnet parameters: [5954], @@ -88,7 +88,7 @@ describe('Get Started (CBD PoC)', () => { const web3Provider = new providers.Web3Provider(MMprovider, mumbai); const newDeployed = await newStrategy.deploy('test', web3Provider); - // 5. Encrypt the plaintext & update Conditions + // 5. Encrypt the plaintext & update predefined const NFTBalanceConfig = { contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', standardContractType: 'ERC721', @@ -100,7 +100,7 @@ describe('Get Started (CBD PoC)', () => { value: 3, }, }; - const NFTBalance = new Conditions.Condition(NFTBalanceConfig); + const NFTBalance = new Condition(NFTBalanceConfig); const encrypter = newDeployed.encrypter; diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 141c8c2a0..9f6ad7a7a 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -1,15 +1,18 @@ import { SecretKey } from '@nucypher/nucypher-core'; -import { - ConditionContext, - Conditions, - ConditionSet, - Operator, -} from '../../src'; -import { CustomContextParam } from '../../src/conditions/condition-context'; -import { USER_ADDRESS_PARAM } from '../../src/conditions/conditions'; +import { conditions, CustomContextParam } from '../../src'; +import { RpcConditionConfig } from '../../src/conditions/base/rpc'; +import { USER_ADDRESS_PARAM } from '../../src/conditions/const'; import { fakeWeb3Provider } from '../utils'; +const { + predefined: { ERC721Balance }, + base: { TimelockCondition, RpcCondition, EvmCondition }, + Operator, + ConditionSet, + ConditionContext, +} = conditions; + const TEST_CONTRACT_ADDR = '0xC36442b4a4522E871399CD717aBDD847Ab11FE88'; const TEST_CONTRACT_ADDR_2 = '0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA'; const TEST_CHAIN_ID = 5; @@ -25,7 +28,7 @@ describe('operator', () => { }); describe('conditions schema', () => { - const condition = new Conditions.ERC721Balance(); + const condition = new ERC721Balance(); let result = condition.validate({ contractAddress: TEST_CONTRACT_ADDR, }); @@ -50,10 +53,10 @@ describe('conditions schema', () => { }); describe('condition set', () => { - const genuineUndead = new Conditions.ERC721Balance({ + const genuineUndead = new ERC721Balance({ contractAddress: TEST_CONTRACT_ADDR, }); - const gnomePals = new Conditions.ERC721Balance({ + const gnomePals = new ERC721Balance({ contractAddress: TEST_CONTRACT_ADDR_2, }); const conditions = new ConditionSet([genuineUndead, Operator.AND, gnomePals]); @@ -84,7 +87,7 @@ describe('standard conditions types validation', () => { describe('works for valid conditions', () => { it('timelock', () => { - const timelock = new Conditions.TimelockCondition({ + const timelock = new TimelockCondition({ returnValueTest, }); expect(timelock.toObj()).toEqual({ @@ -100,7 +103,7 @@ describe('standard conditions types validation', () => { parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], returnValueTest, }; - const rpc = new Conditions.RpcCondition(rpcCondition); + const rpc = new RpcCondition(rpcCondition); expect(rpc.toObj()).toEqual(rpcCondition); }); @@ -113,7 +116,7 @@ describe('standard conditions types validation', () => { parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], returnValueTest, }; - const evm = new Conditions.EvmCondition(evmCondition); + const evm = new EvmCondition(evmCondition); expect(evm.toObj()).toEqual(evmCondition); }); }); @@ -128,9 +131,7 @@ describe('standard conditions types validation', () => { value: '100', }, }; - const badTimelock = new Conditions.TimelockCondition( - badTimelockCondition - ); + const badTimelock = new TimelockCondition(badTimelockCondition); expect(() => badTimelock.toObj()).toThrow( '"returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' ); @@ -148,7 +149,7 @@ describe('standard conditions types validation', () => { parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], returnValueTest, }; - const badRpc = new Conditions.RpcCondition(badRpcCondition); + const badRpc = new RpcCondition(badRpcCondition); expect(() => badRpc.toObj()).toThrow( '"method" must be one of [eth_getBalance, balanceOf]' ); @@ -168,7 +169,7 @@ describe('standard conditions types validation', () => { parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], returnValueTest, }; - const badEvm = new Conditions.EvmCondition(badEvmCondition); + const badEvm = new EvmCondition(badEvmCondition); expect(() => badEvm.toObj()).toThrow('"contractAddress" is required'); const { error } = badEvm.validate(badEvmCondition); expect(error?.message).toEqual('"contractAddress" is required'); @@ -178,16 +179,18 @@ describe('standard conditions types validation', () => { describe('produce context parameters from conditions', () => { describe('from rpc condition', () => { - const methods = Conditions.RpcCondition.RPC_METHODS; + const methods = RpcConditionConfig.RPC_METHODS; methods.forEach((method) => { const contextParams = - Conditions.RpcCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; + RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[ + method as keyof typeof RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD + ]; if (!contextParams) { return; } contextParams.forEach((contextParam) => { it(`produces context parameter ${contextParam} for method ${method}`, () => { - const rpcCondition = new Conditions.RpcCondition({ + const rpcCondition = new RpcCondition({ chain: 5, method, parameters: [contextParam], @@ -205,21 +208,20 @@ describe('produce context parameters from conditions', () => { }); describe('from evm condition', () => { - Conditions.EvmCondition.STANDARD_CONTRACT_TYPES.forEach((contractType) => { - const methods = - Conditions.EvmCondition.METHODS_PER_CONTRACT_TYPE[contractType]; + EvmCondition.STANDARD_CONTRACT_TYPES.forEach((contractType) => { + const methods = EvmCondition.METHODS_PER_CONTRACT_TYPE[contractType]; if (!methods) { return; } methods.forEach((method) => { const contextParams = - Conditions.EvmCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; + EvmCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; if (!contextParams) { return; } contextParams.forEach((contextParam) => { it(`produces context parameter ${contextParam} for method ${method}`, () => { - const evmCondition = new Conditions.EvmCondition({ + const evmCondition = new EvmCondition({ contractAddress: '0x0000000000000000000000000000000000000000', chain: 5, standardContractType: 'ERC20', @@ -244,7 +246,7 @@ describe('condition context', () => { it('should serialize to JSON with context params', async () => { const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - const rpcCondition = new Conditions.RpcCondition({ + const rpcCondition = new RpcCondition({ chain: 5, method: 'eth_getBalance', parameters: [USER_ADDRESS_PARAM], @@ -285,7 +287,7 @@ describe('condition context', () => { }, ], }; - const evmCondition = new Conditions.EvmCondition({ + const evmCondition = new EvmCondition({ chain: 5, functionAbi: fakeFunctionAbi, method: 'balanceOf', @@ -351,13 +353,13 @@ describe('evm condition', () => { it('accepts standardContractType', () => { const conditionJson = { ...baseEvmCondition, standardContractType }; - const evmCondition = new Conditions.EvmCondition(conditionJson); + const evmCondition = new EvmCondition(conditionJson); expect(evmCondition.toObj()).toEqual(conditionJson); }); it('accepts functionAbi', () => { const conditionJson = { ...baseEvmCondition, functionAbi }; - const evmCondition = new Conditions.EvmCondition(conditionJson); + const evmCondition = new EvmCondition(conditionJson); expect(evmCondition.toObj()).toEqual(conditionJson); }); @@ -367,14 +369,14 @@ describe('evm condition', () => { standardContractType, functionAbi, }; - const evmCondition = new Conditions.EvmCondition(conditionJson); + const evmCondition = new EvmCondition(conditionJson); expect(() => evmCondition.toObj()).toThrow( '"value" contains a conflict between exclusive peers [standardContractType, functionAbi]' ); }); it('rejects none', () => { - const evmCondition = new Conditions.EvmCondition(baseEvmCondition); + const evmCondition = new EvmCondition(baseEvmCondition); expect(() => evmCondition.toObj()).toThrow( '"value" must contain at least one of [standardContractType, functionAbi]' ); diff --git a/test/integration/enrico.test.ts b/test/integration/enrico.test.ts index c45c2e452..58db0616a 100644 --- a/test/integration/enrico.test.ts +++ b/test/integration/enrico.test.ts @@ -1,5 +1,6 @@ +// Disabling because we want to access Alice.keyring which is a private property /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Conditions, ConditionSet, Enrico, PolicyMessageKit } from '../../src'; +import { conditions, Enrico, PolicyMessageKit } from '../../src'; import { RetrievalResult } from '../../src/kits/retrieval'; import { toBytes } from '../../src/utils'; import { @@ -10,6 +11,11 @@ import { reencryptKFrags, } from '../utils'; +const { + predefined: { ERC721Ownership }, + ConditionSet, +} = conditions; + describe('enrico', () => { it('alice decrypts message encrypted by enrico', async () => { const label = 'fake-label'; @@ -93,7 +99,7 @@ describe('enrico', () => { const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); - const ownsBufficornNFT = Conditions.ERC721Ownership.fromObj({ + const ownsBufficornNFT = ERC721Ownership.fromObj({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', parameters: [3591], }); @@ -116,13 +122,13 @@ describe('enrico', () => { const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); - const ownsBufficornNFT = new Conditions.ERC721Ownership({ + const ownsBufficornNFT = new ERC721Ownership({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', chain: 5, parameters: [3591], }); - const ownsNonsenseNFT = new Conditions.ERC721Ownership({ + const ownsNonsenseNFT = new ERC721Ownership({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', chain: 5, parameters: [6969], diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index 106814b19..3c6500d95 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -1,17 +1,16 @@ import { CapsuleFrag, reencrypt } from '@nucypher/nucypher-core'; -import { - Conditions, - ConditionSet, - Enrico, - MessageKit, - Operator, - PolicyMessageKit, -} from '../../src'; +import { conditions, Enrico, MessageKit, PolicyMessageKit } from '../../src'; import { RetrievalResult } from '../../src/kits/retrieval'; import { toBytes, zip } from '../../src/utils'; import { fakeAlice, fakeBob, fakeUrsulas, reencryptKFrags } from '../utils'; +const { + predefined: { ERC721Ownership }, + Operator, + ConditionSet, +} = conditions; + describe('proxy reencryption', () => { const plaintext = toBytes('plaintext-message'); const threshold = 2; @@ -85,21 +84,21 @@ describe('proxy reencryption', () => { const policyEncryptingKey = alice.getPolicyEncryptingKeyFromLabel(label); - const genuineUndead = new Conditions.ERC721Ownership({ + const genuineUndead = new ERC721Ownership({ contractAddress: '0x209e639a0EC166Ac7a1A4bA41968fa967dB30221', chain: 1, }); - const gnomePals = new Conditions.ERC721Ownership({ + const gnomePals = new ERC721Ownership({ contractAddress: '0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA', chain: 1, }); - const conditions = new ConditionSet([ + const conditionsSet = new ConditionSet([ genuineUndead, Operator.OR, gnomePals, ]); - const enrico = new Enrico(policyEncryptingKey, undefined, conditions); + const enrico = new Enrico(policyEncryptingKey, undefined, conditionsSet); const encryptedMessage = enrico.encryptMessage(plaintext); const ursulaAddresses = ursulas.map((ursula) => ursula.checksumAddress); diff --git a/test/unit/strategy.test.ts b/test/unit/strategy.test.ts index 5c49eeaa3..5881493c3 100644 --- a/test/unit/strategy.test.ts +++ b/test/unit/strategy.test.ts @@ -6,8 +6,7 @@ import { import { Cohort, - Conditions, - ConditionSet, + conditions, DeployedStrategy, Strategy, tDecDecrypter, @@ -34,6 +33,11 @@ import { strategyJSON, } from './testVariables'; +const { + predefined: { ERC721Ownership }, + ConditionSet, +} = conditions; + describe('Strategy', () => { const cohortConfig = { threshold: 2, @@ -195,7 +199,7 @@ describe('Deployed Strategy', () => { const encrypter = testDeployed.encrypter; const decrypter = testDeployed.decrypter; - const ownsNFT = new Conditions.ERC721Ownership({ + const ownsNFT = new ERC721Ownership({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', parameters: [3591], chain: 5, diff --git a/tsconfig.json b/tsconfig.json index 58621b182..07ea4fca0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, + "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ /* Additional Checks */ "noUnusedLocals": true /* Report errors on unused locals. */, From 5c09612f3f9e982a7ccf124ee90c90266617bb3e Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 11 May 2023 13:12:06 +0200 Subject: [PATCH 22/98] refactor conditions into subclasses with interfaces and a mixin --- src/characters/porter.ts | 2 +- src/conditions/base/evm.ts | 89 +++++++++++-------- src/conditions/base/rpc.ts | 74 +++++++++------ src/conditions/base/timelock.ts | 8 +- src/conditions/condition-set.ts | 14 +-- src/conditions/condition.ts | 84 ++++++++--------- src/conditions/const.ts | 2 + .../context.ts} | 19 ++-- src/conditions/context/index.ts | 2 + src/conditions/context/mixin.ts | 38 ++++++++ src/conditions/{ => context}/providers.ts | 11 ++- src/conditions/index.ts | 4 +- test/docs/cbd.test.ts | 4 +- test/integration/conditions.test.ts | 10 ++- test/integration/enrico.test.ts | 1 + 15 files changed, 219 insertions(+), 143 deletions(-) rename src/conditions/{condition-context.ts => context/context.ts} (84%) create mode 100644 src/conditions/context/index.ts create mode 100644 src/conditions/context/mixin.ts rename src/conditions/{ => context}/providers.ts (94%) diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 243262919..2aa1d7b1c 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -7,7 +7,7 @@ import { import axios, { AxiosResponse } from 'axios'; import qs from 'qs'; -import { ConditionContext } from '../conditions/condition-context'; +import { ConditionContext } from '../conditions'; import { Base64EncodedBytes, ChecksumAddress, HexEncodedBytes } from '../types'; import { fromBase64, fromHexString, toBase64, toHexString } from '../utils'; diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index ef6aa6bc3..a2e79fd53 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -1,11 +1,41 @@ import Joi from 'joi'; -import { Condition } from '../condition'; -import { SUPPORTED_CHAINS, USER_ADDRESS_PARAM } from '../const'; +import { Condition, makeReturnValueTest } from '../condition'; +import { + ETH_ADDRESS_REGEXP, + SUPPORTED_CHAINS, + USER_ADDRESS_PARAM, +} from '../const'; +import { ContextParametersHandlerMixin } from '../context/mixin'; + +export interface EvmConditionConfig { + CONDITION_TYPE: string; + STANDARD_CONTRACT_TYPES: string[]; + METHODS_PER_CONTRACT_TYPE: Record; + PARAMETERS_PER_METHOD: Record; + CONTEXT_PARAMETERS_PER_METHOD: Record; +} + +export const EvmConditionConfig: EvmConditionConfig = { + CONDITION_TYPE: 'evm', + STANDARD_CONTRACT_TYPES: ['ERC20', 'ERC721'], + METHODS_PER_CONTRACT_TYPE: { + ERC20: ['balanceOf'], + ERC721: ['balanceOf', 'ownerOf'], + }, + PARAMETERS_PER_METHOD: { + balanceOf: ['address'], + ownerOf: ['tokenId'], + }, + CONTEXT_PARAMETERS_PER_METHOD: { + balanceOf: [USER_ADDRESS_PARAM], + ownerOf: [USER_ADDRESS_PARAM], + }, +}; // A helper method for making complex Joi types // It says "allow these `types` when `parent` value is given" -const makeGuard = ( +const makeWhenGuard = ( schema: Joi.StringSchema | Joi.ArraySchema, types: Record, parent: string @@ -19,52 +49,33 @@ const makeGuard = ( return schema; }; -export class EvmCondition extends Condition { - public static readonly CONDITION_TYPE = 'evm'; - public static readonly STANDARD_CONTRACT_TYPES = [ - 'ERC20', - 'ERC721', - // 'ERC1155', // TODO(#131) - ]; - public static readonly METHODS_PER_CONTRACT_TYPE: Record = { - ERC20: ['balanceOf'], - ERC721: ['balanceOf', 'ownerOf'], - // ERC1155: ['balanceOf'], // TODO(#131) - }; - public static readonly PARAMETERS_PER_METHOD: Record = { - balanceOf: ['address'], - ownerOf: ['tokenId'], - }; - public static readonly CONTEXT_PARAMETERS_PER_METHOD: Record< - string, - string[] - > = { - balanceOf: [USER_ADDRESS_PARAM], - ownerOf: [USER_ADDRESS_PARAM], - }; - - private makeMethod = () => - makeGuard( - Joi.string(), - EvmCondition.METHODS_PER_CONTRACT_TYPE, - 'standardContractType' - ); +const makeMethod = () => + makeWhenGuard( + Joi.string(), + EvmConditionConfig.METHODS_PER_CONTRACT_TYPE, + 'standardContractType' + ); +export class EvmConditionBase extends Condition { public readonly schema = Joi.object({ - contractAddress: Joi.string() - .pattern(new RegExp('^0x[a-fA-F0-9]{40}$')) - .required(), + contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), chain: Joi.string() .valid(...SUPPORTED_CHAINS) .required(), standardContractType: Joi.string() - .valid(...EvmCondition.STANDARD_CONTRACT_TYPES) + .valid(...EvmConditionConfig.STANDARD_CONTRACT_TYPES) .optional(), functionAbi: Joi.object().optional(), - method: this.makeMethod().required(), + method: makeMethod().required(), parameters: Joi.array().required(), - returnValueTest: this.makeReturnValueTest(), + returnValueTest: makeReturnValueTest(), }) // At most one of these keys needs to be present .xor('standardContractType', 'functionAbi'); } + +export const EvmCondition = ContextParametersHandlerMixin(EvmConditionBase); + +Object.defineProperty(EvmCondition.prototype, 'getContextConfig', { + value: () => EvmConditionConfig.CONTEXT_PARAMETERS_PER_METHOD, +}); diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 6aeffccf7..9c417f29e 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -1,9 +1,21 @@ -import Joi from 'joi'; +import Joi, { Schema } from 'joi'; -import { Condition } from '../condition'; -import { SUPPORTED_CHAINS, USER_ADDRESS_PARAM } from '../const'; +import { Condition, makeReturnValueTest } from '../condition'; +import { + ETH_ADDRESS_REGEXP, + SUPPORTED_CHAINS, + USER_ADDRESS_PARAM, +} from '../const'; +import { ContextParametersHandlerMixin } from '../context/mixin'; -export const RpcConditionConfig = { +export interface RpcConditionConfig { + CONDITION_TYPE: string; + RPC_METHODS: string[]; + PARAMETERS_PER_METHOD: Record; + CONTEXT_PARAMETERS_PER_METHOD: Record; +} + +export const RpcConditionConfig: RpcConditionConfig = { CONDITION_TYPE: 'rpc', RPC_METHODS: ['eth_getBalance', 'balanceOf'], PARAMETERS_PER_METHOD: { @@ -16,7 +28,27 @@ export const RpcConditionConfig = { }, }; -export class RpcCondition extends Condition { +export const ethAddressOrUserAddress = Joi.alternatives().try( + Joi.string().pattern(ETH_ADDRESS_REGEXP), + USER_ADDRESS_PARAM +); + +export const getAddressSchema = () => + Joi.array().items(ethAddressOrUserAddress).required(); + +export const rpcMethodSchema = RpcConditionConfig.RPC_METHODS.reduce( + (acc, method) => { + if (RpcConditionConfig.PARAMETERS_PER_METHOD[method].includes('address')) { + acc[method] = getAddressSchema(); + } else { + acc[method] = Joi.array().items(Joi.string()).required(); + } + return acc; + }, + {} as Record +); + +export class RpcConditionBase extends Condition { public readonly schema = Joi.object({ chain: Joi.number() .valid(...SUPPORTED_CHAINS) @@ -24,26 +56,18 @@ export class RpcCondition extends Condition { method: Joi.string() .valid(...RpcConditionConfig.RPC_METHODS) .required(), - parameters: Joi.array().required(), - returnValueTest: this.makeReturnValueTest(), + parameters: Joi.when('method', { + switch: RpcConditionConfig.RPC_METHODS.map((method) => ({ + is: method, + then: rpcMethodSchema[method], + })), + }), + returnValueTest: makeReturnValueTest(), }); +} - public getContextParameters = (): string[] => { - const asObject = this.toObj(); - - const method = asObject['method'] as string; - const parameters = (asObject['parameters'] ?? []) as string[]; - - const context: string[] = - RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[ - method as keyof typeof RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD - ]; - const returnValueTest = asObject['returnValueTest'] as Record< - string, - string - >; +export const RpcCondition = ContextParametersHandlerMixin(RpcConditionBase); - const maybeParams = [...(context ?? []), returnValueTest['value']]; - return parameters.filter((p) => maybeParams.includes(p)); - }; -} +Object.defineProperty(RpcCondition.prototype, 'getContextConfig', { + value: () => RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD, +}); diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/timelock.ts index a42e7431e..d673c0c13 100644 --- a/src/conditions/base/timelock.ts +++ b/src/conditions/base/timelock.ts @@ -1,16 +1,14 @@ import Joi from 'joi'; -import { Condition } from '../condition'; +import { Condition, makeReturnValueTest } from '../condition'; export class TimelockCondition extends Condition { - public static readonly CONDITION_TYPE = 'timelock'; - defaults = { method: 'timelock', }; public readonly schema = Joi.object({ - returnValueTest: this.makeReturnValueTest(), - method: 'timelock', + returnValueTest: makeReturnValueTest(), + method: Joi.string().valid('timelock').required(), }); } diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index 8165f30a8..afc969889 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -2,13 +2,13 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { Condition } from './condition'; -import { ConditionContext } from './condition-context'; +import { ConditionContext } from './context'; import { Operator } from './operator'; +type ConditionOrOperator = Condition | Operator; + export class ConditionSet { - constructor( - public readonly conditions: ReadonlyArray - ) {} + constructor(public readonly conditions: ReadonlyArray) {} public validate() { if (this.conditions.length % 2 === 0) { @@ -16,12 +16,12 @@ export class ConditionSet { 'conditions must be odd length, ever other element being an operator' ); } - this.conditions.forEach((cnd: Condition | Operator, index) => { - if (index % 2 && cnd.constructor.name !== 'Operator') + this.conditions.forEach((cnd: ConditionOrOperator, index) => { + if (index % 2 && !(cnd instanceof Operator)) throw new Error( `${index} element must be an Operator; Got ${cnd.constructor.name}.` ); - if (!(index % 2) && cnd.constructor.name === 'Operator') + if (!(index % 2) && cnd instanceof Operator) throw new Error( `${index} element must be a Condition; Got ${cnd.constructor.name}.` ); diff --git a/src/conditions/condition.ts b/src/conditions/condition.ts index 59b0c962c..9fdd26498 100644 --- a/src/conditions/condition.ts +++ b/src/conditions/condition.ts @@ -1,8 +1,36 @@ import Joi, { ValidationError } from 'joi'; -export class Condition { - public static readonly COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; +export interface ConditionConfig { + COMPARATORS: string[]; +} + +export interface ReturnValueTestConfig { + index?: number; + comparator: string; + value: string | number; +} + +export const ConditionConfig: ConditionConfig = { + COMPARATORS: ['==', '>', '<', '>=', '<=', '!='], +}; + +export const makeReturnValueTest = + (): Joi.ObjectSchema => + Joi.object({ + index: Joi.number().optional(), + comparator: Joi.string() + .valid(...ConditionConfig.COMPARATORS) + .required(), + value: Joi.alternatives(Joi.string(), Joi.number()).required(), + }); +export interface ConditionInterface { + schema: Joi.ObjectSchema; + defaults: unknown; + toObj: () => Record; +} + +export class Condition implements ConditionInterface { public readonly schema = Joi.object(); public readonly defaults = {}; private validationError?: ValidationError; @@ -13,16 +41,6 @@ export class Condition { return this.validationError?.message; } - protected makeReturnValueTest() { - return Joi.object({ - index: Joi.number().optional(), - comparator: Joi.string() - .valid(...Condition.COMPARATORS) - .required(), - value: Joi.alternatives(Joi.string(), Joi.number()).required(), - }); - } - public toObj(): Record { const { error, value } = this.validate(); if (error) { @@ -31,45 +49,17 @@ export class Condition { return value; } - public static fromObj(obj: Record) { - return new Condition(obj); + public static fromObj( + // We disable the eslint rule here because we have to pass args to the constructor + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this: new (...args: any[]) => T, + obj: Record + ): T { + return new this(obj); } public validate(data: Record = {}) { const newValue = Object.assign(this.defaults, this.value, data); return this.schema.validate(newValue); } - - protected getContextParametersConfig(): Record { - // Empty configuration object by default, to be implemented by subclasses - return {}; - } - - public getContextParameters(): string[] { - const asObject = this.toObj(); - let paramsToCheck: string[] = []; - - const method = asObject['method'] as string; - if (method) { - const contextParams = this.getContextParametersConfig()[method]; - paramsToCheck = [...(contextParams ?? [])]; - } - - const returnValueTest = asObject['returnValueTest'] as Record< - string, - string - >; - if (returnValueTest) { - paramsToCheck.push(returnValueTest['value']); - } - - paramsToCheck = [ - ...paramsToCheck, - ...((asObject['parameters'] as string[]) ?? []), - ]; - const withoutDuplicates = new Set( - paramsToCheck.filter((p) => paramsToCheck.includes(p)) - ); - return [...withoutDuplicates]; - } } diff --git a/src/conditions/const.ts b/src/conditions/const.ts index f0d3f2ced..19f336649 100644 --- a/src/conditions/const.ts +++ b/src/conditions/const.ts @@ -8,3 +8,5 @@ export const SUPPORTED_CHAINS = [ ]; export const USER_ADDRESS_PARAM = ':userAddress'; + +export const ETH_ADDRESS_REGEXP = new RegExp('^0x[a-fA-F0-9]{40}$'); diff --git a/src/conditions/condition-context.ts b/src/conditions/context/context.ts similarity index 84% rename from src/conditions/condition-context.ts rename to src/conditions/context/context.ts index c63b3dc8a..27d26b76f 100644 --- a/src/conditions/condition-context.ts +++ b/src/conditions/context/context.ts @@ -1,9 +1,9 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { Eip712TypedData } from '../web3'; +import { Eip712TypedData } from '../../web3'; +import { USER_ADDRESS_PARAM } from '../const'; -import { USER_ADDRESS_PARAM } from './const'; import { WalletAuthenticationProvider } from './providers'; interface TypedSignature { @@ -41,21 +41,22 @@ export class ConditionContext { await this.walletAuthProvider.getOrCreateWalletSignature(); } - const conditions = JSON.parse(this.conditions.toString()); - conditions.forEach((cond: { parameters: string[] }) => { - cond.parameters.forEach((key) => { + const parsedConditions = JSON.parse(this.conditions.toString()); + for (const cond of parsedConditions) { + for (const key of cond.parameters) { if ( !(key in this.customParameters) && !SPECIAL_CONTEXT_PARAMS.includes(key) ) { throw new Error(`Missing custom context parameter ${key}`); } - }); - }); + } + } - Object.keys(this.customParameters).forEach((key) => { + for (const key in this.customParameters) { payload[key] = this.customParameters[key]; - }); + } + return JSON.stringify(payload); }; diff --git a/src/conditions/context/index.ts b/src/conditions/context/index.ts new file mode 100644 index 000000000..72a893ff3 --- /dev/null +++ b/src/conditions/context/index.ts @@ -0,0 +1,2 @@ +export { ConditionContext } from './context'; +export type { CustomContextParam } from './context'; diff --git a/src/conditions/context/mixin.ts b/src/conditions/context/mixin.ts new file mode 100644 index 000000000..350bcac0d --- /dev/null +++ b/src/conditions/context/mixin.ts @@ -0,0 +1,38 @@ +import { ConditionInterface } from '../condition'; + +// Disabling because mixin constructor requires any arguments +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Constructor = new ( + ...params: TParams +) => TResult; + +export function ContextParametersHandlerMixin< + TBase extends Constructor +>(Base: TBase) { + return class extends Base { + // Disabling because mixin constructor requires any arguments + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(...args: any[]) { + super(...args); + } + + public getContextParameters(): string[] { + const asObject = this.toObj(); + const method = asObject['method'] as string; + const parameters = (asObject['parameters'] ?? []) as string[]; + const context = this.getContextConfig()[method]; + + const returnValueTest = asObject['returnValueTest'] as Record< + string, + string + >; + const maybeParams = [...(context ?? []), returnValueTest['value']]; + return parameters.filter((p) => maybeParams.includes(p)); + } + + public getContextConfig(): Record { + // Empty configuration object by default + return {}; + } + }; +} diff --git a/src/conditions/providers.ts b/src/conditions/context/providers.ts similarity index 94% rename from src/conditions/providers.ts rename to src/conditions/context/providers.ts index 907ba87ec..1758fe323 100644 --- a/src/conditions/providers.ts +++ b/src/conditions/context/providers.ts @@ -1,13 +1,20 @@ import { ethers } from 'ethers'; import { utils as ethersUtils } from 'ethers/lib/ethers'; -import { Eip712TypedData, FormattedTypedData } from '../web3'; +import { Eip712TypedData, FormattedTypedData } from '../../web3'; interface TypedSignature { signature: string; typedData: Eip712TypedData; address: string; } + +interface ChainData { + blockHash: string; + chainId: number; + blockNumber: number; +} + export class WalletAuthenticationProvider { private walletSignature?: Record; @@ -110,7 +117,7 @@ export class WalletAuthenticationProvider { return { signature, typedData: formattedTypedData, address }; } - private async getChainData() { + private async getChainData(): Promise { const blockNumber = await this.web3Provider.getBlockNumber(); const blockHash = (await this.web3Provider.getBlock(blockNumber)).hash; const chainId = (await this.web3Provider.getNetwork()).chainId; diff --git a/src/conditions/index.ts b/src/conditions/index.ts index 2e3759a57..0c87df048 100644 --- a/src/conditions/index.ts +++ b/src/conditions/index.ts @@ -5,5 +5,5 @@ export { predefined, base }; export { Condition } from './condition'; export { ConditionSet } from './condition-set'; export { Operator } from './operator'; -export type { CustomContextParam } from './condition-context'; -export { ConditionContext } from './condition-context'; +export type { CustomContextParam } from './context'; +export { ConditionContext } from './context'; diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index a938959e3..fd5356138 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -67,7 +67,7 @@ describe('Get Started (CBD PoC)', () => { }; const newCohort = await Cohort.create(config); - // 3. Specify default predefined + // 3. Specify default conditions const NFTOwnership = new ERC721Ownership({ contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', chain: 5, // Tapir network uses Görli testnet @@ -88,7 +88,7 @@ describe('Get Started (CBD PoC)', () => { const web3Provider = new providers.Web3Provider(MMprovider, mumbai); const newDeployed = await newStrategy.deploy('test', web3Provider); - // 5. Encrypt the plaintext & update predefined + // 5. Encrypt the plaintext & update conditions const NFTBalanceConfig = { contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', standardContractType: 'ERC721', diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts index 9f6ad7a7a..6324dbe27 100644 --- a/test/integration/conditions.test.ts +++ b/test/integration/conditions.test.ts @@ -1,6 +1,7 @@ import { SecretKey } from '@nucypher/nucypher-core'; import { conditions, CustomContextParam } from '../../src'; +import { EvmConditionConfig } from '../../src/conditions/base/evm'; import { RpcConditionConfig } from '../../src/conditions/base/rpc'; import { USER_ADDRESS_PARAM } from '../../src/conditions/const'; import { fakeWeb3Provider } from '../utils'; @@ -183,7 +184,7 @@ describe('produce context parameters from conditions', () => { methods.forEach((method) => { const contextParams = RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[ - method as keyof typeof RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD + method as keyof RpcConditionConfig ]; if (!contextParams) { return; @@ -208,14 +209,15 @@ describe('produce context parameters from conditions', () => { }); describe('from evm condition', () => { - EvmCondition.STANDARD_CONTRACT_TYPES.forEach((contractType) => { - const methods = EvmCondition.METHODS_PER_CONTRACT_TYPE[contractType]; + EvmConditionConfig.STANDARD_CONTRACT_TYPES.forEach((contractType) => { + const methods = + EvmConditionConfig.METHODS_PER_CONTRACT_TYPE[contractType]; if (!methods) { return; } methods.forEach((method) => { const contextParams = - EvmCondition.CONTEXT_PARAMETERS_PER_METHOD[method]; + EvmConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[method]; if (!contextParams) { return; } diff --git a/test/integration/enrico.test.ts b/test/integration/enrico.test.ts index 58db0616a..66318607b 100644 --- a/test/integration/enrico.test.ts +++ b/test/integration/enrico.test.ts @@ -102,6 +102,7 @@ describe('enrico', () => { const ownsBufficornNFT = ERC721Ownership.fromObj({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', parameters: [3591], + chain: 5, }); const conditions = new ConditionSet([ownsBufficornNFT]); From d1b9818b0513f093094d10945fc943c1690b7bcb Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 11 May 2023 14:45:12 +0200 Subject: [PATCH 23/98] feat!: remove unused revoked strategy --- src/index.ts | 2 +- src/sdk/strategy.ts | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index 83dffc869..0e3c7f973 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,7 @@ export { } from './conditions/condition-context'; export { Cohort } from './sdk/cohort'; -export { DeployedStrategy, RevokedStrategy, Strategy } from './sdk/strategy'; +export { DeployedStrategy, Strategy } from './sdk/strategy'; export { PublicKey, diff --git a/src/sdk/strategy.ts b/src/sdk/strategy.ts index db713a304..5facc86d2 100644 --- a/src/sdk/strategy.ts +++ b/src/sdk/strategy.ts @@ -239,12 +239,3 @@ export class DeployedStrategy { }; } } - -export class RevokedStrategy { - constructor( - public label: string, - public policy: EnactedPolicy, - public encrypter: Enrico, - public decrypter: tDecDecrypter - ) {} -} From 41261843083b01a80587a601a6e068f576e8959c Mon Sep 17 00:00:00 2001 From: piotr-roslaniec <39299780+piotr-roslaniec@users.noreply.github.com> Date: Thu, 11 May 2023 17:23:01 +0200 Subject: [PATCH 24/98] Fix mainnet porter URL Co-authored-by: KPrasch --- src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 7835c14f1..1b59d0c4e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -17,7 +17,7 @@ const CONFIGS: { readonly [key in ChainId]: Configuration } = { }, [ChainId.MAINNET]: { // TODO: Confirm this is correct - porterUri: 'https://porter.nucypher.community', + porterUri: 'https://porter.nucypher.io/', }, }; From 7887d53c0695b12114c5a10deda8b78daafaeb4e Mon Sep 17 00:00:00 2001 From: piotr-roslaniec <39299780+piotr-roslaniec@users.noreply.github.com> Date: Thu, 11 May 2023 18:09:49 +0200 Subject: [PATCH 25/98] undefined is not a string --- src/agents/contracts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agents/contracts.ts b/src/agents/contracts.ts index 1af9f6aba..c6b022421 100644 --- a/src/agents/contracts.ts +++ b/src/agents/contracts.ts @@ -22,7 +22,7 @@ const GOERLI: Contracts = { const MAINNET: Contracts = { SUBSCRIPTION_MANAGER: undefined, - COORDINATOR: 'undefined', + COORDINATOR: undefined, }; const CONTRACTS: { readonly [key in ChainId]: Contracts } = { From 0e180dc7698e1de24bfccebfa7c3897d2092e4e0 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 12 May 2023 09:02:32 +0200 Subject: [PATCH 26/98] move condition to conditions/base --- src/conditions/{ => base}/condition.ts | 0 src/conditions/base/evm.ts | 3 ++- src/conditions/base/index.ts | 1 + src/conditions/base/rpc.ts | 3 ++- src/conditions/base/timelock.ts | 2 +- src/conditions/condition-set.ts | 2 +- src/conditions/context/mixin.ts | 2 +- src/conditions/index.ts | 2 +- 8 files changed, 9 insertions(+), 6 deletions(-) rename src/conditions/{ => base}/condition.ts (100%) diff --git a/src/conditions/condition.ts b/src/conditions/base/condition.ts similarity index 100% rename from src/conditions/condition.ts rename to src/conditions/base/condition.ts diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index a2e79fd53..f1b47eb3f 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -1,6 +1,5 @@ import Joi from 'joi'; -import { Condition, makeReturnValueTest } from '../condition'; import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS, @@ -8,6 +7,8 @@ import { } from '../const'; import { ContextParametersHandlerMixin } from '../context/mixin'; +import { Condition, makeReturnValueTest } from './condition'; + export interface EvmConditionConfig { CONDITION_TYPE: string; STANDARD_CONTRACT_TYPES: string[]; diff --git a/src/conditions/base/index.ts b/src/conditions/base/index.ts index 0942d8b6a..2857b9d63 100644 --- a/src/conditions/base/index.ts +++ b/src/conditions/base/index.ts @@ -1,3 +1,4 @@ +export { Condition } from './condition'; export { EvmCondition } from './evm'; export { RpcCondition } from './rpc'; export { TimelockCondition } from './timelock'; diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 9c417f29e..12dff4ab0 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -1,6 +1,5 @@ import Joi, { Schema } from 'joi'; -import { Condition, makeReturnValueTest } from '../condition'; import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS, @@ -8,6 +7,8 @@ import { } from '../const'; import { ContextParametersHandlerMixin } from '../context/mixin'; +import { Condition, makeReturnValueTest } from './condition'; + export interface RpcConditionConfig { CONDITION_TYPE: string; RPC_METHODS: string[]; diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/timelock.ts index d673c0c13..fdf0b3574 100644 --- a/src/conditions/base/timelock.ts +++ b/src/conditions/base/timelock.ts @@ -1,6 +1,6 @@ import Joi from 'joi'; -import { Condition, makeReturnValueTest } from '../condition'; +import { Condition, makeReturnValueTest } from './condition'; export class TimelockCondition extends Condition { defaults = { diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index afc969889..3ce490b43 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -1,7 +1,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { Condition } from './condition'; +import { Condition } from './base/condition'; import { ConditionContext } from './context'; import { Operator } from './operator'; diff --git a/src/conditions/context/mixin.ts b/src/conditions/context/mixin.ts index 350bcac0d..6d6d0ed66 100644 --- a/src/conditions/context/mixin.ts +++ b/src/conditions/context/mixin.ts @@ -1,4 +1,4 @@ -import { ConditionInterface } from '../condition'; +import { ConditionInterface } from '../base/condition'; // Disabling because mixin constructor requires any arguments // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/conditions/index.ts b/src/conditions/index.ts index 0c87df048..32a45fc46 100644 --- a/src/conditions/index.ts +++ b/src/conditions/index.ts @@ -2,7 +2,7 @@ import * as base from './base'; import * as predefined from './predefined'; export { predefined, base }; -export { Condition } from './condition'; +export { Condition } from './base/condition'; export { ConditionSet } from './condition-set'; export { Operator } from './operator'; export type { CustomContextParam } from './context'; From 2cee252727b6ae68b00bdeb6e37dd9f5fbaec8d7 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 15 May 2023 13:17:12 +0200 Subject: [PATCH 27/98] test: refactor test structure --- src/conditions/condition-set.ts | 17 +- src/conditions/operator.ts | 4 +- test/integration/conditions.test.ts | 406 -------------------- test/unit/conditions/base/condition.test.ts | 104 +++++ test/unit/conditions/base/evm.test.ts | 70 ++++ test/unit/conditions/base/rpc.test.ts | 27 ++ test/unit/conditions/base/timelock.test.ts | 39 ++ test/unit/conditions/condition-set.test.ts | 75 ++++ test/unit/conditions/context.test.ts | 152 ++++++++ test/unit/conditions/operator.test.ts | 14 + test/unit/testVariables.ts | 27 ++ 11 files changed, 522 insertions(+), 413 deletions(-) delete mode 100644 test/integration/conditions.test.ts create mode 100644 test/unit/conditions/base/condition.test.ts create mode 100644 test/unit/conditions/base/evm.test.ts create mode 100644 test/unit/conditions/base/rpc.test.ts create mode 100644 test/unit/conditions/base/timelock.test.ts create mode 100644 test/unit/conditions/condition-set.test.ts create mode 100644 test/unit/conditions/context.test.ts create mode 100644 test/unit/conditions/operator.test.ts diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index 3ce490b43..7864c6c6e 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -11,20 +11,25 @@ export class ConditionSet { constructor(public readonly conditions: ReadonlyArray) {} public validate() { + // Expects [Condition, Operator, Condition, Operator, ...], where the last element is a Condition + if (this.conditions.length % 2 === 0) { throw new Error( - 'conditions must be odd length, ever other element being an operator' + 'conditions must be odd length, every other element being an operator' ); } - this.conditions.forEach((cnd: ConditionOrOperator, index) => { - if (index % 2 && !(cnd instanceof Operator)) + + this.conditions.forEach((cndOrOp: ConditionOrOperator, index) => { + if (index % 2 && !(cndOrOp instanceof Operator)) { throw new Error( - `${index} element must be an Operator; Got ${cnd.constructor.name}.` + `index ${index} must be an Operator, got ${cndOrOp.constructor.name} instead` ); - if (!(index % 2) && cnd instanceof Operator) + } + if (!(index % 2) && cndOrOp instanceof Operator) { throw new Error( - `${index} element must be a Condition; Got ${cnd.constructor.name}.` + `index ${index} must be a Condition, got ${cndOrOp.constructor.name} instead` ); + } }); return true; } diff --git a/src/conditions/operator.ts b/src/conditions/operator.ts index e7fdca994..2ef39471c 100644 --- a/src/conditions/operator.ts +++ b/src/conditions/operator.ts @@ -5,7 +5,9 @@ export class Operator { public constructor(public readonly operator: string) { if (!Operator.LOGICAL_OPERATORS.includes(operator)) { - throw `"${operator}" is not a valid operator`; + throw `"${operator}" must be one of [${Operator.LOGICAL_OPERATORS.join( + ', ' + )}]`; } this.operator = operator; } diff --git a/test/integration/conditions.test.ts b/test/integration/conditions.test.ts deleted file mode 100644 index 224b23390..000000000 --- a/test/integration/conditions.test.ts +++ /dev/null @@ -1,406 +0,0 @@ -import { SecretKey } from '@nucypher/nucypher-core'; - -import { conditions, CustomContextParam } from '../../src'; -import { EvmConditionConfig } from '../../src/conditions/base/evm'; -import { RpcConditionConfig } from '../../src/conditions/base/rpc'; -import { USER_ADDRESS_PARAM } from '../../src/conditions/const'; -import { fakeWeb3Provider } from '../utils'; - -const { - predefined: { ERC721Balance }, - base: { TimelockCondition, RpcCondition, EvmCondition }, - Operator, - ConditionSet, - ConditionContext, -} = conditions; - -const TEST_CONTRACT_ADDR = '0xC36442b4a4522E871399CD717aBDD847Ab11FE88'; -const TEST_CONTRACT_ADDR_2 = '0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA'; -const TEST_CHAIN_ID = 5; - -describe('operator', () => { - it('should validate Operator', async () => { - const op = new Operator('or'); - expect(op.operator).toEqual('or'); - expect(() => { - new Operator('then'); - }).toThrow(); - }); -}); - -describe('conditions schema', () => { - const condition = new ERC721Balance(); - let result = condition.validate({ - contractAddress: TEST_CONTRACT_ADDR, - }); - - it('should validate', async () => { - expect(result.error).toEqual(undefined); - expect(result.value.contractAddress).toEqual(TEST_CONTRACT_ADDR); - }); - - result = condition.validate({ chain: TEST_CHAIN_ID }); - it('should update the value of "chain"', async () => { - expect(result.error).toEqual(undefined); - expect(result.value.chain).toEqual(TEST_CHAIN_ID); - }); - - it('should validate chain id', async () => { - result = condition.validate({ chain: 10 }); - expect(result.error?.message).toEqual( - '"chain" must be one of [1, 5, 137, 80001]' - ); - }); -}); - -describe('condition set', () => { - const genuineUndead = new ERC721Balance({ - contractAddress: TEST_CONTRACT_ADDR, - }); - const gnomePals = new ERC721Balance({ - contractAddress: TEST_CONTRACT_ADDR_2, - }); - const conditions = new ConditionSet([genuineUndead, Operator.AND, gnomePals]); - - it('should validate', async () => { - expect(conditions.validate()).toEqual(true); - }); -}); - -describe('conditions set to/from json', () => { - const json = `[{"chain":${TEST_CHAIN_ID},"method":"ownerOf","parameters":["3591"],"standardContractType":"ERC721","returnValueTest":{"comparator":"==","value":":userAddress"},"contractAddress":"${TEST_CONTRACT_ADDR}"}]`; - const conditionSet = ConditionSet.fromJSON(json); - - it('should be a ConditionSet', async () => { - expect(conditionSet.conditions[0].toObj().contractAddress).toEqual( - TEST_CONTRACT_ADDR - ); - expect(conditionSet.toJson()).toEqual(json); - }); -}); - -describe('standard conditions types validation', () => { - const returnValueTest = { - index: 0, - comparator: '>', - value: '100', - }; - - describe('works for valid conditions', () => { - it('timelock', () => { - const timelock = new TimelockCondition({ - returnValueTest, - }); - expect(timelock.toObj()).toEqual({ - returnValueTest, - method: 'timelock', - }); - }); - - it('rpc', () => { - const rpcCondition = { - chain: 5, - method: 'eth_getBalance', - parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], - returnValueTest, - }; - const rpc = new RpcCondition(rpcCondition); - expect(rpc.toObj()).toEqual(rpcCondition); - }); - - it('evm', () => { - const evmCondition = { - contractAddress: '0x0000000000000000000000000000000000000000', - chain: 5, - standardContractType: 'ERC20', - method: 'balanceOf', - parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], - returnValueTest, - }; - const evm = new EvmCondition(evmCondition); - expect(evm.toObj()).toEqual(evmCondition); - }); - }); - - describe('fails for invalid conditions', () => { - it('invalid timelock', () => { - const badTimelockCondition = { - // Intentionally replacing `returnValueTest` with an invalid test - returnValueTest: { - index: 0, - comparator: 'not-a-comparator', - value: '100', - }, - }; - const badTimelock = new TimelockCondition(badTimelockCondition); - expect(() => badTimelock.toObj()).toThrow( - '"returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' - ); - const { error } = badTimelock.validate(badTimelockCondition); - expect(error?.message).toEqual( - '"returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' - ); - }); - - it('invalid rpc', () => { - const badRpcCondition = { - chain: 5, - // Intentionally replacing `method` with an invalid method - method: 'fake_invalid_method', - parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], - returnValueTest, - }; - const badRpc = new RpcCondition(badRpcCondition); - expect(() => badRpc.toObj()).toThrow( - '"method" must be one of [eth_getBalance, balanceOf]' - ); - const { error } = badRpc.validate(badRpcCondition); - expect(error?.message).toEqual( - '"method" must be one of [eth_getBalance, balanceOf]' - ); - }); - - it('invalid evm', () => { - const badEvmCondition = { - // Intentionally removing `contractAddress` - // contractAddress: '0x0000000000000000000000000000000000000000', - chain: 5, - standardContractType: 'ERC20', - method: 'balanceOf', - parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], - returnValueTest, - }; - const badEvm = new EvmCondition(badEvmCondition); - expect(() => badEvm.toObj()).toThrow('"contractAddress" is required'); - const { error } = badEvm.validate(badEvmCondition); - expect(error?.message).toEqual('"contractAddress" is required'); - }); - }); -}); - -describe('produce context parameters from conditions', () => { - describe('from rpc condition', () => { - const methods = RpcConditionConfig.RPC_METHODS; - methods.forEach((method) => { - const contextParams = - RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[ - method as keyof RpcConditionConfig - ]; - if (!contextParams) { - return; - } - contextParams.forEach((contextParam) => { - it(`produces context parameter ${contextParam} for method ${method}`, () => { - const rpcCondition = new RpcCondition({ - chain: 5, - method, - parameters: [contextParam], - returnValueTest: { - index: 0, - comparator: '==', - value: contextParam, - }, - }); - const producedContextParam = rpcCondition.getContextParameters(); - expect(producedContextParam).toEqual([contextParam]); - }); - }); - }); - }); - - describe('from evm condition', () => { - EvmConditionConfig.STANDARD_CONTRACT_TYPES.forEach((contractType) => { - const methods = - EvmConditionConfig.METHODS_PER_CONTRACT_TYPE[contractType]; - if (!methods) { - return; - } - methods.forEach((method) => { - const contextParams = - EvmConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[method]; - if (!contextParams) { - return; - } - contextParams.forEach((contextParam) => { - it(`produces context parameter ${contextParam} for method ${method}`, () => { - const evmCondition = new EvmCondition({ - contractAddress: '0x0000000000000000000000000000000000000000', - chain: 5, - standardContractType: 'ERC20', - method: 'balanceOf', - parameters: [contextParam], - returnValueTest: { - index: 0, - comparator: '==', - value: contextParam, - }, - }); - const producedContextParam = evmCondition.getContextParameters(); - expect(producedContextParam).toEqual([contextParam]); - }); - }); - }); - }); - }); -}); - -describe('condition context', () => { - it('should serialize to JSON with context params', async () => { - const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - - const rpcCondition = new RpcCondition({ - chain: 5, - method: 'eth_getBalance', - parameters: [USER_ADDRESS_PARAM], - returnValueTest: { - index: 0, - comparator: '==', - value: USER_ADDRESS_PARAM, - }, - }); - const conditionSet = new ConditionSet([rpcCondition]); - - const conditionContext = new ConditionContext( - conditionSet.toWASMConditions(), - web3Provider - ); - const asJson = await conditionContext.toJson(); - expect(asJson).toContain(USER_ADDRESS_PARAM); - }); - - describe('supports user-defined parameters', () => { - const fakeFunctionAbi = { - name: 'myFunction', - type: 'function', - inputs: [ - { - name: 'account', - type: 'address', - }, - { - name: 'myCustomParam', - type: 'uint256', - }, - ], - outputs: [ - { - name: 'someValue', - type: 'uint256', - }, - ], - }; - const evmConditionObj = { - chain: 5, - functionAbi: fakeFunctionAbi, - method: 'balanceOf', - contractAddress: '0x0000000000000000000000000000000000000000', - parameters: [USER_ADDRESS_PARAM, ':customParam'], - returnValueTest: { - index: 0, - comparator: '==', - value: USER_ADDRESS_PARAM, - }, - }; - const evmCondition = new EvmCondition(evmConditionObj); - const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - const conditionSet = new ConditionSet([evmCondition]); - const conditionContext = new ConditionContext( - conditionSet.toWASMConditions(), - web3Provider - ); - const myCustomParam = ':customParam'; - const customParams: Record = {}; - customParams[myCustomParam] = 1234; - - it('parses user-provided context parameters', async () => { - const asJson = await conditionContext - .withCustomParams(customParams) - .toJson(); - expect(asJson).toBeDefined(); - expect(asJson).toContain(USER_ADDRESS_PARAM); - expect(asJson).toContain(myCustomParam); - }); - - it('throws on missing custom context param', async () => { - await expect(conditionContext.toJson()).rejects.toThrow( - `Missing custom context parameter ${myCustomParam}` - ); - }); - - it('throws on using reserved context parameter identifiers', () => { - const badCustomParams: Record = {}; - badCustomParams[USER_ADDRESS_PARAM] = 'this-will-throw'; - - expect(() => conditionContext.withCustomParams(badCustomParams)).toThrow( - `Cannot use reserved parameter name ${USER_ADDRESS_PARAM} as custom parameter` - ); - }); - - it('accepts custom parameters in predefined methods', async () => { - const customEvmCondition = new EvmCondition({ - ...evmConditionObj, - parameters: [myCustomParam], - }); - const conditionSet = new ConditionSet([customEvmCondition]); - const conditionContext = new ConditionContext( - conditionSet.toWASMConditions(), - web3Provider - ); - - const asJson = await conditionContext - .withCustomParams(customParams) - .toJson(); - expect(asJson).toBeDefined(); - expect(asJson).toContain(myCustomParam); - }); - }); -}); - -describe('evm condition', () => { - describe('accepts either standardContractType or functionAbi but not both or none', () => { - const baseEvmCondition = { - contractAddress: '0x0000000000000000000000000000000000000000', - chain: 5, - method: 'balanceOf', - parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], - returnValueTest: { - index: 0, - comparator: '==', - value: USER_ADDRESS_PARAM, - }, - }; - const standardContractType = 'ERC20'; - const functionAbi = { fake_function_abi: true }; - - it('accepts standardContractType', () => { - const conditionJson = { ...baseEvmCondition, standardContractType }; - const evmCondition = new EvmCondition(conditionJson); - expect(evmCondition.toObj()).toEqual(conditionJson); - }); - - it('accepts functionAbi', () => { - const conditionJson = { ...baseEvmCondition, functionAbi }; - const evmCondition = new EvmCondition(conditionJson); - expect(evmCondition.toObj()).toEqual(conditionJson); - }); - - it('rejects both', () => { - const conditionJson = { - ...baseEvmCondition, - standardContractType, - functionAbi, - }; - const evmCondition = new EvmCondition(conditionJson); - expect(() => evmCondition.toObj()).toThrow( - '"value" contains a conflict between exclusive peers [standardContractType, functionAbi]' - ); - }); - - it('rejects none', () => { - const evmCondition = new EvmCondition(baseEvmCondition); - expect(() => evmCondition.toObj()).toThrow( - '"value" must contain at least one of [standardContractType, functionAbi]' - ); - }); - }); -}); diff --git a/test/unit/conditions/base/condition.test.ts b/test/unit/conditions/base/condition.test.ts new file mode 100644 index 000000000..77a1dfba3 --- /dev/null +++ b/test/unit/conditions/base/condition.test.ts @@ -0,0 +1,104 @@ +import { + EvmCondition, + EvmConditionConfig, +} from '../../../../src/conditions/base/evm'; +import { + RpcCondition, + RpcConditionConfig, +} from '../../../../src/conditions/base/rpc'; +import { ERC721Balance } from '../../../../src/conditions/predefined'; +import { + TEST_CHAIN_ID, + TEST_CONTRACT_ADDR, + testEvmConditionObj, + testReturnValueTest, + testRpcConditionObj, +} from '../../testVariables'; + +describe('validation', () => { + const condition = new ERC721Balance(); + let result = condition.validate({ + contractAddress: TEST_CONTRACT_ADDR, + chain: TEST_CHAIN_ID, + }); + + it('accepts a correct schema', async () => { + expect(result.error).toBeUndefined(); + expect(result.value.contractAddress).toEqual(TEST_CONTRACT_ADDR); + }); + + it('updates on a valid schema value', async () => { + result = condition.validate({ chain: TEST_CHAIN_ID }); + expect(result.error).toBeUndefined(); + expect(result.value.chain).toEqual(TEST_CHAIN_ID); + }); + + it('rejects on an invalid schema value', async () => { + result = condition.validate({ chain: -1 }); + expect(result.error?.message).toEqual( + '"chain" must be one of [1, 5, 137, 80001]' + ); + }); +}); + +describe('get context parameters from conditions', () => { + describe('from rpc condition', () => { + const methods = RpcConditionConfig.RPC_METHODS; + methods.forEach((method) => { + const contextParams = + RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[ + method as keyof RpcConditionConfig + ]; + if (!contextParams) { + return; + } + contextParams.forEach((contextParam) => { + it(`gets ${contextParam} for method ${method}`, () => { + const rpcCondition = new RpcCondition({ + ...testRpcConditionObj, + method, + parameters: [contextParam], + returnValueTest: { + ...testReturnValueTest, + value: contextParam, + }, + }); + + const producedContextParam = rpcCondition.getContextParameters(); + expect(producedContextParam).toEqual([contextParam]); + }); + }); + }); + }); + + describe('from evm condition', () => { + EvmConditionConfig.STANDARD_CONTRACT_TYPES.forEach((contractType) => { + const methods = + EvmConditionConfig.METHODS_PER_CONTRACT_TYPE[contractType]; + if (!methods) { + return; + } + methods.forEach((method) => { + const contextParams = + EvmConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[method]; + if (!contextParams) { + return; + } + contextParams.forEach((contextParam) => { + it(`gets ${contextParam} for method ${method}`, () => { + const evmCondition = new EvmCondition({ + ...testEvmConditionObj, + parameters: [contextParam], + returnValueTest: { + ...testReturnValueTest, + value: contextParam, + }, + }); + const producedContextParam = evmCondition.getContextParameters(); + expect(producedContextParam).toEqual([contextParam]); + }); + }); + }); + }); + }); +}); diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts new file mode 100644 index 000000000..a417c9b6f --- /dev/null +++ b/test/unit/conditions/base/evm.test.ts @@ -0,0 +1,70 @@ +import { EvmCondition } from '../../../../src/conditions/base'; +import { testEvmConditionObj } from '../../testVariables'; + +describe('validation', () => { + it('accepts on a valid schema', () => { + const evm = new EvmCondition(testEvmConditionObj); + expect(evm.toObj()).toEqual(testEvmConditionObj); + }); + + it('rejects an invalid schema', () => { + const badEvmCondition = { + ...testEvmConditionObj, + // Intentionally removing `contractAddress` + contractAddress: undefined, + }; + const badEvm = new EvmCondition(badEvmCondition); + expect(() => badEvm.toObj()).toThrow('"contractAddress" is required'); + const { error } = badEvm.validate(badEvmCondition); + expect(error?.message).toEqual('"contractAddress" is required'); + }); +}); + +describe('accepts either standardContractType or functionAbi but not both or none', () => { + const standardContractType = 'ERC20'; + const functionAbi = { fake_function_abi: true }; + + it('accepts standardContractType', () => { + const conditionObj = { + ...testEvmConditionObj, + standardContractType, + functionAbi: undefined, + }; + const evmCondition = new EvmCondition(conditionObj); + expect(evmCondition.toObj()).toEqual(conditionObj); + }); + + it('accepts functionAbi', () => { + const conditionObj = { + ...testEvmConditionObj, + functionAbi, + standardContractType: undefined, + }; + const evmCondition = new EvmCondition(conditionObj); + expect(evmCondition.toObj()).toEqual(conditionObj); + }); + + it('rejects both', () => { + const conditionObj = { + ...testEvmConditionObj, + standardContractType, + functionAbi, + }; + const evmCondition = new EvmCondition(conditionObj); + expect(() => evmCondition.toObj()).toThrow( + '"value" contains a conflict between exclusive peers [standardContractType, functionAbi]' + ); + }); + + it('rejects none', () => { + const conditionObj = { + ...testEvmConditionObj, + standardContractType: undefined, + functionAbi: undefined, + }; + const evmCondition = new EvmCondition(conditionObj); + expect(() => evmCondition.toObj()).toThrow( + '"value" must contain at least one of [standardContractType, functionAbi]' + ); + }); +}); diff --git a/test/unit/conditions/base/rpc.test.ts b/test/unit/conditions/base/rpc.test.ts new file mode 100644 index 000000000..3b8f3a652 --- /dev/null +++ b/test/unit/conditions/base/rpc.test.ts @@ -0,0 +1,27 @@ +import { RpcCondition } from '../../../../src/conditions/base'; +import { testRpcConditionObj } from '../../testVariables'; + +describe('validation', () => { + it('accepts on a valid schema', () => { + const rpc = new RpcCondition(testRpcConditionObj); + expect(rpc.toObj()).toEqual(testRpcConditionObj); + }); + + it('rejects an invalid schema', () => { + const badRpcObj = { + ...testRpcConditionObj, + // Intentionally replacing `method` with an invalid method + method: 'fake_invalid_method', + }; + + const badRpc = new RpcCondition(badRpcObj); + expect(() => badRpc.toObj()).toThrow( + '"method" must be one of [eth_getBalance, balanceOf]' + ); + + const { error } = badRpc.validate(badRpcObj); + expect(error?.message).toEqual( + '"method" must be one of [eth_getBalance, balanceOf]' + ); + }); +}); diff --git a/test/unit/conditions/base/timelock.test.ts b/test/unit/conditions/base/timelock.test.ts new file mode 100644 index 000000000..5d04dc212 --- /dev/null +++ b/test/unit/conditions/base/timelock.test.ts @@ -0,0 +1,39 @@ +import { TimelockCondition } from '../../../../src/conditions/base'; + +describe('validation', () => { + const returnValueTest = { + index: 0, + comparator: '>', + value: '100', + }; + + it('accepts a valid schema', () => { + const timelock = new TimelockCondition({ + returnValueTest, + }); + expect(timelock.toObj()).toEqual({ + returnValueTest, + method: 'timelock', + }); + }); + + it('rejects an invalid schema', () => { + const badTimelockObj = { + // Intentionally replacing `returnValueTest` with an invalid test + returnValueTest: { + ...returnValueTest, + comparator: 'not-a-comparator', + }, + }; + + const badTimelock = new TimelockCondition(badTimelockObj); + expect(() => badTimelock.toObj()).toThrow( + '"returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' + ); + + const { error } = badTimelock.validate(badTimelockObj); + expect(error?.message).toEqual( + '"returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' + ); + }); +}); diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts new file mode 100644 index 000000000..8a9ec9be7 --- /dev/null +++ b/test/unit/conditions/condition-set.test.ts @@ -0,0 +1,75 @@ +import { Condition, ConditionSet, Operator } from '../../../src/conditions'; +import { ERC721Balance } from '../../../src/conditions/predefined'; +import { + TEST_CHAIN_ID, + TEST_CONTRACT_ADDR, + TEST_CONTRACT_ADDR_2, +} from '../testVariables'; + +describe('condition set', () => { + describe('validation', () => { + const cond1 = new ERC721Balance({ + contractAddress: TEST_CONTRACT_ADDR, + }); + const cond2 = new ERC721Balance({ + contractAddress: TEST_CONTRACT_ADDR_2, + }); + + it('validates on a correct set', async () => { + const validSets = [[cond1, Operator.AND, cond2], [cond1]].map( + (set) => new ConditionSet(set) + ); + + validSets.forEach((set) => { + expect(set.validate()).toBeTruthy(); + }); + }); + + it('throws on an invalid set', async () => { + const setWithInvalidLength = new ConditionSet([cond1, cond2]); + expect(() => setWithInvalidLength.validate()).toThrow( + 'conditions must be odd length, every other element being an operator' + ); + + const setWithOperatorInsteadOfComparator = new ConditionSet([ + cond1, + Operator.AND, + Operator.AND, + ]); + expect(() => setWithOperatorInsteadOfComparator.validate()).toThrow( + 'index 2 must be a Condition, got Operator instead' + ); + + const setWithConditionInsteadOfOperator = new ConditionSet([ + cond1, + cond2, + cond1, + ]); + expect(() => setWithConditionInsteadOfOperator.validate()).toThrow( + 'index 1 must be an Operator, got ERC721Balance instead' + ); + }); + }); + + describe('serialization', () => { + it('serializes to and from json', async () => { + const set = new ConditionSet([ + new ERC721Balance({ + chain: TEST_CHAIN_ID, + contractAddress: TEST_CONTRACT_ADDR, + }), + ]); + const setJson = set.toJson(); + expect(setJson).toBeDefined(); + expect(setJson).toContain('chain'); + expect(setJson).toContain(TEST_CHAIN_ID.toString()); + expect(setJson).toContain('contractAddress'); + expect(setJson).toContain(TEST_CONTRACT_ADDR.toString()); + + const setFromJson = ConditionSet.fromJSON(setJson); + expect(setFromJson).toBeDefined(); + expect(setFromJson.conditions.length).toEqual(1); + expect(setFromJson.conditions[0]).toBeInstanceOf(Condition); // TODO: This should arguably be an ERC721Balance + }); + }); +}); diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts new file mode 100644 index 000000000..d26775f2e --- /dev/null +++ b/test/unit/conditions/context.test.ts @@ -0,0 +1,152 @@ +import { SecretKey } from '@nucypher/nucypher-core'; + +import { CustomContextParam } from '../../../src'; +import { ConditionContext, ConditionSet } from '../../../src/conditions'; +import { EvmCondition, RpcCondition } from '../../../src/conditions/base'; +import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; +import { fakeWeb3Provider } from '../../utils'; +import { testEvmConditionObj, testRpcConditionObj } from '../testVariables'; + +describe('condition context', () => { + it('should serialize to JSON with context params', async () => { + const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + + const rpcCondition = new RpcCondition({ + ...testRpcConditionObj, + parameters: [USER_ADDRESS_PARAM], + returnValueTest: { + index: 0, + comparator: '==', + value: USER_ADDRESS_PARAM, + }, + }); + const conditionSet = new ConditionSet([rpcCondition]); + + const conditionContext = new ConditionContext( + conditionSet.toWASMConditions(), + web3Provider + ); + const asJson = await conditionContext.toJson(); + expect(asJson).toContain(USER_ADDRESS_PARAM); + }); + + describe('supports user-defined parameters', () => { + const evmConditionObj = { + ...testEvmConditionObj, + standardContractType: 'ERC20', + method: 'balanceOf', + parameters: [USER_ADDRESS_PARAM, ':customParam'], + returnValueTest: { + index: 0, + comparator: '==', + value: USER_ADDRESS_PARAM, + }, + }; + const evmCondition = new EvmCondition(evmConditionObj); + const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + const conditionSet = new ConditionSet([evmCondition]); + const conditionContext = new ConditionContext( + conditionSet.toWASMConditions(), + web3Provider + ); + const myCustomParam = ':customParam'; + const customParams: Record = {}; + customParams[myCustomParam] = 1234; + + it('accepts user-provided context parameters', async () => { + const asJson = await conditionContext + .withCustomParams(customParams) + .toJson(); + expect(asJson).toBeDefined(); + expect(asJson).toContain(USER_ADDRESS_PARAM); + expect(asJson).toContain(myCustomParam); + }); + + it('throws on missing custom context param', async () => { + await expect(conditionContext.toJson()).rejects.toThrow( + `Missing custom context parameter ${myCustomParam}` + ); + }); + + it('throws on using reserved context parameter identifiers', () => { + const badCustomParams: Record = {}; + badCustomParams[USER_ADDRESS_PARAM] = 'this-will-throw'; + + expect(() => conditionContext.withCustomParams(badCustomParams)).toThrow( + `Cannot use reserved parameter name ${USER_ADDRESS_PARAM} as custom parameter` + ); + }); + + it('accepts custom parameters in predefined methods', async () => { + const customEvmCondition = new EvmCondition({ + ...evmConditionObj, + parameters: [myCustomParam], + }); + const conditionSet = new ConditionSet([customEvmCondition]); + const conditionContext = new ConditionContext( + conditionSet.toWASMConditions(), + web3Provider + ); + + const asJson = await conditionContext + .withCustomParams(customParams) + .toJson(); + expect(asJson).toBeDefined(); + expect(asJson).toContain(myCustomParam); + }); + }); + + // TODO: Fix this test. Fails with '"method" must be [balanceOf]' + // describe('supports custom function abi', () => { + // const fakeFunctionAbi = { + // name: 'myFunction', + // type: 'function', + // inputs: [ + // { + // name: 'account', + // type: 'address', + // }, + // { + // name: 'myCustomParam', + // type: 'uint256', + // }, + // ], + // outputs: [ + // { + // name: 'someValue', + // type: 'uint256', + // }, + // ], + // }; + // const evmConditionObj = { + // ...testEvmConditionObj, + // functionAbi: fakeFunctionAbi, + // method: 'myFunction', + // parameters: [USER_ADDRESS_PARAM, ':customParam'], + // returnValueTest: { + // index: 0, + // comparator: '==', + // value: USER_ADDRESS_PARAM, + // }, + // }; + // const evmCondition = new EvmCondition(evmConditionObj); + // const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + // const conditionSet = new ConditionSet([evmCondition]); + // const conditionContext = new ConditionContext( + // conditionSet.toWASMConditions(), + // web3Provider + // ); + // const myCustomParam = ':customParam'; + // const customParams: Record = {}; + // customParams[myCustomParam] = 1234; + // + // it('accepts custom function abi', async () => { + // const asJson = await conditionContext + // .withCustomParams(customParams) + // .toJson(); + // expect(asJson).toBeDefined(); + // expect(asJson).toContain(USER_ADDRESS_PARAM); + // expect(asJson).toContain(myCustomParam); + // }); + // }); +}); diff --git a/test/unit/conditions/operator.test.ts b/test/unit/conditions/operator.test.ts new file mode 100644 index 000000000..c6c4d3ed6 --- /dev/null +++ b/test/unit/conditions/operator.test.ts @@ -0,0 +1,14 @@ +import { Operator } from '../../../src/conditions'; + +describe('validate', () => { + it('accepts a valid operator', () => { + const op = new Operator('or'); + expect(op.operator).toEqual('or'); + }); + + it('rejects an invalid operator', () => { + expect(() => new Operator('not-an-operator')).toThrow( + '"not-an-operator" must be one of [and, or]' + ); + }); +}); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index 34c1cc099..f577291ef 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -63,3 +63,30 @@ export const decrypterJSON = JSON.stringify({ 'base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj', bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', }); + +export const TEST_CONTRACT_ADDR = '0x0000000000000000000000000000000000000001'; +export const TEST_CONTRACT_ADDR_2 = + '0x0000000000000000000000000000000000000001'; +export const TEST_CHAIN_ID = 5; + +export const testReturnValueTest = { + index: 0, + comparator: '>', + value: '100', +}; + +export const testRpcConditionObj = { + chain: TEST_CHAIN_ID, + method: 'eth_getBalance', + parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], + returnValueTest: testReturnValueTest, +}; + +export const testEvmConditionObj = { + contractAddress: '0x0000000000000000000000000000000000000000', + chain: 5, + standardContractType: 'ERC20', + method: 'balanceOf', + parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], + returnValueTest: testReturnValueTest, +}; From d1e362e27c56e9206cc0f3445674f9666db8e567 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 15 May 2023 13:31:50 +0200 Subject: [PATCH 28/98] chore: add codecov to ci --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3e1a202b0..1ddfad165 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,6 +31,11 @@ jobs: - name: Check run: yarn run package-check + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true + build_examples: name: Build project examples From 68c69dac7e401cdafd3a96531168afc26f59c849 Mon Sep 17 00:00:00 2001 From: piotr-roslaniec <39299780+piotr-roslaniec@users.noreply.github.com> Date: Mon, 15 May 2023 15:46:16 +0200 Subject: [PATCH 29/98] test: fix bad contract address --- test/unit/testVariables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index f577291ef..f40efbc5c 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -66,7 +66,7 @@ export const decrypterJSON = JSON.stringify({ export const TEST_CONTRACT_ADDR = '0x0000000000000000000000000000000000000001'; export const TEST_CONTRACT_ADDR_2 = - '0x0000000000000000000000000000000000000001'; + '0x0000000000000000000000000000000000000002'; export const TEST_CHAIN_ID = 5; export const testReturnValueTest = { From 2dfebd64dedbaaf5f1e6df0c3574a81f52eee8ca Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 15 May 2023 20:04:38 +0200 Subject: [PATCH 30/98] fix context parameters --- src/conditions/base/condition.ts | 22 +-- src/conditions/base/evm.ts | 36 ++-- src/conditions/base/rpc.ts | 18 +- src/conditions/base/timelock.ts | 6 +- src/conditions/context/context.ts | 82 ++++++--- src/conditions/context/mixin.ts | 38 ---- src/conditions/context/providers.ts | 2 +- test/docs/cbd.test.ts | 4 +- test/unit/conditions/base/condition.test.ts | 86 +-------- test/unit/conditions/base/evm.test.ts | 92 ++++++++++ test/unit/conditions/context.test.ts | 188 ++++++++------------ test/unit/testVariables.ts | 21 +++ 12 files changed, 286 insertions(+), 309 deletions(-) delete mode 100644 src/conditions/context/mixin.ts diff --git a/src/conditions/base/condition.ts b/src/conditions/base/condition.ts index 9fdd26498..f8711fba3 100644 --- a/src/conditions/base/condition.ts +++ b/src/conditions/base/condition.ts @@ -1,8 +1,6 @@ import Joi, { ValidationError } from 'joi'; -export interface ConditionConfig { - COMPARATORS: string[]; -} +const COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; export interface ReturnValueTestConfig { index?: number; @@ -10,29 +8,21 @@ export interface ReturnValueTestConfig { value: string | number; } -export const ConditionConfig: ConditionConfig = { - COMPARATORS: ['==', '>', '<', '>=', '<=', '!='], -}; - export const makeReturnValueTest = (): Joi.ObjectSchema => Joi.object({ index: Joi.number().optional(), comparator: Joi.string() - .valid(...ConditionConfig.COMPARATORS) + .valid(...COMPARATORS) .required(), value: Joi.alternatives(Joi.string(), Joi.number()).required(), }); -export interface ConditionInterface { - schema: Joi.ObjectSchema; - defaults: unknown; - toObj: () => Record; -} - -export class Condition implements ConditionInterface { +// TODO: Consider turning this into an abstract class +export class Condition { + // No schema by default, i.e. no validation by default public readonly schema = Joi.object(); - public readonly defaults = {}; + public readonly defaults: Record = {}; private validationError?: ValidationError; constructor(private readonly value: Record = {}) {} diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index f1b47eb3f..c46659fc0 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -1,37 +1,29 @@ import Joi from 'joi'; -import { - ETH_ADDRESS_REGEXP, - SUPPORTED_CHAINS, - USER_ADDRESS_PARAM, -} from '../const'; -import { ContextParametersHandlerMixin } from '../context/mixin'; +import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; -import { Condition, makeReturnValueTest } from './condition'; +import { makeReturnValueTest } from './condition'; +import { RpcCondition } from './rpc'; export interface EvmConditionConfig { CONDITION_TYPE: string; STANDARD_CONTRACT_TYPES: string[]; METHODS_PER_CONTRACT_TYPE: Record; PARAMETERS_PER_METHOD: Record; - CONTEXT_PARAMETERS_PER_METHOD: Record; } +const METHODS_PER_CONTRACT_TYPE = { + ERC20: ['balanceOf'], + ERC721: ['balanceOf', 'ownerOf'], +}; export const EvmConditionConfig: EvmConditionConfig = { - CONDITION_TYPE: 'evm', - STANDARD_CONTRACT_TYPES: ['ERC20', 'ERC721'], - METHODS_PER_CONTRACT_TYPE: { - ERC20: ['balanceOf'], - ERC721: ['balanceOf', 'ownerOf'], - }, + CONDITION_TYPE: 'evm', // TODO: How is this used? Similar to the Timelock.defaults.method? + STANDARD_CONTRACT_TYPES: Object.keys(METHODS_PER_CONTRACT_TYPE), + METHODS_PER_CONTRACT_TYPE, PARAMETERS_PER_METHOD: { balanceOf: ['address'], ownerOf: ['tokenId'], }, - CONTEXT_PARAMETERS_PER_METHOD: { - balanceOf: [USER_ADDRESS_PARAM], - ownerOf: [USER_ADDRESS_PARAM], - }, }; // A helper method for making complex Joi types @@ -57,7 +49,7 @@ const makeMethod = () => 'standardContractType' ); -export class EvmConditionBase extends Condition { +export class EvmCondition extends RpcCondition { public readonly schema = Joi.object({ contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), chain: Joi.string() @@ -74,9 +66,3 @@ export class EvmConditionBase extends Condition { // At most one of these keys needs to be present .xor('standardContractType', 'functionAbi'); } - -export const EvmCondition = ContextParametersHandlerMixin(EvmConditionBase); - -Object.defineProperty(EvmCondition.prototype, 'getContextConfig', { - value: () => EvmConditionConfig.CONTEXT_PARAMETERS_PER_METHOD, -}); diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 12dff4ab0..4cf9a3e6c 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -5,7 +5,6 @@ import { SUPPORTED_CHAINS, USER_ADDRESS_PARAM, } from '../const'; -import { ContextParametersHandlerMixin } from '../context/mixin'; import { Condition, makeReturnValueTest } from './condition'; @@ -13,7 +12,6 @@ export interface RpcConditionConfig { CONDITION_TYPE: string; RPC_METHODS: string[]; PARAMETERS_PER_METHOD: Record; - CONTEXT_PARAMETERS_PER_METHOD: Record; } export const RpcConditionConfig: RpcConditionConfig = { @@ -23,15 +21,11 @@ export const RpcConditionConfig: RpcConditionConfig = { eth_getBalance: ['address'], balanceOf: ['address'], }, - CONTEXT_PARAMETERS_PER_METHOD: { - eth_getBalance: [USER_ADDRESS_PARAM], - balanceOf: [USER_ADDRESS_PARAM], - }, }; export const ethAddressOrUserAddress = Joi.alternatives().try( Joi.string().pattern(ETH_ADDRESS_REGEXP), - USER_ADDRESS_PARAM + USER_ADDRESS_PARAM, ); export const getAddressSchema = () => @@ -46,10 +40,10 @@ export const rpcMethodSchema = RpcConditionConfig.RPC_METHODS.reduce( } return acc; }, - {} as Record + {} as Record, ); -export class RpcConditionBase extends Condition { +export class RpcCondition extends Condition { public readonly schema = Joi.object({ chain: Joi.number() .valid(...SUPPORTED_CHAINS) @@ -66,9 +60,3 @@ export class RpcConditionBase extends Condition { returnValueTest: makeReturnValueTest(), }); } - -export const RpcCondition = ContextParametersHandlerMixin(RpcConditionBase); - -Object.defineProperty(RpcCondition.prototype, 'getContextConfig', { - value: () => RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD, -}); diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/timelock.ts index fdf0b3574..ac00f6089 100644 --- a/src/conditions/base/timelock.ts +++ b/src/conditions/base/timelock.ts @@ -3,12 +3,16 @@ import Joi from 'joi'; import { Condition, makeReturnValueTest } from './condition'; export class TimelockCondition extends Condition { + // TODO: This is the only condition that uses defaults, and also the only condition that uses `method` in order + // to determine the schema. I.e. the only method that used `METHOD = 'timelock'` in `nucypher/nucypher`. + // TODO: Consider introducing a different field for this, e.g. `conditionType` or `type`. Use this field in a + // condition factory. defaults = { method: 'timelock', }; public readonly schema = Joi.object({ + method: Joi.string().valid(this.defaults.method).required(), returnValueTest: makeReturnValueTest(), - method: Joi.string().valid('timelock').required(), }); } diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index 27d26b76f..95949962a 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -1,31 +1,36 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { Eip712TypedData } from '../../web3'; import { USER_ADDRESS_PARAM } from '../const'; -import { WalletAuthenticationProvider } from './providers'; - -interface TypedSignature { - signature: string; - typedData: Eip712TypedData; - address: string; -} +import { TypedSignature, WalletAuthenticationProvider } from './providers'; export type CustomContextParam = string | number | boolean; +export type ContextParam = CustomContextParam | TypedSignature; -const SPECIAL_CONTEXT_PARAMS = [USER_ADDRESS_PARAM]; +const CONTEXT_PARAMS_HANDLERS = { + [USER_ADDRESS_PARAM]: async ( + web3Provider: ethers.providers.Web3Provider + ): Promise => { + const provider = new WalletAuthenticationProvider(web3Provider); + return provider.getOrCreateWalletSignature(); + }, +}; +export const RESERVED_CONTEXT_PARAMS = Object.keys(CONTEXT_PARAMS_HANDLERS); +export const CONTEXT_PARAM_PREFIX = ':'; export class ConditionContext { private readonly walletAuthProvider: WalletAuthenticationProvider; constructor( private readonly conditions: WASMConditions, + // TODO: We don't always need a web3 provider, only in cases where some specific context parameters are used + // TODO: Consider making this optional or introducing a different pattern to handle that private readonly web3Provider: ethers.providers.Web3Provider, public readonly customParameters: Record = {} ) { Object.keys(customParameters).forEach((key) => { - if (SPECIAL_CONTEXT_PARAMS.includes(key)) { + if (RESERVED_CONTEXT_PARAMS.includes(key)) { throw new Error( `Cannot use reserved parameter name ${key} as custom parameter` ); @@ -34,30 +39,63 @@ export class ConditionContext { this.walletAuthProvider = new WalletAuthenticationProvider(web3Provider); } - public toJson = async (): Promise => { - const payload: Record = {}; - if (this.conditions.toString().includes(USER_ADDRESS_PARAM)) { - payload[USER_ADDRESS_PARAM] = - await this.walletAuthProvider.getOrCreateWalletSignature(); - } + public toObj = async (): Promise> => { + // First, we want to find all the parameters we need to add + const requestedParameters = new Set(); + // Search conditions for parameters const parsedConditions = JSON.parse(this.conditions.toString()); for (const cond of parsedConditions) { - for (const key of cond.parameters) { + // Check return value test + const rvt = cond.returnValueTest.value; + if (typeof rvt === 'string' && rvt.startsWith(CONTEXT_PARAM_PREFIX)) { + requestedParameters.add(rvt); + } + + // Check condition parameters + for (const param of cond.parameters ?? []) { if ( - !(key in this.customParameters) && - !SPECIAL_CONTEXT_PARAMS.includes(key) + typeof param === 'string' && + param.startsWith(CONTEXT_PARAM_PREFIX) ) { - throw new Error(`Missing custom context parameter ${key}`); + requestedParameters.add(param); } } } + // Now, we can safely add all the parameters + const parameters: Record = {}; + + // Fill in predefined context parameters + if (requestedParameters.has(USER_ADDRESS_PARAM)) { + parameters[USER_ADDRESS_PARAM] = + await this.walletAuthProvider.getOrCreateWalletSignature(); + // Remove from requested parameters + requestedParameters.delete(USER_ADDRESS_PARAM); + } + + // Fill in custom parameters for (const key in this.customParameters) { - payload[key] = this.customParameters[key]; + parameters[key] = this.customParameters[key]; + } + + // Ok, so at this point we should have all the parameters we need + // If we don't, we have a problem and we should throw + const missingParameters = Array.from(requestedParameters).filter( + (key) => !parameters[key] + ); + if (missingParameters.length > 0) { + throw new Error( + `Missing custom context parameter(s): ${missingParameters.join(', ')}` + ); } - return JSON.stringify(payload); + return parameters; + }; + + public toJson = async (): Promise => { + const parameters = await this.toObj(); + return JSON.stringify(parameters); }; public withCustomParams = ( diff --git a/src/conditions/context/mixin.ts b/src/conditions/context/mixin.ts deleted file mode 100644 index 6d6d0ed66..000000000 --- a/src/conditions/context/mixin.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ConditionInterface } from '../base/condition'; - -// Disabling because mixin constructor requires any arguments -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Constructor = new ( - ...params: TParams -) => TResult; - -export function ContextParametersHandlerMixin< - TBase extends Constructor ->(Base: TBase) { - return class extends Base { - // Disabling because mixin constructor requires any arguments - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(...args: any[]) { - super(...args); - } - - public getContextParameters(): string[] { - const asObject = this.toObj(); - const method = asObject['method'] as string; - const parameters = (asObject['parameters'] ?? []) as string[]; - const context = this.getContextConfig()[method]; - - const returnValueTest = asObject['returnValueTest'] as Record< - string, - string - >; - const maybeParams = [...(context ?? []), returnValueTest['value']]; - return parameters.filter((p) => maybeParams.includes(p)); - } - - public getContextConfig(): Record { - // Empty configuration object by default - return {}; - } - }; -} diff --git a/src/conditions/context/providers.ts b/src/conditions/context/providers.ts index 1758fe323..9b8db518e 100644 --- a/src/conditions/context/providers.ts +++ b/src/conditions/context/providers.ts @@ -3,7 +3,7 @@ import { utils as ethersUtils } from 'ethers/lib/ethers'; import { Eip712TypedData, FormattedTypedData } from '../../web3'; -interface TypedSignature { +export interface TypedSignature { signature: string; typedData: Eip712TypedData; address: string; diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index fd5356138..f444720af 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -3,6 +3,7 @@ import { providers } from 'ethers'; import { Cohort, conditions, SecretKey, Strategy } from '../../src'; import { Ursula } from '../../src/characters/porter'; +import { EvmCondition } from '../../src/conditions/base'; import { toBytes } from '../../src/utils'; import { fakeUrsulas, @@ -19,7 +20,6 @@ import { const { predefined: { ERC721Ownership }, ConditionSet, - Condition, } = conditions; describe('Get Started (CBD PoC)', () => { @@ -100,7 +100,7 @@ describe('Get Started (CBD PoC)', () => { value: 3, }, }; - const NFTBalance = new Condition(NFTBalanceConfig); + const NFTBalance = new EvmCondition(NFTBalanceConfig); const encrypter = newDeployed.encrypter; diff --git a/test/unit/conditions/base/condition.test.ts b/test/unit/conditions/base/condition.test.ts index 77a1dfba3..af25c8dd8 100644 --- a/test/unit/conditions/base/condition.test.ts +++ b/test/unit/conditions/base/condition.test.ts @@ -1,21 +1,10 @@ -import { - EvmCondition, - EvmConditionConfig, -} from '../../../../src/conditions/base/evm'; -import { - RpcCondition, - RpcConditionConfig, -} from '../../../../src/conditions/base/rpc'; import { ERC721Balance } from '../../../../src/conditions/predefined'; -import { - TEST_CHAIN_ID, - TEST_CONTRACT_ADDR, - testEvmConditionObj, - testReturnValueTest, - testRpcConditionObj, -} from '../../testVariables'; +import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from '../../testVariables'; describe('validation', () => { + // TODO: Consider: + // Use Condition here with returnTestValue schema + // Refactor returnTestValue to be the part of the Condition const condition = new ERC721Balance(); let result = condition.validate({ contractAddress: TEST_CONTRACT_ADDR, @@ -34,71 +23,12 @@ describe('validation', () => { }); it('rejects on an invalid schema value', async () => { - result = condition.validate({ chain: -1 }); + result = condition.validate({ + chain: -1, + contractAddress: TEST_CONTRACT_ADDR, + }); expect(result.error?.message).toEqual( '"chain" must be one of [1, 5, 137, 80001]' ); }); }); - -describe('get context parameters from conditions', () => { - describe('from rpc condition', () => { - const methods = RpcConditionConfig.RPC_METHODS; - methods.forEach((method) => { - const contextParams = - RpcConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[ - method as keyof RpcConditionConfig - ]; - if (!contextParams) { - return; - } - contextParams.forEach((contextParam) => { - it(`gets ${contextParam} for method ${method}`, () => { - const rpcCondition = new RpcCondition({ - ...testRpcConditionObj, - method, - parameters: [contextParam], - returnValueTest: { - ...testReturnValueTest, - value: contextParam, - }, - }); - - const producedContextParam = rpcCondition.getContextParameters(); - expect(producedContextParam).toEqual([contextParam]); - }); - }); - }); - }); - - describe('from evm condition', () => { - EvmConditionConfig.STANDARD_CONTRACT_TYPES.forEach((contractType) => { - const methods = - EvmConditionConfig.METHODS_PER_CONTRACT_TYPE[contractType]; - if (!methods) { - return; - } - methods.forEach((method) => { - const contextParams = - EvmConditionConfig.CONTEXT_PARAMETERS_PER_METHOD[method]; - if (!contextParams) { - return; - } - contextParams.forEach((contextParam) => { - it(`gets ${contextParam} for method ${method}`, () => { - const evmCondition = new EvmCondition({ - ...testEvmConditionObj, - parameters: [contextParam], - returnValueTest: { - ...testReturnValueTest, - value: contextParam, - }, - }); - const producedContextParam = evmCondition.getContextParameters(); - expect(producedContextParam).toEqual([contextParam]); - }); - }); - }); - }); - }); -}); diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts index a417c9b6f..786fc31d7 100644 --- a/test/unit/conditions/base/evm.test.ts +++ b/test/unit/conditions/base/evm.test.ts @@ -1,4 +1,5 @@ import { EvmCondition } from '../../../../src/conditions/base'; +import { EvmConditionConfig } from '../../../../src/conditions/base/evm'; import { testEvmConditionObj } from '../../testVariables'; describe('validation', () => { @@ -68,3 +69,94 @@ describe('accepts either standardContractType or functionAbi but not both or non ); }); }); + +describe('standard contract types and methods', () => { + const methods = EvmConditionConfig.METHODS_PER_CONTRACT_TYPE; + const methods_per_contract_type = Object.keys(methods).map((key) => + methods[key].flatMap((method) => [key, method]) + ); + + test.each(methods_per_contract_type)( + 'accepts %s with method %s', + (standardContractType, method) => { + const evmConditionObj = { + ...testEvmConditionObj, + standardContractType, + method, + }; + const evmCondition = new EvmCondition(evmConditionObj); + expect(evmCondition.toObj()).toEqual(evmConditionObj); + } + ); + + it('rejects non-standard contract type with method', () => { + const conditionObj = { + ...testEvmConditionObj, + standardContractType: 'fake_standard_contract_type', + method: 'fake_method', + }; + const badEvmCondition = new EvmCondition(conditionObj); + expect(() => badEvmCondition.toObj()).toThrow( + '"standardContractType" must be one of [ERC20, ERC721]' + ); + }); +}); + +// TODO(#124) +// it('accepts custom parameters in function abi methods', async () => { +// throw new Error('Not implemented'); +// }); + +// TODO(#124) +// describe('supports custom function abi', () => { +// const fakeFunctionAbi = { +// name: 'myFunction', +// type: 'function', +// inputs: [ +// { +// name: 'account', +// type: 'address', +// }, +// { +// name: 'myCustomParam', +// type: 'uint256', +// }, +// ], +// outputs: [ +// { +// name: 'someValue', +// type: 'uint256', +// }, +// ], +// }; +// const evmConditionObj = { +// ...testEvmConditionObj, +// functionAbi: fakeFunctionAbi, +// method: 'myFunction', +// parameters: [USER_ADDRESS_PARAM, ':customParam'], +// returnValueTest: { +// index: 0, +// comparator: '==', +// value: USER_ADDRESS_PARAM, +// }, +// }; +// const evmCondition = new EvmCondition(evmConditionObj); +// const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); +// const conditionSet = new ConditionSet([evmCondition]); +// const conditionContext = new ConditionContext( +// conditionSet.toWASMConditions(), +// web3Provider +// ); +// const myCustomParam = ':customParam'; +// const customParams: Record = {}; +// customParams[myCustomParam] = 1234; +// +// it('accepts custom function abi', async () => { +// const asJson = await conditionContext +// .withCustomParams(customParams) +// .toJson(); +// expect(asJson).toBeDefined(); +// expect(asJson).toContain(USER_ADDRESS_PARAM); +// expect(asJson).toContain(myCustomParam); +// }); +// }); diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts index d26775f2e..1909a9548 100644 --- a/test/unit/conditions/context.test.ts +++ b/test/unit/conditions/context.test.ts @@ -1,16 +1,22 @@ import { SecretKey } from '@nucypher/nucypher-core'; import { CustomContextParam } from '../../../src'; -import { ConditionContext, ConditionSet } from '../../../src/conditions'; +import { ConditionSet } from '../../../src/conditions'; import { EvmCondition, RpcCondition } from '../../../src/conditions/base'; import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; +import { RESERVED_CONTEXT_PARAMS } from '../../../src/conditions/context/context'; import { fakeWeb3Provider } from '../../utils'; -import { testEvmConditionObj, testRpcConditionObj } from '../testVariables'; +import { + testEvmConditionObj, + testFunctionAbi, + testReturnValueTest, + testRpcConditionObj, +} from '../testVariables'; -describe('condition context', () => { - it('should serialize to JSON with context params', async () => { - const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); +const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); +describe('serialization', () => { + it('serializes to json', async () => { const rpcCondition = new RpcCondition({ ...testRpcConditionObj, parameters: [USER_ADDRESS_PARAM], @@ -20,133 +26,93 @@ describe('condition context', () => { value: USER_ADDRESS_PARAM, }, }); - const conditionSet = new ConditionSet([rpcCondition]); - - const conditionContext = new ConditionContext( - conditionSet.toWASMConditions(), + const conditionContext = new ConditionSet([rpcCondition]).buildContext( web3Provider ); const asJson = await conditionContext.toJson(); - expect(asJson).toContain(USER_ADDRESS_PARAM); + expect(asJson).toBeDefined(); }); +}); - describe('supports user-defined parameters', () => { - const evmConditionObj = { - ...testEvmConditionObj, - standardContractType: 'ERC20', - method: 'balanceOf', - parameters: [USER_ADDRESS_PARAM, ':customParam'], - returnValueTest: { - index: 0, - comparator: '==', - value: USER_ADDRESS_PARAM, - }, - }; - const evmCondition = new EvmCondition(evmConditionObj); - const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - const conditionSet = new ConditionSet([evmCondition]); - const conditionContext = new ConditionContext( - conditionSet.toWASMConditions(), - web3Provider - ); - const myCustomParam = ':customParam'; - const customParams: Record = {}; - customParams[myCustomParam] = 1234; +describe('context parameters', () => { + const customParamKey = ':customParam'; + const customParams: Record = {}; + customParams[customParamKey] = 1234; - it('accepts user-provided context parameters', async () => { - const asJson = await conditionContext + const evmConditionObj = { + ...testEvmConditionObj, + returnValueTest: { + ...testReturnValueTest, + value: customParamKey, + }, + }; + const evmCondition = new EvmCondition(evmConditionObj); + const conditionSet = new ConditionSet([evmCondition]); + const conditionContext = conditionSet.buildContext(web3Provider); + + describe('return value test', () => { + it('accepts on a custom context parameters', async () => { + const asObj = await conditionContext .withCustomParams(customParams) - .toJson(); - expect(asJson).toBeDefined(); - expect(asJson).toContain(USER_ADDRESS_PARAM); - expect(asJson).toContain(myCustomParam); + .toObj(); + expect(asObj).toBeDefined(); + expect(asObj[customParamKey]).toEqual(1234); }); - it('throws on missing custom context param', async () => { - await expect(conditionContext.toJson()).rejects.toThrow( - `Missing custom context parameter ${myCustomParam}` + it('rejects on a missing custom context parameter', async () => { + await expect(conditionContext.toObj()).rejects.toThrow( + `Missing custom context parameter(s): ${customParamKey}` ); }); + }); - it('throws on using reserved context parameter identifiers', () => { - const badCustomParams: Record = {}; - badCustomParams[USER_ADDRESS_PARAM] = 'this-will-throw'; - + it('rejects on using reserved context parameter', () => { + const badCustomParams: Record = {}; + RESERVED_CONTEXT_PARAMS.forEach((reservedParam) => { + badCustomParams[reservedParam] = 'this-will-throw'; expect(() => conditionContext.withCustomParams(badCustomParams)).toThrow( - `Cannot use reserved parameter name ${USER_ADDRESS_PARAM} as custom parameter` + `Cannot use reserved parameter name ${reservedParam} as custom parameter` ); }); + }); + + describe('custom method parameters', () => { + const evmConditionObj = { + ...testEvmConditionObj, + standardContractType: undefined, + functionAbi: testFunctionAbi, + parameters: [USER_ADDRESS_PARAM, customParamKey], + returnValueTest: { + ...testReturnValueTest, + }, + }; - it('accepts custom parameters in predefined methods', async () => { + it('rejects on a missing parameter ', async () => { const customEvmCondition = new EvmCondition({ ...evmConditionObj, - parameters: [myCustomParam], + parameters: [USER_ADDRESS_PARAM, customParamKey], }); - const conditionSet = new ConditionSet([customEvmCondition]); - const conditionContext = new ConditionContext( - conditionSet.toWASMConditions(), - web3Provider + const conditionContext = new ConditionSet([ + customEvmCondition, + ]).buildContext(web3Provider); + + await expect(async () => conditionContext.toObj()).rejects.toThrow( + `Missing custom context parameter(s): ${customParamKey}` ); + }); - const asJson = await conditionContext - .withCustomParams(customParams) - .toJson(); - expect(asJson).toBeDefined(); - expect(asJson).toContain(myCustomParam); + it('accepts on a hard-coded parameter ', async () => { + const customEvmCondition = new EvmCondition({ + ...evmConditionObj, + parameters: [USER_ADDRESS_PARAM, 100], + }); + const conditionContext = new ConditionSet([ + customEvmCondition, + ]).buildContext(web3Provider); + + const asObj = await conditionContext.toObj(); + expect(asObj).toBeDefined(); + expect(asObj[USER_ADDRESS_PARAM]).toBeDefined(); }); }); - - // TODO: Fix this test. Fails with '"method" must be [balanceOf]' - // describe('supports custom function abi', () => { - // const fakeFunctionAbi = { - // name: 'myFunction', - // type: 'function', - // inputs: [ - // { - // name: 'account', - // type: 'address', - // }, - // { - // name: 'myCustomParam', - // type: 'uint256', - // }, - // ], - // outputs: [ - // { - // name: 'someValue', - // type: 'uint256', - // }, - // ], - // }; - // const evmConditionObj = { - // ...testEvmConditionObj, - // functionAbi: fakeFunctionAbi, - // method: 'myFunction', - // parameters: [USER_ADDRESS_PARAM, ':customParam'], - // returnValueTest: { - // index: 0, - // comparator: '==', - // value: USER_ADDRESS_PARAM, - // }, - // }; - // const evmCondition = new EvmCondition(evmConditionObj); - // const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - // const conditionSet = new ConditionSet([evmCondition]); - // const conditionContext = new ConditionContext( - // conditionSet.toWASMConditions(), - // web3Provider - // ); - // const myCustomParam = ':customParam'; - // const customParams: Record = {}; - // customParams[myCustomParam] = 1234; - // - // it('accepts custom function abi', async () => { - // const asJson = await conditionContext - // .withCustomParams(customParams) - // .toJson(); - // expect(asJson).toBeDefined(); - // expect(asJson).toContain(USER_ADDRESS_PARAM); - // expect(asJson).toContain(myCustomParam); - // }); - // }); }); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index f577291ef..13cad2210 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -90,3 +90,24 @@ export const testEvmConditionObj = { parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], returnValueTest: testReturnValueTest, }; + +export const testFunctionAbi = { + name: 'myFunction', + type: 'function', + inputs: [ + { + name: 'account', + type: 'address', + }, + { + name: 'myCustomParam', + type: 'uint256', + }, + ], + outputs: [ + { + name: 'someValue', + type: 'uint256', + }, + ], +}; From a0ea384ec813052c0fbab06e7f0644ebb105c089 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 15 May 2023 20:08:32 +0200 Subject: [PATCH 31/98] feat!: remove unused revocation kit --- src/index.ts | 1 - src/kits/revocation.ts | 10 ---------- 2 files changed, 11 deletions(-) delete mode 100644 src/kits/revocation.ts diff --git a/src/index.ts b/src/index.ts index 1b54d89cb..3087508d6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,7 +20,6 @@ export type { Configuration } from './config'; export { defaultConfiguration } from './config'; // Kits -export { RevocationKit } from './kits/revocation'; export { PolicyMessageKit } from './kits/message'; // Conditions diff --git a/src/kits/revocation.ts b/src/kits/revocation.ts deleted file mode 100644 index 9e34a77f0..000000000 --- a/src/kits/revocation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RevocationOrder, Signer, TreasureMap } from '@nucypher/nucypher-core'; - -export class RevocationKit { - public revocationOrder: RevocationOrder | null; - - // ideally underlying rust should throw an error if the treasure map is not signed by the signer - constructor(treasureMap: TreasureMap, signer: Signer) { - this.revocationOrder = treasureMap.makeRevocationOrders(signer); - } -} From 96291e8c005c656713bc2a438111a2fedad3c544 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 16 May 2023 13:44:22 +0200 Subject: [PATCH 32/98] reworked evm condition validation --- src/conditions/base/condition.ts | 19 ----- src/conditions/base/evm.ts | 109 ++++++++++++++------------ src/conditions/base/rpc.ts | 39 ++++----- src/conditions/base/schema.ts | 35 +++++++++ src/conditions/base/timelock.ts | 3 +- test/integration/pre.test.ts | 2 + test/unit/conditions/base/evm.test.ts | 41 ++++++++-- 7 files changed, 153 insertions(+), 95 deletions(-) create mode 100644 src/conditions/base/schema.ts diff --git a/src/conditions/base/condition.ts b/src/conditions/base/condition.ts index f8711fba3..d6f5e5115 100644 --- a/src/conditions/base/condition.ts +++ b/src/conditions/base/condition.ts @@ -1,24 +1,5 @@ import Joi, { ValidationError } from 'joi'; -const COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; - -export interface ReturnValueTestConfig { - index?: number; - comparator: string; - value: string | number; -} - -export const makeReturnValueTest = - (): Joi.ObjectSchema => - Joi.object({ - index: Joi.number().optional(), - comparator: Joi.string() - .valid(...COMPARATORS) - .required(), - value: Joi.alternatives(Joi.string(), Joi.number()).required(), - }); - -// TODO: Consider turning this into an abstract class export class Condition { // No schema by default, i.e. no validation by default public readonly schema = Joi.object(); diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index c46659fc0..a0bf32594 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -1,68 +1,79 @@ import Joi from 'joi'; -import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; +import { + ETH_ADDRESS_REGEXP, + SUPPORTED_CHAINS, + USER_ADDRESS_PARAM, +} from '../const'; -import { makeReturnValueTest } from './condition'; -import { RpcCondition } from './rpc'; +import { Condition } from './condition'; +import { makeReturnValueTest } from './schema'; -export interface EvmConditionConfig { - CONDITION_TYPE: string; - STANDARD_CONTRACT_TYPES: string[]; - METHODS_PER_CONTRACT_TYPE: Record; - PARAMETERS_PER_METHOD: Record; -} - -const METHODS_PER_CONTRACT_TYPE = { - ERC20: ['balanceOf'], - ERC721: ['balanceOf', 'ownerOf'], -}; -export const EvmConditionConfig: EvmConditionConfig = { - CONDITION_TYPE: 'evm', // TODO: How is this used? Similar to the Timelock.defaults.method? - STANDARD_CONTRACT_TYPES: Object.keys(METHODS_PER_CONTRACT_TYPE), - METHODS_PER_CONTRACT_TYPE, - PARAMETERS_PER_METHOD: { - balanceOf: ['address'], - ownerOf: ['tokenId'], - }, -}; +const standardContractMethods = Joi.string().when('standardContractType', { + switch: [ + { + is: 'ERC20', + then: Joi.valid('balanceOf').required(), + }, + { + is: 'ERC721', + then: Joi.valid('balanceOf', 'ownerOf').required(), + }, + ], +}); -// A helper method for making complex Joi types -// It says "allow these `types` when `parent` value is given" -const makeWhenGuard = ( - schema: Joi.StringSchema | Joi.ArraySchema, - types: Record, - parent: string -) => { - Object.entries(types).forEach(([key, value]) => { - schema = schema.when(parent, { - is: key, - then: value, - }); - }); - return schema; -}; +const standardContractParameters = Joi.when('method', { + switch: [ + { + is: 'balanceOf', + then: Joi.array() + .length(1) + .items( + Joi.alternatives( + Joi.string().pattern(ETH_ADDRESS_REGEXP), + Joi.string().equal(USER_ADDRESS_PARAM) + ) + ), + }, + { + is: 'ownerOf', + then: Joi.array() + .length(1) + .items(Joi.alternatives(Joi.number().integer().positive())), + }, + ], +}); -const makeMethod = () => - makeWhenGuard( - Joi.string(), - EvmConditionConfig.METHODS_PER_CONTRACT_TYPE, - 'standardContractType' - ); - -export class EvmCondition extends RpcCondition { +export class EvmCondition extends Condition { public readonly schema = Joi.object({ contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), chain: Joi.string() .valid(...SUPPORTED_CHAINS) .required(), standardContractType: Joi.string() - .valid(...EvmConditionConfig.STANDARD_CONTRACT_TYPES) + .valid(...['ERC20', 'ERC721']) .optional(), functionAbi: Joi.object().optional(), - method: makeMethod().required(), + method: Joi.string().required(), parameters: Joi.array().required(), returnValueTest: makeReturnValueTest(), }) // At most one of these keys needs to be present - .xor('standardContractType', 'functionAbi'); + .xor('standardContractType', 'functionAbi') + // When standardContractType is present: + .when('.standardContractType', { + is: Joi.exist(), + then: Joi.object({ + method: standardContractMethods, + parameters: standardContractParameters, + }), + }) + // When functionAbi is present: + .when('.functionAbi', { + is: Joi.exist(), + then: Joi.object({ + method: Joi.string().required(), + parameters: Joi.array().required(), + }), + }); } diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 4cf9a3e6c..bbddf1f6f 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -6,7 +6,8 @@ import { USER_ADDRESS_PARAM, } from '../const'; -import { Condition, makeReturnValueTest } from './condition'; +import { Condition } from './condition'; +import { makeReturnValueTest } from './schema'; export interface RpcConditionConfig { CONDITION_TYPE: string; @@ -25,23 +26,28 @@ export const RpcConditionConfig: RpcConditionConfig = { export const ethAddressOrUserAddress = Joi.alternatives().try( Joi.string().pattern(ETH_ADDRESS_REGEXP), - USER_ADDRESS_PARAM, + USER_ADDRESS_PARAM ); export const getAddressSchema = () => Joi.array().items(ethAddressOrUserAddress).required(); -export const rpcMethodSchema = RpcConditionConfig.RPC_METHODS.reduce( - (acc, method) => { - if (RpcConditionConfig.PARAMETERS_PER_METHOD[method].includes('address')) { - acc[method] = getAddressSchema(); - } else { - acc[method] = Joi.array().items(Joi.string()).required(); - } - return acc; - }, - {} as Record, -); +const rpcMethodSchema = RpcConditionConfig.RPC_METHODS.reduce((acc, method) => { + if (RpcConditionConfig.PARAMETERS_PER_METHOD[method].includes('address')) { + acc[method] = getAddressSchema(); + } else { + acc[method] = Joi.array().items(Joi.string()).required(); + } + return acc; +}, {} as Record); + +const makeParameters = () => + Joi.when('method', { + switch: RpcConditionConfig.RPC_METHODS.map((method) => ({ + is: method, + then: rpcMethodSchema[method], + })), + }); export class RpcCondition extends Condition { public readonly schema = Joi.object({ @@ -51,12 +57,7 @@ export class RpcCondition extends Condition { method: Joi.string() .valid(...RpcConditionConfig.RPC_METHODS) .required(), - parameters: Joi.when('method', { - switch: RpcConditionConfig.RPC_METHODS.map((method) => ({ - is: method, - then: rpcMethodSchema[method], - })), - }), + parameters: makeParameters(), returnValueTest: makeReturnValueTest(), }); } diff --git a/src/conditions/base/schema.ts b/src/conditions/base/schema.ts new file mode 100644 index 000000000..4f9bec4cc --- /dev/null +++ b/src/conditions/base/schema.ts @@ -0,0 +1,35 @@ +import Joi from 'joi'; + +const COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; + +export interface ReturnValueTestConfig { + index?: number; + comparator: string; + value: string | number; +} + +export const makeReturnValueTest = + (): Joi.ObjectSchema => + Joi.object({ + index: Joi.number().optional(), + comparator: Joi.string() + .valid(...COMPARATORS) + .required(), + value: Joi.alternatives(Joi.string(), Joi.number()).required(), + }); + +// A helper method for making complex Joi types +// It says "allow these `types` when `parent` value is given" +export const makeWhenGuard = ( + schema: Joi.StringSchema | Joi.ArraySchema, + types: Record, + parent: string +) => { + Object.entries(types).forEach(([key, value]) => { + schema = schema.when(parent, { + is: key, + then: value, + }); + }); + return schema; +}; diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/timelock.ts index ac00f6089..4a734ff01 100644 --- a/src/conditions/base/timelock.ts +++ b/src/conditions/base/timelock.ts @@ -1,6 +1,7 @@ import Joi from 'joi'; -import { Condition, makeReturnValueTest } from './condition'; +import { Condition } from './condition'; +import { makeReturnValueTest } from './schema'; export class TimelockCondition extends Condition { // TODO: This is the only condition that uses defaults, and also the only condition that uses `method` in order diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index 3c6500d95..2501509cb 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -87,10 +87,12 @@ describe('proxy reencryption', () => { const genuineUndead = new ERC721Ownership({ contractAddress: '0x209e639a0EC166Ac7a1A4bA41968fa967dB30221', chain: 1, + parameters: [1], }); const gnomePals = new ERC721Ownership({ contractAddress: '0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA', chain: 1, + parameters: [1], }); const conditionsSet = new ConditionSet([ genuineUndead, diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts index 786fc31d7..e0b47c7c9 100644 --- a/test/unit/conditions/base/evm.test.ts +++ b/test/unit/conditions/base/evm.test.ts @@ -1,5 +1,4 @@ import { EvmCondition } from '../../../../src/conditions/base'; -import { EvmConditionConfig } from '../../../../src/conditions/base/evm'; import { testEvmConditionObj } from '../../testVariables'; describe('validation', () => { @@ -70,14 +69,17 @@ describe('accepts either standardContractType or functionAbi but not both or non }); }); -describe('standard contract types and methods', () => { - const methods = EvmConditionConfig.METHODS_PER_CONTRACT_TYPE; +describe('standard contracts', () => { + const methods: Record = { + ERC20: ['balanceOf'], + ERC721: ['balanceOf', 'ownerOf'], + }; const methods_per_contract_type = Object.keys(methods).map((key) => methods[key].flatMap((method) => [key, method]) ); test.each(methods_per_contract_type)( - 'accepts %s with method %s', + 'accepts on %s with method %s', (standardContractType, method) => { const evmConditionObj = { ...testEvmConditionObj, @@ -89,17 +91,42 @@ describe('standard contract types and methods', () => { } ); - it('rejects non-standard contract type with method', () => { - const conditionObj = { + it('rejects on a non-standard contract type with method', () => { + const badConditionObj = { ...testEvmConditionObj, standardContractType: 'fake_standard_contract_type', method: 'fake_method', }; - const badEvmCondition = new EvmCondition(conditionObj); + const badEvmCondition = new EvmCondition(badConditionObj); expect(() => badEvmCondition.toObj()).toThrow( '"standardContractType" must be one of [ERC20, ERC721]' ); }); + + it('rejects on a standard contract type with non-method', () => { + const badConditionObj = { + ...testEvmConditionObj, + standardContractType: 'ERC20', + method: 'fake_method', + }; + const badEvmCondition = new EvmCondition(badConditionObj); + expect(() => badEvmCondition.toObj()).toThrow( + '"method" must be [balanceOf]' + ); + }); + + it('rejects on a standard contract method with bad parameters', () => { + const badConditionObj = { + ...testEvmConditionObj, + standardContractType: 'ERC20', + method: 'balanceOf', + parameters: ['bad-address'], + }; + const badEvmCondition = new EvmCondition(badConditionObj); + expect(() => badEvmCondition.toObj()).toThrow( + '"parameters[0]" with value "bad-address" fails to match the required pattern: /^0x[a-fA-F0-9]{40}$/' + ); + }); }); // TODO(#124) From 5bf6b74db7eb5c5de6f7938db0c0ace1e7a8c42f Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 16 May 2023 14:43:10 +0200 Subject: [PATCH 33/98] reworked rpc condition validation --- src/conditions/base/evm.ts | 16 +++-------- src/conditions/base/rpc.ts | 51 +++++++---------------------------- src/conditions/base/schema.ts | 21 +++++---------- 3 files changed, 20 insertions(+), 68 deletions(-) diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index a0bf32594..f4a562fd1 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -1,13 +1,9 @@ import Joi from 'joi'; -import { - ETH_ADDRESS_REGEXP, - SUPPORTED_CHAINS, - USER_ADDRESS_PARAM, -} from '../const'; +import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; import { Condition } from './condition'; -import { makeReturnValueTest } from './schema'; +import { ethAddressOrUserAddressSchema, makeReturnValueTest } from './schema'; const standardContractMethods = Joi.string().when('standardContractType', { switch: [ @@ -28,12 +24,8 @@ const standardContractParameters = Joi.when('method', { is: 'balanceOf', then: Joi.array() .length(1) - .items( - Joi.alternatives( - Joi.string().pattern(ETH_ADDRESS_REGEXP), - Joi.string().equal(USER_ADDRESS_PARAM) - ) - ), + .items(ethAddressOrUserAddressSchema) + .required(), }, { is: 'ownerOf', diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index bbddf1f6f..b5ee34c21 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -1,51 +1,20 @@ -import Joi, { Schema } from 'joi'; +import Joi from 'joi'; -import { - ETH_ADDRESS_REGEXP, - SUPPORTED_CHAINS, - USER_ADDRESS_PARAM, -} from '../const'; +import { SUPPORTED_CHAINS } from '../const'; import { Condition } from './condition'; -import { makeReturnValueTest } from './schema'; +import { ethAddressOrUserAddressSchema, makeReturnValueTest } from './schema'; -export interface RpcConditionConfig { - CONDITION_TYPE: string; - RPC_METHODS: string[]; - PARAMETERS_PER_METHOD: Record; -} - -export const RpcConditionConfig: RpcConditionConfig = { - CONDITION_TYPE: 'rpc', - RPC_METHODS: ['eth_getBalance', 'balanceOf'], - PARAMETERS_PER_METHOD: { - eth_getBalance: ['address'], - balanceOf: ['address'], - }, +const rpcMethodSchemas: Record = { + eth_getBalance: Joi.array().items(ethAddressOrUserAddressSchema).required(), + balanceOf: Joi.array().items(ethAddressOrUserAddressSchema).required(), }; -export const ethAddressOrUserAddress = Joi.alternatives().try( - Joi.string().pattern(ETH_ADDRESS_REGEXP), - USER_ADDRESS_PARAM -); - -export const getAddressSchema = () => - Joi.array().items(ethAddressOrUserAddress).required(); - -const rpcMethodSchema = RpcConditionConfig.RPC_METHODS.reduce((acc, method) => { - if (RpcConditionConfig.PARAMETERS_PER_METHOD[method].includes('address')) { - acc[method] = getAddressSchema(); - } else { - acc[method] = Joi.array().items(Joi.string()).required(); - } - return acc; -}, {} as Record); - const makeParameters = () => - Joi.when('method', { - switch: RpcConditionConfig.RPC_METHODS.map((method) => ({ + Joi.array().when('method', { + switch: Object.keys(rpcMethodSchemas).map((method) => ({ is: method, - then: rpcMethodSchema[method], + then: rpcMethodSchemas[method], })), }); @@ -55,7 +24,7 @@ export class RpcCondition extends Condition { .valid(...SUPPORTED_CHAINS) .required(), method: Joi.string() - .valid(...RpcConditionConfig.RPC_METHODS) + .valid(...Object.keys(rpcMethodSchemas)) .required(), parameters: makeParameters(), returnValueTest: makeReturnValueTest(), diff --git a/src/conditions/base/schema.ts b/src/conditions/base/schema.ts index 4f9bec4cc..b2647cc93 100644 --- a/src/conditions/base/schema.ts +++ b/src/conditions/base/schema.ts @@ -1,5 +1,7 @@ import Joi from 'joi'; +import { ETH_ADDRESS_REGEXP, USER_ADDRESS_PARAM } from '../const'; + const COMPARATORS = ['==', '>', '<', '>=', '<=', '!=']; export interface ReturnValueTestConfig { @@ -18,18 +20,7 @@ export const makeReturnValueTest = value: Joi.alternatives(Joi.string(), Joi.number()).required(), }); -// A helper method for making complex Joi types -// It says "allow these `types` when `parent` value is given" -export const makeWhenGuard = ( - schema: Joi.StringSchema | Joi.ArraySchema, - types: Record, - parent: string -) => { - Object.entries(types).forEach(([key, value]) => { - schema = schema.when(parent, { - is: key, - then: value, - }); - }); - return schema; -}; +export const ethAddressOrUserAddressSchema = Joi.alternatives().try( + Joi.string().pattern(ETH_ADDRESS_REGEXP), + USER_ADDRESS_PARAM +); From 278333ab5df48278fed2e0a111eac9623ef3988f Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 16 May 2023 15:51:28 +0200 Subject: [PATCH 34/98] self review --- src/conditions/base/evm.ts | 4 ++-- src/conditions/base/rpc.ts | 4 ++-- src/conditions/base/schema.ts | 19 +++++++++---------- src/conditions/base/timelock.ts | 4 ++-- src/conditions/context/context.ts | 10 +--------- test/docs/cbd.test.ts | 2 +- test/unit/conditions/base/evm.test.ts | 2 +- test/unit/conditions/context.test.ts | 7 ++++--- 8 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index f4a562fd1..0e6b32810 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -3,7 +3,7 @@ import Joi from 'joi'; import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; import { Condition } from './condition'; -import { ethAddressOrUserAddressSchema, makeReturnValueTest } from './schema'; +import { ethAddressOrUserAddressSchema, returnValueTestSchema } from './schema'; const standardContractMethods = Joi.string().when('standardContractType', { switch: [ @@ -48,7 +48,7 @@ export class EvmCondition extends Condition { functionAbi: Joi.object().optional(), method: Joi.string().required(), parameters: Joi.array().required(), - returnValueTest: makeReturnValueTest(), + returnValueTest: returnValueTestSchema, }) // At most one of these keys needs to be present .xor('standardContractType', 'functionAbi') diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index b5ee34c21..13d1edb33 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -3,7 +3,7 @@ import Joi from 'joi'; import { SUPPORTED_CHAINS } from '../const'; import { Condition } from './condition'; -import { ethAddressOrUserAddressSchema, makeReturnValueTest } from './schema'; +import { ethAddressOrUserAddressSchema, returnValueTestSchema } from './schema'; const rpcMethodSchemas: Record = { eth_getBalance: Joi.array().items(ethAddressOrUserAddressSchema).required(), @@ -27,6 +27,6 @@ export class RpcCondition extends Condition { .valid(...Object.keys(rpcMethodSchemas)) .required(), parameters: makeParameters(), - returnValueTest: makeReturnValueTest(), + returnValueTest: returnValueTestSchema, }); } diff --git a/src/conditions/base/schema.ts b/src/conditions/base/schema.ts index b2647cc93..62af41800 100644 --- a/src/conditions/base/schema.ts +++ b/src/conditions/base/schema.ts @@ -10,17 +10,16 @@ export interface ReturnValueTestConfig { value: string | number; } -export const makeReturnValueTest = - (): Joi.ObjectSchema => - Joi.object({ - index: Joi.number().optional(), - comparator: Joi.string() - .valid(...COMPARATORS) - .required(), - value: Joi.alternatives(Joi.string(), Joi.number()).required(), - }); +export const returnValueTestSchema: Joi.ObjectSchema = + Joi.object({ + index: Joi.number().optional(), + comparator: Joi.string() + .valid(...COMPARATORS) + .required(), + value: Joi.alternatives(Joi.string(), Joi.number()).required(), + }); -export const ethAddressOrUserAddressSchema = Joi.alternatives().try( +export const ethAddressOrUserAddressSchema = Joi.alternatives( Joi.string().pattern(ETH_ADDRESS_REGEXP), USER_ADDRESS_PARAM ); diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/timelock.ts index 4a734ff01..a517835c2 100644 --- a/src/conditions/base/timelock.ts +++ b/src/conditions/base/timelock.ts @@ -1,7 +1,7 @@ import Joi from 'joi'; import { Condition } from './condition'; -import { makeReturnValueTest } from './schema'; +import { returnValueTestSchema } from './schema'; export class TimelockCondition extends Condition { // TODO: This is the only condition that uses defaults, and also the only condition that uses `method` in order @@ -14,6 +14,6 @@ export class TimelockCondition extends Condition { public readonly schema = Joi.object({ method: Joi.string().valid(this.defaults.method).required(), - returnValueTest: makeReturnValueTest(), + returnValueTest: returnValueTestSchema, }); } diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index 95949962a..91547d8d8 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -8,15 +8,7 @@ import { TypedSignature, WalletAuthenticationProvider } from './providers'; export type CustomContextParam = string | number | boolean; export type ContextParam = CustomContextParam | TypedSignature; -const CONTEXT_PARAMS_HANDLERS = { - [USER_ADDRESS_PARAM]: async ( - web3Provider: ethers.providers.Web3Provider - ): Promise => { - const provider = new WalletAuthenticationProvider(web3Provider); - return provider.getOrCreateWalletSignature(); - }, -}; -export const RESERVED_CONTEXT_PARAMS = Object.keys(CONTEXT_PARAMS_HANDLERS); +export const RESERVED_CONTEXT_PARAMS = [USER_ADDRESS_PARAM]; export const CONTEXT_PARAM_PREFIX = ':'; export class ConditionContext { diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index f444720af..934bb2985 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -3,7 +3,6 @@ import { providers } from 'ethers'; import { Cohort, conditions, SecretKey, Strategy } from '../../src'; import { Ursula } from '../../src/characters/porter'; -import { EvmCondition } from '../../src/conditions/base'; import { toBytes } from '../../src/utils'; import { fakeUrsulas, @@ -19,6 +18,7 @@ import { const { predefined: { ERC721Ownership }, + base: { EvmCondition }, ConditionSet, } = conditions; diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts index e0b47c7c9..2ed5331e6 100644 --- a/test/unit/conditions/base/evm.test.ts +++ b/test/unit/conditions/base/evm.test.ts @@ -95,7 +95,7 @@ describe('standard contracts', () => { const badConditionObj = { ...testEvmConditionObj, standardContractType: 'fake_standard_contract_type', - method: 'fake_method', + method: 'ownerOf', }; const badEvmCondition = new EvmCondition(badConditionObj); expect(() => badEvmCondition.toObj()).toThrow( diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts index 1909a9548..9a1746919 100644 --- a/test/unit/conditions/context.test.ts +++ b/test/unit/conditions/context.test.ts @@ -31,6 +31,7 @@ describe('serialization', () => { ); const asJson = await conditionContext.toJson(); expect(asJson).toBeDefined(); + expect(asJson).toContain(USER_ADDRESS_PARAM); }); }); @@ -79,9 +80,9 @@ describe('context parameters', () => { describe('custom method parameters', () => { const evmConditionObj = { ...testEvmConditionObj, - standardContractType: undefined, + standardContractType: undefined, // We're going to use a custom function ABI functionAbi: testFunctionAbi, - parameters: [USER_ADDRESS_PARAM, customParamKey], + parameters: [USER_ADDRESS_PARAM, customParamKey], // We're going to use a custom parameter returnValueTest: { ...testReturnValueTest, }, @@ -101,7 +102,7 @@ describe('context parameters', () => { ); }); - it('accepts on a hard-coded parameter ', async () => { + it('accepts on a hard-coded parameter', async () => { const customEvmCondition = new EvmCondition({ ...evmConditionObj, parameters: [USER_ADDRESS_PARAM, 100], From 6c4d9a05ad2ac03ce21622e40a2ee1627724249d Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 17 May 2023 18:13:50 +0200 Subject: [PATCH 35/98] remove limitations on standard contract type methods and params --- src/conditions/base/evm.ts | 51 +---------------------- test/unit/conditions/base/evm.test.ts | 60 --------------------------- 2 files changed, 2 insertions(+), 109 deletions(-) diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index 0e6b32810..820ffefe3 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -3,38 +3,7 @@ import Joi from 'joi'; import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; import { Condition } from './condition'; -import { ethAddressOrUserAddressSchema, returnValueTestSchema } from './schema'; - -const standardContractMethods = Joi.string().when('standardContractType', { - switch: [ - { - is: 'ERC20', - then: Joi.valid('balanceOf').required(), - }, - { - is: 'ERC721', - then: Joi.valid('balanceOf', 'ownerOf').required(), - }, - ], -}); - -const standardContractParameters = Joi.when('method', { - switch: [ - { - is: 'balanceOf', - then: Joi.array() - .length(1) - .items(ethAddressOrUserAddressSchema) - .required(), - }, - { - is: 'ownerOf', - then: Joi.array() - .length(1) - .items(Joi.alternatives(Joi.number().integer().positive())), - }, - ], -}); +import { returnValueTestSchema } from './schema'; export class EvmCondition extends Condition { public readonly schema = Joi.object({ @@ -51,21 +20,5 @@ export class EvmCondition extends Condition { returnValueTest: returnValueTestSchema, }) // At most one of these keys needs to be present - .xor('standardContractType', 'functionAbi') - // When standardContractType is present: - .when('.standardContractType', { - is: Joi.exist(), - then: Joi.object({ - method: standardContractMethods, - parameters: standardContractParameters, - }), - }) - // When functionAbi is present: - .when('.functionAbi', { - is: Joi.exist(), - then: Joi.object({ - method: Joi.string().required(), - parameters: Joi.array().required(), - }), - }); + .xor('standardContractType', 'functionAbi'); } diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts index 2ed5331e6..ade286a4d 100644 --- a/test/unit/conditions/base/evm.test.ts +++ b/test/unit/conditions/base/evm.test.ts @@ -69,66 +69,6 @@ describe('accepts either standardContractType or functionAbi but not both or non }); }); -describe('standard contracts', () => { - const methods: Record = { - ERC20: ['balanceOf'], - ERC721: ['balanceOf', 'ownerOf'], - }; - const methods_per_contract_type = Object.keys(methods).map((key) => - methods[key].flatMap((method) => [key, method]) - ); - - test.each(methods_per_contract_type)( - 'accepts on %s with method %s', - (standardContractType, method) => { - const evmConditionObj = { - ...testEvmConditionObj, - standardContractType, - method, - }; - const evmCondition = new EvmCondition(evmConditionObj); - expect(evmCondition.toObj()).toEqual(evmConditionObj); - } - ); - - it('rejects on a non-standard contract type with method', () => { - const badConditionObj = { - ...testEvmConditionObj, - standardContractType: 'fake_standard_contract_type', - method: 'ownerOf', - }; - const badEvmCondition = new EvmCondition(badConditionObj); - expect(() => badEvmCondition.toObj()).toThrow( - '"standardContractType" must be one of [ERC20, ERC721]' - ); - }); - - it('rejects on a standard contract type with non-method', () => { - const badConditionObj = { - ...testEvmConditionObj, - standardContractType: 'ERC20', - method: 'fake_method', - }; - const badEvmCondition = new EvmCondition(badConditionObj); - expect(() => badEvmCondition.toObj()).toThrow( - '"method" must be [balanceOf]' - ); - }); - - it('rejects on a standard contract method with bad parameters', () => { - const badConditionObj = { - ...testEvmConditionObj, - standardContractType: 'ERC20', - method: 'balanceOf', - parameters: ['bad-address'], - }; - const badEvmCondition = new EvmCondition(badConditionObj); - expect(() => badEvmCondition.toObj()).toThrow( - '"parameters[0]" with value "bad-address" fails to match the required pattern: /^0x[a-fA-F0-9]{40}$/' - ); - }); -}); - // TODO(#124) // it('accepts custom parameters in function abi methods', async () => { // throw new Error('Not implemented'); From a22cb9bfa9c11a1c33a32405ea589a52a162fef3 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 17 May 2023 18:23:20 +0200 Subject: [PATCH 36/98] address pr comments --- src/conditions/base/schema.ts | 6 +++++- src/conditions/context/context.ts | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/conditions/base/schema.ts b/src/conditions/base/schema.ts index 62af41800..544033cdc 100644 --- a/src/conditions/base/schema.ts +++ b/src/conditions/base/schema.ts @@ -16,7 +16,11 @@ export const returnValueTestSchema: Joi.ObjectSchema = comparator: Joi.string() .valid(...COMPARATORS) .required(), - value: Joi.alternatives(Joi.string(), Joi.number()).required(), + value: Joi.alternatives( + Joi.string(), + Joi.number(), + Joi.boolean() + ).required(), }); export const ethAddressOrUserAddressSchema = Joi.alternatives( diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index 91547d8d8..8549de332 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -27,6 +27,11 @@ export class ConditionContext { `Cannot use reserved parameter name ${key} as custom parameter` ); } + if (!key.startsWith(CONTEXT_PARAM_PREFIX)) { + throw new Error( + `Custom parameter ${key} must start with ${CONTEXT_PARAM_PREFIX}` + ); + } }); this.walletAuthProvider = new WalletAuthenticationProvider(web3Provider); } From 5d08d7b0fc1991d1ca1564517bf76aed583fc67f Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 18 May 2023 19:55:06 +0200 Subject: [PATCH 37/98] address pr comments, part 2 --- src/conditions/base/condition.ts | 6 +++++- src/conditions/base/evm.ts | 4 +++- src/conditions/base/rpc.ts | 2 +- src/conditions/base/timelock.ts | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/conditions/base/condition.ts b/src/conditions/base/condition.ts index d6f5e5115..2f53249d7 100644 --- a/src/conditions/base/condition.ts +++ b/src/conditions/base/condition.ts @@ -1,9 +1,13 @@ import Joi, { ValidationError } from 'joi'; +import { returnValueTestSchema } from './schema'; + export class Condition { // No schema by default, i.e. no validation by default public readonly schema = Joi.object(); - public readonly defaults: Record = {}; + public readonly defaults: Record = { + returnValueTest: returnValueTestSchema.required(), + }; private validationError?: ValidationError; constructor(private readonly value: Record = {}) {} diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index 820ffefe3..79e7dfac3 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -5,6 +5,8 @@ import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; import { Condition } from './condition'; import { returnValueTestSchema } from './schema'; +export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; + export class EvmCondition extends Condition { public readonly schema = Joi.object({ contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), @@ -12,7 +14,7 @@ export class EvmCondition extends Condition { .valid(...SUPPORTED_CHAINS) .required(), standardContractType: Joi.string() - .valid(...['ERC20', 'ERC721']) + .valid(...STANDARD_CONTRACT_TYPES) .optional(), functionAbi: Joi.object().optional(), method: Joi.string().required(), diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 13d1edb33..5144c251f 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -27,6 +27,6 @@ export class RpcCondition extends Condition { .valid(...Object.keys(rpcMethodSchemas)) .required(), parameters: makeParameters(), - returnValueTest: returnValueTestSchema, + returnValueTest: returnValueTestSchema.required(), }); } diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/timelock.ts index a517835c2..5565f14c0 100644 --- a/src/conditions/base/timelock.ts +++ b/src/conditions/base/timelock.ts @@ -14,6 +14,6 @@ export class TimelockCondition extends Condition { public readonly schema = Joi.object({ method: Joi.string().valid(this.defaults.method).required(), - returnValueTest: returnValueTestSchema, + returnValueTest: returnValueTestSchema.required(), }); } From 284df4a26cb6c512749d918e079052808c4335c4 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 29 May 2023 13:25:38 +0200 Subject: [PATCH 38/98] fix: struct deser --- package.json | 4 +- src/characters/universal-bob.ts | 27 ++- src/conditions/base/condition.ts | 34 ++-- src/conditions/base/timelock.ts | 2 +- src/conditions/condition-set.ts | 37 ++-- src/conditions/context/context.ts | 7 +- src/conditions/operator.ts | 4 + src/conditions/predefined/erc721.ts | 4 +- src/sdk/cohort.ts | 5 + src/sdk/strategy.ts | 71 ++++++-- src/utils.ts | 42 ++++- test/integration/dkg-client.test.ts | 2 +- test/unit/cohort.test.ts | 68 ++----- test/unit/conditions/base/condition.test.ts | 43 ++++- test/unit/conditions/base/evm.test.ts | 5 +- test/unit/conditions/base/rpc.test.ts | 2 +- test/unit/conditions/base/timelock.test.ts | 2 +- test/unit/strategy.test.ts | 188 +++++++++----------- test/utils.ts | 24 ++- yarn.lock | 111 +++++++++++- 20 files changed, 444 insertions(+), 238 deletions(-) diff --git a/package.json b/package.json index 763ab07db..d1853adae 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,10 @@ }, "dependencies": { "@nucypher/nucypher-core": "^0.7.0", - "ferveo-wasm": "^0.1.1", "axios": "^0.21.1", + "deep-equal": "^2.2.1", "ethers": "^5.4.1", + "ferveo-wasm": "^0.1.1", "joi": "^17.7.0", "qs": "^6.10.1" }, @@ -64,6 +65,7 @@ "@babel/preset-env": "^7.15.6", "@skypack/package-check": "^0.2.2", "@typechain/ethers-v5": "^9.0.0", + "@types/deep-equal": "^1.0.1", "@types/jest": "^26.0.24", "@types/qs": "^6.9.7", "@typescript-eslint/eslint-plugin": "^4.0.1", diff --git a/src/characters/universal-bob.ts b/src/characters/universal-bob.ts index 68c8bdfcd..5f4182cd7 100644 --- a/src/characters/universal-bob.ts +++ b/src/characters/universal-bob.ts @@ -8,11 +8,11 @@ import { } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { ConditionSet } from '../conditions/condition-set'; +import { ConditionSet } from '../conditions'; import { Keyring } from '../keyring'; import { PolicyMessageKit } from '../kits/message'; import { RetrievalResult } from '../kits/retrieval'; -import { base64ToU8Receiver, u8ToBase64Replacer, zip } from '../utils'; +import { base64ToU8Receiver, bytesEquals, toJson, zip } from '../utils'; import { Porter } from './porter'; @@ -27,6 +27,7 @@ type decrypterJSON = { export class tDecDecrypter { private readonly porter: Porter; private readonly keyring: Keyring; + // private readonly verifyingKey: Keyring; constructor( @@ -98,7 +99,7 @@ export class tDecDecrypter { .reduce((acc: Record[], val) => acc.concat(val), []); const conditionContext = - ConditionSet.fromList(conditions).buildContext(provider); + ConditionSet.fromConditionList(conditions).buildContext(provider); const policyMessageKits = messageKits.map((mk) => PolicyMessageKit.fromMessageKit( @@ -149,7 +150,7 @@ export class tDecDecrypter { } public toJSON(): string { - return JSON.stringify(this.toObj(), u8ToBase64Replacer); + return toJson(this.toObj()); } private static fromObj({ @@ -172,4 +173,22 @@ export class tDecDecrypter { const config = JSON.parse(json, base64ToU8Receiver); return tDecDecrypter.fromObj(config); } + + public equals(other: tDecDecrypter): boolean { + return ( + this.porter.porterUrl.toString() === other.porter.porterUrl.toString() && + bytesEquals( + this.policyEncryptingKey.toCompressedBytes(), + other.policyEncryptingKey.toCompressedBytes() + ) && + bytesEquals( + this.encryptedTreasureMap.toBytes(), + other.encryptedTreasureMap.toBytes() + ) && + bytesEquals( + this.publisherVerifyingKey.toCompressedBytes(), + other.publisherVerifyingKey.toCompressedBytes() + ) + ); + } } diff --git a/src/conditions/base/condition.ts b/src/conditions/base/condition.ts index 2f53249d7..ee9fa4d04 100644 --- a/src/conditions/base/condition.ts +++ b/src/conditions/base/condition.ts @@ -1,25 +1,28 @@ -import Joi, { ValidationError } from 'joi'; +import Joi from 'joi'; -import { returnValueTestSchema } from './schema'; +import { objectEquals } from '../../utils'; + +type Map = Record; export class Condition { - // No schema by default, i.e. no validation by default public readonly schema = Joi.object(); - public readonly defaults: Record = { - returnValueTest: returnValueTestSchema.required(), - }; - private validationError?: ValidationError; + public readonly defaults: Map = {}; constructor(private readonly value: Record = {}) {} - public get error(): string | undefined { - return this.validationError?.message; + public validate(override: Map = {}) { + const newValue = { + ...this.defaults, + ...this.value, + ...override, + }; + return this.schema.validate(newValue); } - public toObj(): Record { - const { error, value } = this.validate(); + public toObj(): Map { + const { error, value } = this.validate(this.value); if (error) { - throw Error(error.message); + throw `Invalid condition: ${error.message}`; } return value; } @@ -28,13 +31,12 @@ export class Condition { // We disable the eslint rule here because we have to pass args to the constructor // eslint-disable-next-line @typescript-eslint/no-explicit-any this: new (...args: any[]) => T, - obj: Record + obj: Map ): T { return new this(obj); } - public validate(data: Record = {}) { - const newValue = Object.assign(this.defaults, this.value, data); - return this.schema.validate(newValue); + public equals(other: Condition) { + return objectEquals(this, other); } } diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/timelock.ts index 5565f14c0..e347524fb 100644 --- a/src/conditions/base/timelock.ts +++ b/src/conditions/base/timelock.ts @@ -8,7 +8,7 @@ export class TimelockCondition extends Condition { // to determine the schema. I.e. the only method that used `METHOD = 'timelock'` in `nucypher/nucypher`. // TODO: Consider introducing a different field for this, e.g. `conditionType` or `type`. Use this field in a // condition factory. - defaults = { + public readonly defaults: Record = { method: 'timelock', }; diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index 7864c6c6e..31361b3c3 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -1,12 +1,18 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { Condition } from './base/condition'; +import { toJson } from '../utils'; + +import { Condition } from './base'; import { ConditionContext } from './context'; import { Operator } from './operator'; type ConditionOrOperator = Condition | Operator; +export type ConditionSetJSON = { + conditions: ({ operator: string } | Record)[]; +}; + export class ConditionSet { constructor(public readonly conditions: ReadonlyArray) {} @@ -34,13 +40,24 @@ export class ConditionSet { return true; } - public toList() { - return this.conditions.map((cnd) => { + public toObj(): ConditionSetJSON { + const conditions = this.conditions.map((cnd) => { return cnd.toObj(); }); + return { conditions }; + } + + public static fromObj(obj: ConditionSetJSON): ConditionSet { + const conditions = obj.conditions.map((cnd) => { + if ('operator' in cnd) { + return Operator.fromObj(cnd as Record); + } + return Condition.fromObj(cnd); + }); + return new ConditionSet(conditions); } - public static fromList(list: ReadonlyArray>) { + public static fromConditionList(list: ReadonlyArray>) { return new ConditionSet( list.map((ele: Record) => { if ('operator' in ele) return Operator.fromObj(ele); @@ -49,16 +66,16 @@ export class ConditionSet { ); } - public toJson() { - return JSON.stringify(this.toList()); + public toJson(): string { + return toJson(this.toObj()); } - public static fromJSON(json: string) { - return ConditionSet.fromList(JSON.parse(json)); + public static fromJSON(json: string): ConditionSet { + return ConditionSet.fromObj(JSON.parse(json)); } - public toWASMConditions() { - return new WASMConditions(this.toJson()); + public toWASMConditions(): WASMConditions { + return new WASMConditions(toJson(this.toObj())); } public buildContext( diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index 8549de332..02e7156a0 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -1,6 +1,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; +import { fromJson, toJson } from '../../utils'; import { USER_ADDRESS_PARAM } from '../const'; import { TypedSignature, WalletAuthenticationProvider } from './providers'; @@ -41,8 +42,8 @@ export class ConditionContext { const requestedParameters = new Set(); // Search conditions for parameters - const parsedConditions = JSON.parse(this.conditions.toString()); - for (const cond of parsedConditions) { + const { conditions } = fromJson(this.conditions.toString()); + for (const cond of conditions) { // Check return value test const rvt = cond.returnValueTest.value; if (typeof rvt === 'string' && rvt.startsWith(CONTEXT_PARAM_PREFIX)) { @@ -92,7 +93,7 @@ export class ConditionContext { public toJson = async (): Promise => { const parameters = await this.toObj(); - return JSON.stringify(parameters); + return toJson(parameters); }; public withCustomParams = ( diff --git a/src/conditions/operator.ts b/src/conditions/operator.ts index 2ef39471c..0c96e36b3 100644 --- a/src/conditions/operator.ts +++ b/src/conditions/operator.ts @@ -19,4 +19,8 @@ export class Operator { static fromObj(obj: Record) { return new Operator(obj.operator); } + + public equals(other: Operator): boolean { + return this.operator === other.operator; + } } diff --git a/src/conditions/predefined/erc721.ts b/src/conditions/predefined/erc721.ts index 211a0b11b..ef9f880f3 100644 --- a/src/conditions/predefined/erc721.ts +++ b/src/conditions/predefined/erc721.ts @@ -2,7 +2,7 @@ import { EvmCondition } from '../base'; import { USER_ADDRESS_PARAM } from '../const'; export class ERC721Ownership extends EvmCondition { - readonly defaults = { + public readonly defaults = { method: 'ownerOf', parameters: [], standardContractType: 'ERC721', @@ -15,7 +15,7 @@ export class ERC721Ownership extends EvmCondition { } export class ERC721Balance extends EvmCondition { - readonly defaults = { + public readonly defaults = { method: 'balanceOf', parameters: [USER_ADDRESS_PARAM], standardContractType: 'ERC721', diff --git a/src/sdk/cohort.ts b/src/sdk/cohort.ts index 02bc45bd4..99f2831db 100644 --- a/src/sdk/cohort.ts +++ b/src/sdk/cohort.ts @@ -1,5 +1,6 @@ import { Porter } from '../characters/porter'; import { ChecksumAddress } from '../types'; +import { objectEquals } from '../utils'; export type CohortConfiguration = { readonly threshold: number; @@ -70,4 +71,8 @@ export class Cohort { porterUri: this.configuration.porterUri, }; } + + public equals(other: Cohort): boolean { + return objectEquals(this, other); + } } diff --git a/src/sdk/strategy.ts b/src/sdk/strategy.ts index 5facc86d2..e8626bcd3 100644 --- a/src/sdk/strategy.ts +++ b/src/sdk/strategy.ts @@ -10,9 +10,9 @@ import { Alice } from '../characters/alice'; import { Bob } from '../characters/bob'; import { Enrico } from '../characters/enrico'; import { tDecDecrypter } from '../characters/universal-bob'; -import { ConditionSet } from '../conditions/condition-set'; +import { ConditionSet, ConditionSetJSON } from '../conditions/condition-set'; import { EnactedPolicy, EnactedPolicyJSON } from '../policies/policy'; -import { base64ToU8Receiver, u8ToBase64Replacer } from '../utils'; +import { base64ToU8Receiver, bytesEquals, toJson } from '../utils'; import { Cohort, CohortJSON } from './cohort'; @@ -20,7 +20,7 @@ type StrategyJSON = { cohort: CohortJSON; aliceSecretKeyBytes: Uint8Array; bobSecretKeyBytes: Uint8Array; - conditionSet?: ConditionSet; + conditionSet?: ConditionSetJSON; startDate: Date; endDate: Date; }; @@ -29,7 +29,7 @@ type DeployedStrategyJSON = { policy: EnactedPolicyJSON; cohortConfig: CohortJSON; bobSecretKeyBytes: Uint8Array; - conditionSet?: ConditionSet; + conditionSet?: ConditionSetJSON; }; export class Strategy { @@ -119,14 +119,16 @@ export class Strategy { public static fromJSON(json: string) { const config = JSON.parse(json, base64ToU8Receiver); + config.startDate = new Date(config.startDate); + config.endDate = new Date(config.endDate); return Strategy.fromObj(config); } public toJSON() { - return JSON.stringify(this.toObj(), u8ToBase64Replacer); + return toJson(this.toObj()); } - private static fromObj({ + public static fromObj({ cohort, aliceSecretKeyBytes, bobSecretKeyBytes, @@ -138,9 +140,9 @@ export class Strategy { Cohort.fromObj(cohort), SecretKey.fromBEBytes(aliceSecretKeyBytes), SecretKey.fromBEBytes(bobSecretKeyBytes), - startDate, - endDate, - conditionSet + new Date(startDate), + new Date(endDate), + conditionSet ? ConditionSet.fromObj(conditionSet) : undefined ); } @@ -149,11 +151,30 @@ export class Strategy { cohort: this.cohort.toObj(), aliceSecretKeyBytes: this.aliceSecretKey.toBEBytes(), bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), - conditionSet: this.conditionSet, + conditionSet: this.conditionSet ? this.conditionSet.toObj() : undefined, startDate: this.startDate, endDate: this.endDate, }; } + + public equals(other: Strategy) { + return ( + this.cohort.equals(other.cohort) && + // TODO: Add equality to WASM bindings + bytesEquals( + this.aliceSecretKey.toBEBytes(), + other.aliceSecretKey.toBEBytes() + ) && + bytesEquals( + this.bobSecretKey.toBEBytes(), + other.bobSecretKey.toBEBytes() + ) && + this.startDate.toString() === other.startDate.toString() && + this.endDate.toString() === other.endDate.toString() && + this.conditionSet?.toWASMConditions === + other.conditionSet?.toWASMConditions + ); + } } export class DeployedStrategy { @@ -173,7 +194,7 @@ export class DeployedStrategy { } public toJSON() { - return JSON.stringify(this.toObj(), u8ToBase64Replacer); + return toJson(this.toObj()); } private static fromObj({ @@ -204,7 +225,15 @@ export class DeployedStrategy { const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); const label = newPolicy.label; const cohort = Cohort.fromObj(cohortConfig); - const encrypter = new Enrico(newPolicy.policyKey, undefined, conditionSet); + + const conditionSetOrUndefined = conditionSet + ? ConditionSet.fromObj(conditionSet) + : undefined; + const encrypter = new Enrico( + newPolicy.policyKey, + undefined, + conditionSetOrUndefined + ); const decrypter = new tDecDecrypter( cohort.configuration.porterUri, @@ -220,7 +249,7 @@ export class DeployedStrategy { encrypter, decrypter, bobSecretKey, - conditionSet + conditionSetOrUndefined ); } @@ -235,7 +264,21 @@ export class DeployedStrategy { policy, cohortConfig: this.cohort.toObj(), bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), - conditionSet: this.conditionSet, + conditionSet: this.conditionSet?.toObj(), }; } + + public equals(other: DeployedStrategy) { + return ( + this.label === other.label && + this.cohort.equals(other.cohort) && + bytesEquals(this.policy.id.toBytes(), other.policy.id.toBytes()) && + this.policy.label === other.policy.label && + this.policy.policyKey.equals(other.policy.policyKey) && + bytesEquals( + this.policy.encryptedTreasureMap.toBytes(), + other.policy.encryptedTreasureMap.toBytes() + ) + ); + } } diff --git a/src/utils.ts b/src/utils.ts index c000feba2..dcacd59aa 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,5 @@ +import deepEqual from 'deep-equal'; + export const toBytes = (str: string): Uint8Array => new TextEncoder().encode(str); @@ -15,29 +17,53 @@ export const toBase64 = (bytes: Uint8Array): string => export const fromBase64 = (str: string): Uint8Array => Buffer.from(str, 'base64'); -export const base64ToU8Receiver = ( - _key: string, - value: string | number | Uint8Array -) => { +export const base64ToU8Receiver = (_key: string, value: unknown) => { if (typeof value === 'string' && value.startsWith('base64:')) { return fromBase64(value.split('base64:')[1]); } return value; }; -export const u8ToBase64Replacer = ( - _key: string, - value: string | number | Uint8Array -) => { +const sortedReplacer = (_key: string, value: unknown) => { + if (value instanceof Object && !(value instanceof Array)) { + return Object.keys(value) + .sort() + .reduce((sorted: Record, key) => { + sorted[key] = (value as Record)[key]; + return sorted; + }, {}); + } + + return value; +}; + +const u8ToBase64Replacer = (_key: string, value: unknown) => { if (value instanceof Uint8Array) { return `base64:${toBase64(value)}`; } return value; }; +const sortedSerializingReplacer = (_key: string, value: unknown): unknown => { + const serializedValue = u8ToBase64Replacer(_key, value); + return sortedReplacer(_key, serializedValue); +}; + +export const toJson = (obj: unknown) => + JSON.stringify(obj, sortedSerializingReplacer); + +export const fromJson = (json: string) => JSON.parse(json, base64ToU8Receiver); + export const zip = ( a: ReadonlyArray, b: ReadonlyArray ): ReadonlyArray => a.map((k, i) => [k, b[i]]); export const toEpoch = (date: Date) => (date.getTime() / 1000) | 0; + +export const bytesEquals = (first: Uint8Array, second: Uint8Array): boolean => + first.length === second.length && + first.every((value, index) => value === second[index]); + +export const objectEquals = (a: unknown, b: unknown): boolean => + deepEqual(a, b, { strict: true }); diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts index 13a76aa82..c1d002086 100644 --- a/test/integration/dkg-client.test.ts +++ b/test/integration/dkg-client.test.ts @@ -42,6 +42,6 @@ describe('DkgClient', () => { const dkgClient = new DkgClient(provider, ritualId); const isValid = await dkgClient.verifyRitual(); - expect(isValid).toBe(true); + expect(isValid).toBeTruthy(); }); }); diff --git a/test/unit/cohort.test.ts b/test/unit/cohort.test.ts index 80ca256eb..40ab18ee8 100644 --- a/test/unit/cohort.test.ts +++ b/test/unit/cohort.test.ts @@ -1,62 +1,26 @@ import { Cohort } from '../../src'; -import { fakeUrsulas, mockGetUrsulas } from '../utils'; +import { fakeUrsulas, makeCohort } from '../utils'; describe('Cohort', () => { - it('can create Cohort from configuration', async () => { - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + const mockedUrsulas = fakeUrsulas().slice(0, 3); - const config = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - const testCohort = await Cohort.create(config); - - const expectedUrsulas = [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ]; - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(testCohort.ursulaAddresses).toEqual(expectedUrsulas); + it('creates a Cohort', async () => { + const cohort = await makeCohort(mockedUrsulas); + const expectedUrsulas = mockedUrsulas.map((u) => u.checksumAddress); + expect(cohort.ursulaAddresses).toEqual(expectedUrsulas); }); - it('can export to JSON', async () => { - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - const config = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - const testCohort = await Cohort.create(config); - const configJSON = testCohort.toJSON(); - const expectedJSON = - '{"ursulaAddresses":["0x5cf1703a1c99a4b42eb056535840e93118177232","0x7fff551249d223f723557a96a0e1a469c79cc934","0x9c7c824239d3159327024459ad69bb215859bd25"],"threshold":2,"shares":3,"porterUri":"https://_this.should.crash"}'; - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(configJSON).toEqual(expectedJSON); + it('serializes to a plain object', async () => { + const cohort = await makeCohort(mockedUrsulas); + const asObj = cohort.toObj(); + const fromObj = Cohort.fromObj(asObj); + expect(fromObj).toEqual(cohort); }); - it('can import from JSON', async () => { - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - - const configJSON = - '{"ursulaAddresses":["0x5cf1703a1c99a4b42eb056535840e93118177232","0x7fff551249d223f723557a96a0e1a469c79cc934","0x9c7c824239d3159327024459ad69bb215859bd25"],"threshold":2,"shares":3,"porterUri":"https://_this.should.crash"}'; - const testCohort = Cohort.fromJSON(configJSON); - const expectedUrsulas = [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ]; - const expectedConfiguration = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(testCohort.ursulaAddresses).toEqual(expectedUrsulas); - expect(testCohort.configuration).toEqual(expectedConfiguration); + it('serializes to JSON', async () => { + const cohort = await makeCohort(mockedUrsulas); + const asJson = cohort.toJSON(); + const fromJson = Cohort.fromJSON(asJson); + expect(fromJson).toEqual(cohort); }); }); diff --git a/test/unit/conditions/base/condition.test.ts b/test/unit/conditions/base/condition.test.ts index af25c8dd8..0fe10d714 100644 --- a/test/unit/conditions/base/condition.test.ts +++ b/test/unit/conditions/base/condition.test.ts @@ -1,29 +1,41 @@ -import { ERC721Balance } from '../../../../src/conditions/predefined'; -import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from '../../testVariables'; +import { EvmCondition } from '../../../../src/conditions/base'; +import { + ERC721Balance, + ERC721Ownership, +} from '../../../../src/conditions/predefined'; +import { + TEST_CHAIN_ID, + TEST_CONTRACT_ADDR, + TEST_CONTRACT_ADDR_2, + testEvmConditionObj, +} from '../../testVariables'; describe('validation', () => { // TODO: Consider: // Use Condition here with returnTestValue schema // Refactor returnTestValue to be the part of the Condition const condition = new ERC721Balance(); - let result = condition.validate({ - contractAddress: TEST_CONTRACT_ADDR, - chain: TEST_CHAIN_ID, - }); it('accepts a correct schema', async () => { + const result = condition.validate({ + contractAddress: TEST_CONTRACT_ADDR, + chain: TEST_CHAIN_ID, + }); expect(result.error).toBeUndefined(); expect(result.value.contractAddress).toEqual(TEST_CONTRACT_ADDR); }); it('updates on a valid schema value', async () => { - result = condition.validate({ chain: TEST_CHAIN_ID }); + const result = condition.validate({ + chain: TEST_CHAIN_ID, + contractAddress: TEST_CONTRACT_ADDR_2, + }); expect(result.error).toBeUndefined(); expect(result.value.chain).toEqual(TEST_CHAIN_ID); }); it('rejects on an invalid schema value', async () => { - result = condition.validate({ + const result = condition.validate({ chain: -1, contractAddress: TEST_CONTRACT_ADDR, }); @@ -32,3 +44,18 @@ describe('validation', () => { ); }); }); + +describe('serialization', () => { + it('serializes to a plain object', () => { + const evm = new EvmCondition(testEvmConditionObj); + expect(evm.toObj()).toEqual(testEvmConditionObj); + }); + + it('serializes predefined conditions', () => { + const evm = new ERC721Ownership(testEvmConditionObj); + expect(evm.toObj()).toEqual({ + ...evm.defaults, + ...testEvmConditionObj, + }); + }); +}); diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts index ade286a4d..5dbdd3224 100644 --- a/test/unit/conditions/base/evm.test.ts +++ b/test/unit/conditions/base/evm.test.ts @@ -14,7 +14,10 @@ describe('validation', () => { contractAddress: undefined, }; const badEvm = new EvmCondition(badEvmCondition); - expect(() => badEvm.toObj()).toThrow('"contractAddress" is required'); + expect(() => badEvm.toObj()).toThrow( + 'Invalid condition: "contractAddress" is required' + ); + const { error } = badEvm.validate(badEvmCondition); expect(error?.message).toEqual('"contractAddress" is required'); }); diff --git a/test/unit/conditions/base/rpc.test.ts b/test/unit/conditions/base/rpc.test.ts index 3b8f3a652..4f50abbe8 100644 --- a/test/unit/conditions/base/rpc.test.ts +++ b/test/unit/conditions/base/rpc.test.ts @@ -16,7 +16,7 @@ describe('validation', () => { const badRpc = new RpcCondition(badRpcObj); expect(() => badRpc.toObj()).toThrow( - '"method" must be one of [eth_getBalance, balanceOf]' + 'Invalid condition: "method" must be one of [eth_getBalance, balanceOf]' ); const { error } = badRpc.validate(badRpcObj); diff --git a/test/unit/conditions/base/timelock.test.ts b/test/unit/conditions/base/timelock.test.ts index 5d04dc212..674b1700b 100644 --- a/test/unit/conditions/base/timelock.test.ts +++ b/test/unit/conditions/base/timelock.test.ts @@ -28,7 +28,7 @@ describe('validation', () => { const badTimelock = new TimelockCondition(badTimelockObj); expect(() => badTimelock.toObj()).toThrow( - '"returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' + 'Invalid condition: "returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' ); const { error } = badTimelock.validate(badTimelockObj); diff --git a/test/unit/strategy.test.ts b/test/unit/strategy.test.ts index 5881493c3..18b6bd0e0 100644 --- a/test/unit/strategy.test.ts +++ b/test/unit/strategy.test.ts @@ -5,7 +5,6 @@ import { } from '@nucypher/nucypher-core'; import { - Cohort, conditions, DeployedStrategy, Strategy, @@ -16,6 +15,7 @@ import { fromBase64, toBytes } from '../../src/utils'; import { fakeUrsulas, fakeWeb3Provider, + makeCohort, mockEncryptTreasureMap, mockGenerateKFrags, mockGetUrsulas, @@ -27,10 +27,8 @@ import { import { aliceSecretKeyBytes, bobSecretKeyBytes, - decrypterJSON, deployedStrategyJSON, encryptedTreasureMapBase64, - strategyJSON, } from './testVariables'; const { @@ -38,106 +36,93 @@ const { ConditionSet, } = conditions; -describe('Strategy', () => { - const cohortConfig = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); - const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - Date.now = jest.fn(() => 1487076708000); +// Shared test variables +const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); +const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); +const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); +Date.now = jest.fn(() => 1487076708000); +const ownsNFT = new ERC721Ownership({ + contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', + parameters: [3591], + chain: 5, +}); +const conditionSet = new ConditionSet([ownsNFT]); +const mockedUrsulas = fakeUrsulas().slice(0, 3); +describe('Strategy', () => { afterEach(() => { jest.restoreAllMocks(); }); - it('can create Strategy from configuration', async () => { - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = Strategy.create( - testCohort, - undefined, + it('creates a Strategy', async () => { + const cohort = await makeCohort(mockedUrsulas); + const strategy = Strategy.create( + cohort, + conditionSet, aliceSecretKey, bobSecretKey ); + expect(strategy.cohort).toEqual(cohort); + }); - const expectedUrsulas = mockedUrsulas.map( - (ursula) => ursula.checksumAddress + it('serializes to plain object', async () => { + const cohort = await makeCohort(mockedUrsulas); + const strategy = Strategy.create( + cohort, + conditionSet, + aliceSecretKey, + bobSecretKey ); - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(testStrategy.cohort.ursulaAddresses).toEqual(expectedUrsulas); + const asObject = strategy.toObj(); + const fromObject = Strategy.fromObj(asObject); + expect(fromObject.equals(strategy)).toBeTruthy(); }); - it('can export to JSON', async () => { - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = Strategy.create( - testCohort, - undefined, + it('serializes to JSON', async () => { + const cohort = await makeCohort(mockedUrsulas); + const strategy = Strategy.create( + cohort, + conditionSet, aliceSecretKey, - bobSecretKey, - new Date('2017-02-14T12:51:48.000Z'), - new Date('2123-03-16T12:51:48.000Z') + bobSecretKey ); - const configJSON = testStrategy.toJSON(); - expect(configJSON).toEqual(strategyJSON); - expect(getUrsulasSpy).toHaveBeenCalled(); - }); - - it('can import from JSON', async () => { - const testStrategy = Strategy.fromJSON(strategyJSON); - const expectedUrsulas = [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ]; - expect(testStrategy.cohort.ursulaAddresses).toEqual(expectedUrsulas); + const asJson = strategy.toJSON(); + const fromJSON = Strategy.fromJSON(asJson); + expect(fromJSON.equals(strategy)).toBeTruthy(); }); - it('can deploy and return DeployedStrategy', async () => { + it('deploys strategy', async () => { const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); const makeTreasureMapSpy = mockMakeTreasureMap(); const encryptTreasureMapSpy = mockEncryptTreasureMap(); - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = Strategy.create(testCohort, undefined, aliceSecretKey); - const testDeployed = await testStrategy.deploy('test', aliceProvider); - expect(getUrsulasSpy).toHaveBeenCalled(); + const cohort = await makeCohort(mockedUrsulas); + const strategy = Strategy.create(cohort, conditionSet, aliceSecretKey); + const label = 'test'; + + const deployedStrategy = await strategy.deploy(label, aliceProvider); expect(generateKFragsSpy).toHaveBeenCalled(); expect(publishToBlockchainSpy).toHaveBeenCalled(); expect(encryptTreasureMapSpy).toHaveBeenCalled(); expect(makeTreasureMapSpy).toHaveBeenCalled(); - expect(testDeployed.label).toEqual('test'); + expect(deployedStrategy.cohort).toEqual(cohort); + expect(deployedStrategy.conditionSet).toEqual(conditionSet); + expect(deployedStrategy.label).toEqual(label); }); }); describe('Deployed Strategy', () => { - const cohortConfig = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); - afterEach(() => { jest.restoreAllMocks(); }); - it('can export to JSON', async () => { + it('serializes to JSON', async () => { const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); const makeTreasureMapSpy = mockMakeTreasureMap(); @@ -145,33 +130,30 @@ describe('Deployed Strategy', () => { EncryptedTreasureMap.fromBytes(fromBase64(encryptedTreasureMapBase64)) ); - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = Strategy.create( - testCohort, - undefined, + const cohort = await makeCohort(mockedUrsulas); + const strategy = Strategy.create( + cohort, + conditionSet, aliceSecretKey, - bobSecretKey, - new Date('2017-02-14T12:51:48.000Z'), - new Date('2123-03-16T12:51:48.000Z') + bobSecretKey ); - const testDeployed = await testStrategy.deploy('test', aliceProvider); - expect(getUrsulasSpy).toHaveBeenCalled(); + + const strategyAsJson = strategy.toJSON(); + const strategyFromJson = Strategy.fromJSON(strategyAsJson); + expect(strategyFromJson.equals(strategy)).toBeTruthy(); + + const deployedStrategy = await strategy.deploy('test', aliceProvider); expect(generateKFragsSpy).toHaveBeenCalled(); expect(publishToBlockchainSpy).toHaveBeenCalled(); expect(makeTreasureMapSpy).toHaveBeenCalled(); expect(encryptTreasureMapSpy).toHaveBeenCalled(); - const configJSON = testDeployed.toJSON(); - expect(configJSON).toEqual(deployedStrategyJSON); - }); - - it('can import from JSON', async () => { - const importedStrategy = DeployedStrategy.fromJSON(deployedStrategyJSON); - const configJSON = importedStrategy.toJSON(); - expect(configJSON).toEqual(deployedStrategyJSON); + const asJson = deployedStrategy.toJSON(); + const fromJson = DeployedStrategy.fromJSON(asJson); + expect(fromJson.equals(deployedStrategy)).toBeTruthy(); }); - it('can encrypt and decrypt', async () => { + it('encrypts and decrypts', async () => { const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); const bobProvider = fakeWeb3Provider(bobSecretKey.toBEBytes()); const mockedUrsulas = fakeUrsulas().slice(0, 3); @@ -181,14 +163,14 @@ describe('Deployed Strategy', () => { const makeTreasureMapSpy = mockMakeTreasureMap(); const encryptTreasureMapSpy = mockEncryptTreasureMap(); - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = Strategy.create( - testCohort, - undefined, + const cohort = await makeCohort(mockedUrsulas); + const strategy = Strategy.create( + cohort, + conditionSet, aliceSecretKey, bobSecretKey ); - const testDeployed = await testStrategy.deploy('test', aliceProvider); + const deployedStrategy = await strategy.deploy('test', aliceProvider); expect(getUrsulasSpy).toHaveBeenCalled(); expect(generateKFragsSpy).toHaveBeenCalled(); @@ -196,16 +178,11 @@ describe('Deployed Strategy', () => { expect(encryptTreasureMapSpy).toHaveBeenCalled(); expect(makeTreasureMapSpy).toHaveBeenCalled(); - const encrypter = testDeployed.encrypter; - const decrypter = testDeployed.decrypter; + const encrypter = deployedStrategy.encrypter; + const decrypter = deployedStrategy.decrypter; - const ownsNFT = new ERC721Ownership({ - contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', - parameters: [3591], - chain: 5, - }); const plaintext = 'this is a secret'; - encrypter.conditions = new ConditionSet([ownsNFT]); + encrypter.conditions = conditionSet; const encryptedMessageKit = encrypter.encryptMessage(plaintext); // Setup mocks for `retrieveAndDecrypt` @@ -228,20 +205,19 @@ describe('Deployed Strategy', () => { expect(getUrsulasSpy2).toHaveBeenCalled(); expect(retrieveCFragsSpy).toHaveBeenCalled(); expect(decryptedMessage[0]).toEqual(toBytes(plaintext)); + + const serialized = deployedStrategy.toJSON(); + const deserialized = DeployedStrategy.fromJSON(serialized); + expect(deserialized.equals(deployedStrategy)).toBeTruthy(); }); }); describe('tDecDecrypter', () => { - const importedStrategy = DeployedStrategy.fromJSON(deployedStrategyJSON); - - it('can export to JSON', () => { - const configJSON = importedStrategy.decrypter.toJSON(); - expect(configJSON).toEqual(decrypterJSON); - }); + const decrypter = DeployedStrategy.fromJSON(deployedStrategyJSON).decrypter; - it('can import from JSON', () => { - const decrypter = tDecDecrypter.fromJSON(decrypterJSON); - const configJSON = decrypter.toJSON(); - expect(configJSON).toEqual(decrypterJSON); + it('serializes to JSON', () => { + const asJson = decrypter.toJSON(); + const fromJson = tDecDecrypter.fromJSON(asJson); + expect(fromJson.equals(decrypter)).toBeTruthy(); }); }); diff --git a/test/utils.ts b/test/utils.ts index b735af163..a0299b502 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -31,7 +31,7 @@ import { ValidatorMessage, } from 'ferveo-wasm'; -import { Alice, Bob, Configuration, RemoteBob } from '../src'; +import { Alice, Bob, Cohort, Configuration, RemoteBob } from '../src'; import { DkgParticipant, DkgRitual } from '../src/agents/coordinator'; import { GetUrsulasResponse, @@ -43,13 +43,13 @@ import { BlockchainPolicy, PreEnactedPolicy } from '../src/policies/policy'; import { ChecksumAddress } from '../src/types'; import { toBytes, toHexString, zip } from '../src/utils'; -export const fromBytes = (bytes: Uint8Array): string => - new TextDecoder().decode(bytes); - export const bytesEqual = (first: Uint8Array, second: Uint8Array): boolean => first.length === second.length && first.every((value, index) => value === second[index]); +export const fromBytes = (bytes: Uint8Array): string => + new TextDecoder().decode(bytes); + const mockConfig: Configuration = { porterUri: 'https://_this_should_crash.com/', }; @@ -273,11 +273,11 @@ const fakeDkgRitualE2e = (variant: 'precomputed' | 'simple') => { const receivedMessages = messages.slice(0, threshold); const serverAggregate = dkg.aggregateTranscript(receivedMessages); - expect(serverAggregate.verify(sharesNum, receivedMessages)).toBe(true); + expect(serverAggregate.verify(sharesNum, receivedMessages)).toBeTruthy(); // Client can also aggregate the transcripts and verify them const clientAggregate = new AggregatedTranscript(receivedMessages); - expect(clientAggregate.verify(sharesNum, receivedMessages)).toBe(true); + expect(clientAggregate.verify(sharesNum, receivedMessages)).toBeTruthy(); // In the meantime, the client creates a ciphertext and decryption request const msg = Buffer.from('my-msg'); @@ -388,3 +388,15 @@ export const fakeDkgParticipants = (): DkgParticipant[] => { }; }); }; + +export const makeCohort = async (ursulas: Ursula[]) => { + const getUrsulasSpy = mockGetUrsulas(ursulas); + const config = { + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }; + const cohort = await Cohort.create(config); + expect(getUrsulasSpy).toHaveBeenCalled(); + return cohort; +}; diff --git a/yarn.lock b/yarn.lock index 4df504fb2..95d1fd5b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1780,6 +1780,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/deep-equal@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.1.tgz#71cfabb247c22bcc16d536111f50c0ed12476b03" + integrity sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg== + "@types/graceful-fs@^4.1.2": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" @@ -2921,6 +2926,30 @@ dedent@0.7.0, dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +deep-equal@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.1.tgz#c72ab22f3a7d3503a4ca87dde976fe9978816739" + integrity sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.0" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.0" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + deep-extend@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -3116,6 +3145,21 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.9" +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -4124,7 +4168,7 @@ inquirer@8.2.5: through "^2.3.6" wrap-ansi "^7.0.0" -internal-slot@^1.0.5: +internal-slot@^1.0.4, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== @@ -4133,6 +4177,14 @@ internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" @@ -4174,7 +4226,7 @@ is-core-module@^2.11.0, is-core-module@^2.5.0: dependencies: has "^1.0.3" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -4213,6 +4265,11 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -4253,6 +4310,11 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -4312,6 +4374,11 @@ is-utf8@^0.2.1: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -4319,6 +4386,14 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-windows@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4331,6 +4406,11 @@ is-wsl@^2.1.1: dependencies: is-docker "^2.0.0" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -5391,6 +5471,14 @@ object-inspect@^1.12.3, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5876,7 +5964,7 @@ regenerator-transform@^0.15.1: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== @@ -6223,6 +6311,13 @@ standard-version@^9.0.0: stringify-package "^1.0.1" yargs "^16.0.0" +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + string-format@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" @@ -6942,6 +7037,16 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" From 76ac0f1ae56ffcf9bb8410f015f52e3af6c98af1 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 30 May 2023 10:44:44 +0200 Subject: [PATCH 39/98] feat: add class metadata to serialized conditions --- src/conditions/base/condition.ts | 6 +++++- test/unit/conditions/base/condition.test.ts | 6 +++++- test/unit/conditions/base/evm.test.ts | 15 ++++++++++++--- test/unit/conditions/base/rpc.test.ts | 5 ++++- test/unit/conditions/base/timelock.test.ts | 1 + 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/conditions/base/condition.ts b/src/conditions/base/condition.ts index ee9fa4d04..16c8c95ec 100644 --- a/src/conditions/base/condition.ts +++ b/src/conditions/base/condition.ts @@ -24,7 +24,10 @@ export class Condition { if (error) { throw `Invalid condition: ${error.message}`; } - return value; + return { + ...value, + _class: this.constructor.name, + }; } public static fromObj( @@ -33,6 +36,7 @@ export class Condition { this: new (...args: any[]) => T, obj: Map ): T { + delete obj._class; return new this(obj); } diff --git a/test/unit/conditions/base/condition.test.ts b/test/unit/conditions/base/condition.test.ts index 0fe10d714..a38798744 100644 --- a/test/unit/conditions/base/condition.test.ts +++ b/test/unit/conditions/base/condition.test.ts @@ -48,7 +48,10 @@ describe('validation', () => { describe('serialization', () => { it('serializes to a plain object', () => { const evm = new EvmCondition(testEvmConditionObj); - expect(evm.toObj()).toEqual(testEvmConditionObj); + expect(evm.toObj()).toEqual({ + ...testEvmConditionObj, + _class: 'EvmCondition', + }); }); it('serializes predefined conditions', () => { @@ -56,6 +59,7 @@ describe('serialization', () => { expect(evm.toObj()).toEqual({ ...evm.defaults, ...testEvmConditionObj, + _class: 'ERC721Ownership', }); }); }); diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts index 5dbdd3224..5a35efa93 100644 --- a/test/unit/conditions/base/evm.test.ts +++ b/test/unit/conditions/base/evm.test.ts @@ -4,7 +4,10 @@ import { testEvmConditionObj } from '../../testVariables'; describe('validation', () => { it('accepts on a valid schema', () => { const evm = new EvmCondition(testEvmConditionObj); - expect(evm.toObj()).toEqual(testEvmConditionObj); + expect(evm.toObj()).toEqual({ + ...testEvmConditionObj, + _class: 'EvmCondition', + }); }); it('rejects an invalid schema', () => { @@ -34,7 +37,10 @@ describe('accepts either standardContractType or functionAbi but not both or non functionAbi: undefined, }; const evmCondition = new EvmCondition(conditionObj); - expect(evmCondition.toObj()).toEqual(conditionObj); + expect(evmCondition.toObj()).toEqual({ + ...conditionObj, + _class: 'EvmCondition', + }); }); it('accepts functionAbi', () => { @@ -44,7 +50,10 @@ describe('accepts either standardContractType or functionAbi but not both or non standardContractType: undefined, }; const evmCondition = new EvmCondition(conditionObj); - expect(evmCondition.toObj()).toEqual(conditionObj); + expect(evmCondition.toObj()).toEqual({ + ...conditionObj, + _class: 'EvmCondition', + }); }); it('rejects both', () => { diff --git a/test/unit/conditions/base/rpc.test.ts b/test/unit/conditions/base/rpc.test.ts index 4f50abbe8..8803854cf 100644 --- a/test/unit/conditions/base/rpc.test.ts +++ b/test/unit/conditions/base/rpc.test.ts @@ -4,7 +4,10 @@ import { testRpcConditionObj } from '../../testVariables'; describe('validation', () => { it('accepts on a valid schema', () => { const rpc = new RpcCondition(testRpcConditionObj); - expect(rpc.toObj()).toEqual(testRpcConditionObj); + expect(rpc.toObj()).toEqual({ + ...testRpcConditionObj, + _class: 'RpcCondition', + }); }); it('rejects an invalid schema', () => { diff --git a/test/unit/conditions/base/timelock.test.ts b/test/unit/conditions/base/timelock.test.ts index 674b1700b..b0ecdc3ca 100644 --- a/test/unit/conditions/base/timelock.test.ts +++ b/test/unit/conditions/base/timelock.test.ts @@ -14,6 +14,7 @@ describe('validation', () => { expect(timelock.toObj()).toEqual({ returnValueTest, method: 'timelock', + _class: 'TimelockCondition', }); }); From b6eb53e1422c099d8c663ea5a97d82c67bf41d36 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 17 May 2023 14:50:08 +0200 Subject: [PATCH 40/98] feat: add tdec endpoint to porter --- src/characters/porter.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 2aa1d7b1c..4b67b99fd 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -2,7 +2,9 @@ import { CapsuleFrag, PublicKey, RetrievalKit, - TreasureMap, + ThresholdDecryptionRequest, + ThresholdDecryptionResponse, + TreasureMap } from '@nucypher/nucypher-core'; import axios, { AxiosResponse } from 'axios'; import qs from 'qs'; @@ -64,6 +66,13 @@ export type RetrieveCFragsResponse = { errors: Record; }; + +type PostDecryptRequest = Uint8Array; + +type PostDecryptResult = Uint8Array; + + + export class Porter { readonly porterUrl: URL; @@ -132,4 +141,15 @@ export class Porter { return { cFrags, errors }; }); } + + public async decrypt( + tDecRequest: ThresholdDecryptionRequest + ): Promise { + const data: PostDecryptRequest = tDecRequest.toBytes(); + const resp: AxiosResponse = await axios.post( + new URL('/decrypt', this.porterUrl).toString(), + data + ); + return ThresholdDecryptionResponse.fromBytes(resp.data); + } } From cb5068727961f98eb7f0187d0f315baf772ffa9f Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 18 May 2023 12:53:35 +0200 Subject: [PATCH 41/98] sketch a cbd flow with dkg --- examples/api-example.ts | 2 +- src/agents/coordinator.ts | 4 +- src/characters/cbd-universal-bob.ts | 165 ++++++++++++++ src/characters/enrico.ts | 47 +++- src/characters/porter.ts | 12 +- ...{universal-bob.ts => pre-universal-bob.ts} | 12 +- src/dkg.ts | 56 ++++- src/index.ts | 4 +- src/sdk/strategy/cbd-strategy.ts | 160 ++++++++++++++ .../{strategy.ts => strategy/pre-strategy.ts} | 41 ++-- test/acceptance/alice-grants.test.ts | 2 +- test/docs/cbd.test.ts | 6 +- test/integration/dkg-client.test.ts | 15 +- test/integration/enrico.test.ts | 8 +- test/integration/pre.test.ts | 4 +- test/unit/cbd-strategy.test.ts | 209 ++++++++++++++++++ ...{strategy.test.ts => pre-strategy.test.ts} | 50 +++-- test/utils.ts | 46 +++- 18 files changed, 744 insertions(+), 99 deletions(-) create mode 100644 src/characters/cbd-universal-bob.ts rename src/characters/{universal-bob.ts => pre-universal-bob.ts} (96%) create mode 100644 src/sdk/strategy/cbd-strategy.ts rename src/sdk/{strategy.ts => strategy/pre-strategy.ts} (88%) create mode 100644 test/unit/cbd-strategy.test.ts rename test/unit/{strategy.test.ts => pre-strategy.test.ts} (83%) diff --git a/examples/api-example.ts b/examples/api-example.ts index 0df37e9ea..6ea4909d4 100644 --- a/examples/api-example.ts +++ b/examples/api-example.ts @@ -26,7 +26,7 @@ async function example() { // Enrico encrypts data on behalf of Alice const enrico = new Enrico(policy.policyKey); const message = 'secret-message-from-alice'; - const encryptedMessage = enrico.encryptMessage(toBytes(message)); + const encryptedMessage = enrico.encryptMessagePre(toBytes(message)); // Bob retrieves & decrypts encrypted message const bob = fakeBob(); diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts index 159492ff9..f6fca8176 100644 --- a/src/agents/coordinator.ts +++ b/src/agents/coordinator.ts @@ -7,7 +7,7 @@ import { import { getContract } from './contracts'; -export interface DkgRitual { +export interface CoordinatorRitual { id: number; initiator: string; dkgSize: number; @@ -43,7 +43,7 @@ export class DkgCoordinatorAgent { public static async getRitual( provider: ethers.providers.Provider, ritualId: number - ): Promise { + ): Promise { const Coordinator = await this.connectReadOnly(provider); return await Coordinator.rituals(ritualId); } diff --git a/src/characters/cbd-universal-bob.ts b/src/characters/cbd-universal-bob.ts new file mode 100644 index 000000000..2a1f96fc9 --- /dev/null +++ b/src/characters/cbd-universal-bob.ts @@ -0,0 +1,165 @@ +import { + Context, + MessageKit, + PublicKey, + SecretKey, + Signer, + ThresholdDecryptionRequest, +} from '@nucypher/nucypher-core'; +import { ethers } from 'ethers'; +import { + combineDecryptionSharesPrecomputed, + combineDecryptionSharesSimple, + DecryptionSharePrecomputed, + DecryptionShareSimple, + decryptWithSharedSecret, + DkgPublicKey, +} from 'ferveo-wasm'; +import { SharedSecret } from 'ferveo-wasm'; +import { Ciphertext } from 'ferveo-wasm'; + +import { ConditionSet } from '../conditions'; +import { DkgRitual, FerveoVariant } from '../dkg'; +import { Keyring } from '../keyring'; +import { PolicyMessageKit } from '../kits/message'; +import { base64ToU8Receiver, u8ToBase64Replacer } from '../utils'; + +import { Porter } from './porter'; + +type CbdTDecDecrypterJSON = { + porterUri: string; + encryptingKeyBytes: Uint8Array; + bobSecretKeyBytes: Uint8Array; +}; + +export class CbdTDecDecrypter { + private readonly porter: Porter; + private readonly keyring: Keyring; + + // private readonly verifyingKey: Keyring; + + constructor( + porterUri: string, + private readonly dkgPublicKey: DkgPublicKey, + secretKey: SecretKey + ) { + this.porter = new Porter(porterUri); + this.keyring = new Keyring(secretKey); + } + + public get decryptingKey(): PublicKey { + return this.keyring.publicKey; + } + + public get signer(): Signer { + return this.keyring.signer; + } + + public decrypt(messageKit: MessageKit | PolicyMessageKit): Uint8Array { + return this.keyring.decrypt(messageKit); + } + + public async retrieveAndDecrypt( + provider: ethers.providers.Web3Provider, + conditionSet: ConditionSet, + dkgRitual: DkgRitual, + variant: number, + ciphertext: Ciphertext, + aad: Uint8Array + ): Promise { + const decryptionShares = await this.retrieve( + provider, + conditionSet, + dkgRitual.id, + variant, + ciphertext + ); + + // TODO: Replace with a factory method + let sharedSecret: SharedSecret; + if (variant === FerveoVariant.Simple) { + sharedSecret = combineDecryptionSharesSimple( + decryptionShares as DecryptionShareSimple[], + dkgRitual.dkgPublicParams + ); + } else if (variant === FerveoVariant.Precomputed) { + sharedSecret = combineDecryptionSharesPrecomputed( + decryptionShares as DecryptionSharePrecomputed[] + ); + } else { + throw new Error(`Unknown variant ${variant}`); + } + + const plaintext = decryptWithSharedSecret( + ciphertext, + aad, + sharedSecret, + dkgRitual.dkgPublicParams + ); + return [plaintext]; + } + + public async retrieve( + provider: ethers.providers.Web3Provider, + conditionSet: ConditionSet, + ritualId: number, + variant: number, + ciphertext: Ciphertext + ): Promise { + const contextStr = await conditionSet.buildContext(provider).toJson(); + + // TODO: Move ThresholdDecryptionRequest creation and parsing to Porter? + const tDecRequest = new ThresholdDecryptionRequest( + ritualId, + variant, + ciphertext.toBytes(), + conditionSet.toWASMConditions(), + new Context(contextStr) + ); + + // TODO: This should return multiple responses + const resp = await this.porter.decrypt(tDecRequest); + + // TODO: Replace with a factory method + if (variant === FerveoVariant.Simple) { + return resp.map((r) => + DecryptionShareSimple.fromBytes(r.decryptionShare) + ); + } else if (variant === FerveoVariant.Precomputed) { + return resp.map((r) => + DecryptionSharePrecomputed.fromBytes(r.decryptionShare) + ); + } else { + throw new Error(`Unknown variant ${variant}`); + } + } + + public toObj(): CbdTDecDecrypterJSON { + return { + porterUri: this.porter.porterUrl.toString(), + encryptingKeyBytes: this.dkgPublicKey.toBytes(), + bobSecretKeyBytes: this.keyring.secretKey.toBEBytes(), + }; + } + + public toJSON(): string { + return JSON.stringify(this.toObj(), u8ToBase64Replacer); + } + + private static fromObj({ + porterUri, + encryptingKeyBytes, + bobSecretKeyBytes, + }: CbdTDecDecrypterJSON) { + return new CbdTDecDecrypter( + porterUri, + DkgPublicKey.fromBytes(encryptingKeyBytes), + SecretKey.fromBEBytes(bobSecretKeyBytes) + ); + } + + public static fromJSON(json: string) { + const config = JSON.parse(json, base64ToU8Receiver); + return CbdTDecDecrypter.fromObj(config); + } +} diff --git a/src/characters/enrico.ts b/src/characters/enrico.ts index cd8bed5bc..488b6ae33 100644 --- a/src/characters/enrico.ts +++ b/src/characters/enrico.ts @@ -1,20 +1,21 @@ import { MessageKit, PublicKey, SecretKey } from '@nucypher/nucypher-core'; +import { Ciphertext, DkgPublicKey, encrypt } from 'ferveo-wasm'; -import { ConditionSet } from '../conditions/condition-set'; +import { ConditionSet } from '../conditions'; import { Keyring } from '../keyring'; import { toBytes } from '../utils'; export class Enrico { - public readonly policyEncryptingKey: PublicKey; + public readonly encryptingKey: PublicKey | DkgPublicKey; private readonly keyring: Keyring; public conditions?: ConditionSet; constructor( - policyEncryptingKey: PublicKey, + encryptingKey: PublicKey | DkgPublicKey, verifyingKey?: SecretKey, conditions?: ConditionSet ) { - this.policyEncryptingKey = policyEncryptingKey; + this.encryptingKey = encryptingKey; this.keyring = new Keyring(verifyingKey ?? SecretKey.random()); this.conditions = conditions; } @@ -23,17 +24,43 @@ export class Enrico { return this.keyring.publicKey; } - public encryptMessage( + public encryptMessagePre( plaintext: Uint8Array | string, - currentConditions?: ConditionSet + withConditions?: ConditionSet ): MessageKit { - if (!currentConditions) { - currentConditions = this.conditions; + if (!withConditions) { + withConditions = this.conditions; } + + if (!(this.encryptingKey instanceof PublicKey)) { + throw new Error('Wrong key type. Use encryptMessageCbd instead.'); + } + return new MessageKit( - this.policyEncryptingKey, + this.encryptingKey, + plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), + withConditions ? withConditions.toWASMConditions() : null + ); + } + + public encryptMessageCbd( + plaintext: Uint8Array | string, + withConditions?: ConditionSet + ): { ciphertext: Ciphertext; aad: Uint8Array } { + if (!withConditions) { + withConditions = this.conditions; + } + + if (!(this.encryptingKey instanceof DkgPublicKey)) { + throw new Error('Wrong key type. Use encryptMessagePre instead.'); + } + + const aad = toBytes(withConditions?.toJson() ?? ''); + const ciphertext = encrypt( plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), - currentConditions ? currentConditions.toWASMConditions() : null + aad, + this.encryptingKey ); + return { ciphertext, aad }; } } diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 4b67b99fd..47f403a8e 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -4,7 +4,7 @@ import { RetrievalKit, ThresholdDecryptionRequest, ThresholdDecryptionResponse, - TreasureMap + TreasureMap, } from '@nucypher/nucypher-core'; import axios, { AxiosResponse } from 'axios'; import qs from 'qs'; @@ -66,13 +66,10 @@ export type RetrieveCFragsResponse = { errors: Record; }; - -type PostDecryptRequest = Uint8Array; +type PostDecryptRequest = Uint8Array; type PostDecryptResult = Uint8Array; - - export class Porter { readonly porterUrl: URL; @@ -144,12 +141,13 @@ export class Porter { public async decrypt( tDecRequest: ThresholdDecryptionRequest - ): Promise { + ): Promise { const data: PostDecryptRequest = tDecRequest.toBytes(); const resp: AxiosResponse = await axios.post( new URL('/decrypt', this.porterUrl).toString(), data ); - return ThresholdDecryptionResponse.fromBytes(resp.data); + // TODO: In /cbd_decrypt, the response is a list of ThresholdDecryptionResponse + return [ThresholdDecryptionResponse.fromBytes(resp.data)]; } } diff --git a/src/characters/universal-bob.ts b/src/characters/pre-universal-bob.ts similarity index 96% rename from src/characters/universal-bob.ts rename to src/characters/pre-universal-bob.ts index 5f4182cd7..01b7d30ab 100644 --- a/src/characters/universal-bob.ts +++ b/src/characters/pre-universal-bob.ts @@ -16,7 +16,7 @@ import { base64ToU8Receiver, bytesEquals, toJson, zip } from '../utils'; import { Porter } from './porter'; -type decrypterJSON = { +type PreTDecDecrypterJSON = { porterUri: string; policyEncryptingKeyBytes: Uint8Array; encryptedTreasureMapBytes: Uint8Array; @@ -24,7 +24,7 @@ type decrypterJSON = { bobSecretKeyBytes: Uint8Array; }; -export class tDecDecrypter { +export class PreTDecDecrypter { private readonly porter: Porter; private readonly keyring: Keyring; @@ -138,7 +138,7 @@ export class tDecDecrypter { }); } - public toObj(): decrypterJSON { + public toObj(): PreTDecDecrypterJSON { return { porterUri: this.porter.porterUrl.toString(), policyEncryptingKeyBytes: this.policyEncryptingKey.toCompressedBytes(), @@ -159,8 +159,8 @@ export class tDecDecrypter { encryptedTreasureMapBytes, publisherVerifyingKeyBytes, bobSecretKeyBytes, - }: decrypterJSON) { - return new tDecDecrypter( + }: PreTDecDecrypterJSON) { + return new PreTDecDecrypter( porterUri, PublicKey.fromCompressedBytes(policyEncryptingKeyBytes), EncryptedTreasureMap.fromBytes(encryptedTreasureMapBytes), @@ -171,7 +171,7 @@ export class tDecDecrypter { public static fromJSON(json: string) { const config = JSON.parse(json, base64ToU8Receiver); - return tDecDecrypter.fromObj(config); + return PreTDecDecrypter.fromObj(config); } public equals(other: tDecDecrypter): boolean { diff --git a/src/dkg.ts b/src/dkg.ts index 1230babd5..ece1d66ef 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -1,6 +1,8 @@ import { ethers } from 'ethers'; import { AggregatedTranscript, + DkgPublicKey, + DkgPublicParameters, EthereumAddress, PublicKey as FerveoPublicKey, Transcript, @@ -11,20 +13,58 @@ import { import { DkgCoordinatorAgent } from './agents/coordinator'; import { fromHexString } from './utils'; -export class DkgClient { +// TOOD: Move to nucypher-core +export enum FerveoVariant { + Simple = 0, + Precomputed = 1, +} + +export interface DkgRitualJSON { + id: number; + dkgPublicKey: Uint8Array; + dkgPublicParams: Uint8Array; +} + +export class DkgRitual { constructor( - private readonly provider: ethers.providers.Web3Provider, - public readonly ritualId: number + public readonly id: number, + public readonly dkgPublicKey: DkgPublicKey, + public readonly dkgPublicParams: DkgPublicParameters ) {} - public async verifyRitual(): Promise { - const ritual = await DkgCoordinatorAgent.getRitual( - this.provider, - this.ritualId + public toObj(): DkgRitualJSON { + return { + id: this.id, + dkgPublicKey: this.dkgPublicKey.toBytes(), + dkgPublicParams: this.dkgPublicParams.toBytes(), + }; + } + + public static fromObj(json: DkgRitualJSON): DkgRitual { + return new DkgRitual( + json.id, + DkgPublicKey.fromBytes(json.dkgPublicKey), + DkgPublicParameters.fromBytes(json.dkgPublicParams) ); + } +} + +export class DkgClient { + constructor(private readonly provider: ethers.providers.Web3Provider) {} + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async initializeRitual( + _provider: ethers.providers.Web3Provider, + _ritualParams: unknown + ): Promise { + // TODO: Create a new DKG ritual here + throw new Error('Not implemented'); + } + public async verifyRitual(ritualId: number): Promise { + const ritual = await DkgCoordinatorAgent.getRitual(this.provider, ritualId); const participants = await DkgCoordinatorAgent.getParticipants( this.provider, - this.ritualId + ritualId ); const validatorMessages = participants.map((p) => { diff --git a/src/index.ts b/src/index.ts index 3087508d6..3ce372dbd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ export { Alice } from './characters/alice'; export { Bob, RemoteBob } from './characters/bob'; export { Enrico } from './characters/enrico'; -export { tDecDecrypter } from './characters/universal-bob'; +export { PreTDecDecrypter } from './characters/pre-universal-bob'; export { Porter } from './characters/porter'; // Policies @@ -30,7 +30,7 @@ export { conditions, CustomContextParam }; // SDK export { Cohort } from './sdk/cohort'; -export { DeployedStrategy, Strategy } from './sdk/strategy'; +export { DeployedPreStrategy, PreStrategy } from './sdk/strategy/pre-strategy'; // Re-exports export { diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts new file mode 100644 index 000000000..6f60b5a9f --- /dev/null +++ b/src/sdk/strategy/cbd-strategy.ts @@ -0,0 +1,160 @@ +import { SecretKey } from '@nucypher/nucypher-core'; +import { ethers } from 'ethers'; + +import { CbdTDecDecrypter } from '../../characters/cbd-universal-bob'; +import { Enrico } from '../../characters/enrico'; +import { ConditionSet } from '../../conditions'; +import { DkgClient, DkgRitual, DkgRitualJSON } from '../../dkg'; +import { base64ToU8Receiver, u8ToBase64Replacer } from '../../utils'; +import { Cohort, CohortJSON } from '../cohort'; + +type StrategyJSON = { + cohort: CohortJSON; + bobSecretKeyBytes: Uint8Array; + conditionSet?: ConditionSet; +}; + +type DeployedStrategyJSON = { + dkgRitual: DkgRitualJSON; + cohortConfig: CohortJSON; + bobSecretKeyBytes: Uint8Array; + conditionSet?: ConditionSet; +}; + +export class CbdStrategy { + private constructor( + public readonly cohort: Cohort, + private readonly bobSecretKey: SecretKey, + private readonly conditionSet?: ConditionSet + ) {} + + public static create( + cohort: Cohort, + conditionSet?: ConditionSet, + bobSecretKey?: SecretKey + ) { + if (!bobSecretKey) { + bobSecretKey = SecretKey.random(); + } + return new CbdStrategy(cohort, bobSecretKey, conditionSet); + } + + public async deploy( + provider: ethers.providers.Web3Provider + ): Promise { + const dkgRitualParams = { + threshold: this.cohort.configuration.threshold, + shares: this.cohort.configuration.shares, + }; + const dkgClient = new DkgClient(provider); + const dkgRitual = await dkgClient.initializeRitual( + provider, + dkgRitualParams + ); + + const encrypter = new Enrico( + dkgRitual.dkgPublicKey, + undefined, + this.conditionSet + ); + + const decrypter = new CbdTDecDecrypter( + this.cohort.configuration.porterUri, + dkgRitual.dkgPublicKey, + this.bobSecretKey + ); + + return new DeployedCbdStrategy( + this.cohort, + dkgRitual, + encrypter, + decrypter, + this.bobSecretKey, + this.conditionSet + ); + } + + public static fromJSON(json: string) { + const config = JSON.parse(json, base64ToU8Receiver); + return CbdStrategy.fromObj(config); + } + + public toJSON() { + return JSON.stringify(this.toObj(), u8ToBase64Replacer); + } + + private static fromObj({ + cohort, + bobSecretKeyBytes, + conditionSet, + }: StrategyJSON) { + return new CbdStrategy( + Cohort.fromObj(cohort), + SecretKey.fromBEBytes(bobSecretKeyBytes), + conditionSet + ); + } + + public toObj(): StrategyJSON { + return { + cohort: this.cohort.toObj(), + bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), + conditionSet: this.conditionSet, + }; + } +} + +export class DeployedCbdStrategy { + constructor( + public cohort: Cohort, + public dkgRitual: DkgRitual, + public encrypter: Enrico, + public decrypter: CbdTDecDecrypter, + private bobSecretKey: SecretKey, + public conditionSet?: ConditionSet + ) {} + + public static fromJSON(json: string) { + const config = JSON.parse(json, base64ToU8Receiver); + return DeployedCbdStrategy.fromObj(config); + } + + public toJSON() { + return JSON.stringify(this.toObj(), u8ToBase64Replacer); + } + + private static fromObj({ + dkgRitual, + cohortConfig, + bobSecretKeyBytes, + conditionSet, + }: DeployedStrategyJSON) { + const ritual = DkgRitual.fromObj(dkgRitual); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); + const cohort = Cohort.fromObj(cohortConfig); + const encrypter = new Enrico(ritual.dkgPublicKey, undefined, conditionSet); + + const decrypter = new CbdTDecDecrypter( + cohort.configuration.porterUri, + ritual.dkgPublicKey, + bobSecretKey + ); + return new DeployedCbdStrategy( + cohort, + ritual, + encrypter, + decrypter, + bobSecretKey, + conditionSet + ); + } + + private toObj(): DeployedStrategyJSON { + return { + dkgRitual: this.dkgRitual.toObj(), + cohortConfig: this.cohort.toObj(), + bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), + conditionSet: this.conditionSet, + }; + } +} diff --git a/src/sdk/strategy.ts b/src/sdk/strategy/pre-strategy.ts similarity index 88% rename from src/sdk/strategy.ts rename to src/sdk/strategy/pre-strategy.ts index e8626bcd3..dd29a16b7 100644 --- a/src/sdk/strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -6,15 +6,14 @@ import { } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { Alice } from '../characters/alice'; -import { Bob } from '../characters/bob'; -import { Enrico } from '../characters/enrico'; -import { tDecDecrypter } from '../characters/universal-bob'; -import { ConditionSet, ConditionSetJSON } from '../conditions/condition-set'; -import { EnactedPolicy, EnactedPolicyJSON } from '../policies/policy'; -import { base64ToU8Receiver, bytesEquals, toJson } from '../utils'; - -import { Cohort, CohortJSON } from './cohort'; +import { Alice } from '../../characters/alice'; +import { Bob } from '../../characters/bob'; +import { Enrico } from '../../characters/enrico'; +import { PreTDecDecrypter } from '../../characters/pre-universal-bob'; +import { ConditionSet, ConditionSetJSON } from '../../conditions'; +import { EnactedPolicy, EnactedPolicyJSON } from '../../policies/policy'; +import { base64ToU8Receiver, bytesEquals, toJson } from '../../utils'; +import { Cohort, CohortJSON } from '../cohort'; type StrategyJSON = { cohort: CohortJSON; @@ -32,7 +31,7 @@ type DeployedStrategyJSON = { conditionSet?: ConditionSetJSON; }; -export class Strategy { +export class PreStrategy { private constructor( public readonly cohort: Cohort, private readonly aliceSecretKey: SecretKey, @@ -62,7 +61,7 @@ export class Strategy { if (!endDate) { endDate = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30); } - return new Strategy( + return new PreStrategy( cohort, aliceSecretKey, bobSecretKey, @@ -75,7 +74,7 @@ export class Strategy { public async deploy( label: string, provider: ethers.providers.Web3Provider - ): Promise { + ): Promise { const porterUri = this.cohort.configuration.porterUri; const configuration = { porterUri }; const alice = Alice.fromSecretKey( @@ -99,14 +98,14 @@ export class Strategy { this.conditionSet ); - const decrypter = new tDecDecrypter( + const decrypter = new PreTDecDecrypter( this.cohort.configuration.porterUri, policy.policyKey, policy.encryptedTreasureMap, alice.verifyingKey, this.bobSecretKey ); - return new DeployedStrategy( + return new DeployedPreStrategy( label, this.cohort, policy, @@ -121,7 +120,7 @@ export class Strategy { const config = JSON.parse(json, base64ToU8Receiver); config.startDate = new Date(config.startDate); config.endDate = new Date(config.endDate); - return Strategy.fromObj(config); + return PreStrategy.fromObj(config); } public toJSON() { @@ -136,7 +135,7 @@ export class Strategy { startDate, endDate, }: StrategyJSON) { - return new Strategy( + return new PreStrategy( Cohort.fromObj(cohort), SecretKey.fromBEBytes(aliceSecretKeyBytes), SecretKey.fromBEBytes(bobSecretKeyBytes), @@ -177,20 +176,20 @@ export class Strategy { } } -export class DeployedStrategy { +export class DeployedPreStrategy { constructor( public label: string, public cohort: Cohort, public policy: EnactedPolicy, public encrypter: Enrico, - public decrypter: tDecDecrypter, + public decrypter: PreTDecDecrypter, private bobSecretKey: SecretKey, public conditionSet?: ConditionSet ) {} public static fromJSON(json: string) { const config = JSON.parse(json, base64ToU8Receiver); - return DeployedStrategy.fromObj(config); + return DeployedPreStrategy.fromObj(config); } public toJSON() { @@ -235,14 +234,14 @@ export class DeployedStrategy { conditionSetOrUndefined ); - const decrypter = new tDecDecrypter( + const decrypter = new PreTDecDecrypter( cohort.configuration.porterUri, policyKey, encryptedTreasureMap, aliceVerifyingKey, bobSecretKey ); - return new DeployedStrategy( + return new DeployedPreStrategy( label, cohort, newPolicy, diff --git a/test/acceptance/alice-grants.test.ts b/test/acceptance/alice-grants.test.ts index c234c1078..741877a04 100644 --- a/test/acceptance/alice-grants.test.ts +++ b/test/acceptance/alice-grants.test.ts @@ -88,7 +88,7 @@ describe('story: alice shares message with bob through policy', () => { it('enrico encrypts the message', () => { const enrico = new Enrico(policyEncryptingKey); - encryptedMessage = enrico.encryptMessage(toBytes(message)); + encryptedMessage = enrico.encryptMessagePre(toBytes(message)); }); it('bob retrieves and decrypts the message', async () => { diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 934bb2985..86be24fe6 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -1,7 +1,7 @@ import { MessageKit, VerifiedKeyFrag } from '@nucypher/nucypher-core'; import { providers } from 'ethers'; -import { Cohort, conditions, SecretKey, Strategy } from '../../src'; +import { Cohort, conditions, PreStrategy, SecretKey } from '../../src'; import { Ursula } from '../../src/characters/porter'; import { toBytes } from '../../src/utils'; import { @@ -80,7 +80,7 @@ describe('Get Started (CBD PoC)', () => { ]); // 4. Build a Strategy - const newStrategy = Strategy.create(newCohort, conditions); + const newStrategy = PreStrategy.create(newCohort, conditions); const MMprovider = await detectEthereumProvider(); const mumbai = providers.getNetwork(80001); @@ -105,7 +105,7 @@ describe('Get Started (CBD PoC)', () => { const encrypter = newDeployed.encrypter; const plaintext = 'this is a secret'; - const encryptedMessageKit = encrypter.encryptMessage( + const encryptedMessageKit = encrypter.encryptMessagePre( plaintext, new ConditionSet([NFTBalance]) ); diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts index c1d002086..824c30ab9 100644 --- a/test/integration/dkg-client.test.ts +++ b/test/integration/dkg-client.test.ts @@ -2,13 +2,16 @@ import { SecretKey } from '@nucypher/nucypher-core'; import { DkgCoordinatorAgent } from '../../src/agents/coordinator'; import { DkgClient } from '../../src/dkg'; -import { fakeDkgParticipants, fakeDkgRitual, fakeWeb3Provider } from '../utils'; - -const ritualId = 0; +import { + fakeCoordinatorRitual, + fakeDkgParticipants, + fakeWeb3Provider, +} from '../utils'; +const ritualId = 1; jest.mock('../../src/agents/coordinator', () => ({ DkgCoordinatorAgent: { - getRitual: () => Promise.resolve(fakeDkgRitual(ritualId)), + getRitual: () => Promise.resolve(fakeCoordinatorRitual(ritualId)), getParticipants: () => Promise.resolve(fakeDkgParticipants()), }, })); @@ -40,8 +43,8 @@ describe('DkgClient', () => { it('verifies the dkg ritual', async () => { const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - const dkgClient = new DkgClient(provider, ritualId); - const isValid = await dkgClient.verifyRitual(); + const dkgClient = new DkgClient(provider); + const isValid = await dkgClient.verifyRitual(ritualId); expect(isValid).toBeTruthy(); }); }); diff --git a/test/integration/enrico.test.ts b/test/integration/enrico.test.ts index 66318607b..bd90e5d5a 100644 --- a/test/integration/enrico.test.ts +++ b/test/integration/enrico.test.ts @@ -24,7 +24,7 @@ describe('enrico', () => { const policyKey = alice.getPolicyEncryptingKeyFromLabel(label); const enrico = new Enrico(policyKey); - const encrypted = enrico.encryptMessage(toBytes(message)); + const encrypted = enrico.encryptMessagePre(toBytes(message)); const aliceKeyring = (alice as any).keyring; const aliceSk = await aliceKeyring.getSecretKeyFromLabel(label); @@ -42,7 +42,7 @@ describe('enrico', () => { const plaintext = 'Plaintext message'; const plaintextBytes = toBytes(plaintext); - const encrypted = enrico.encryptMessage(plaintextBytes); + const encrypted = enrico.encryptMessagePre(plaintextBytes); // Alice can decrypt capsule she created const aliceSk = await (alice as any).keyring.getSecretKeyFromLabel(label); @@ -108,7 +108,7 @@ describe('enrico', () => { const conditions = new ConditionSet([ownsBufficornNFT]); const enrico = new Enrico(policyKey, undefined, conditions); - const encrypted = enrico.encryptMessage(toBytes(message)); + const encrypted = enrico.encryptMessagePre(toBytes(message)); const aliceKeyring = (alice as any).keyring; const aliceSk = await aliceKeyring.getSecretKeyFromLabel(label); @@ -139,7 +139,7 @@ describe('enrico', () => { const updatedConditions = new ConditionSet([ownsNonsenseNFT]); const enrico = new Enrico(policyKey, undefined, conditions); - const encrypted = enrico.encryptMessage( + const encrypted = enrico.encryptMessagePre( toBytes(message), updatedConditions ); diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index 2501509cb..1a9fb5600 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -54,7 +54,7 @@ describe('proxy reencryption', () => { const policyEncryptingKey = alice.getPolicyEncryptingKeyFromLabel(label); const enrico = new Enrico(policyEncryptingKey); - const encryptedMessage = enrico.encryptMessage(plaintext); + const encryptedMessage = enrico.encryptMessagePre(plaintext); const ursulaAddresses = ursulas.map((ursula) => ursula.checksumAddress); const reencrypted = verifiedKFrags.map((kFrag) => @@ -101,7 +101,7 @@ describe('proxy reencryption', () => { ]); const enrico = new Enrico(policyEncryptingKey, undefined, conditionsSet); - const encryptedMessage = enrico.encryptMessage(plaintext); + const encryptedMessage = enrico.encryptMessagePre(plaintext); const ursulaAddresses = ursulas.map((ursula) => ursula.checksumAddress); const reencrypted = verifiedKFrags.map((kFrag) => diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts new file mode 100644 index 000000000..c32fa3cca --- /dev/null +++ b/test/unit/cbd-strategy.test.ts @@ -0,0 +1,209 @@ +import { SecretKey } from '@nucypher/nucypher-core'; + +import { Cohort, conditions } from '../../src'; +import { FerveoVariant } from '../../src/dkg'; +import { CbdStrategy } from '../../src/sdk/strategy/cbd-strategy'; +import { toBytes } from '../../src/utils'; +import { + fakeDkgRitual, + fakeDkgRitualE2e, + fakeUrsulas, + fakeWeb3Provider, + mockDecrypt, + mockGetUrsulas, + mockInitializeRitual, +} from '../utils'; + +import { + aliceSecretKeyBytes, + bobSecretKeyBytes, + strategyJSON, +} from './testVariables'; + +const { + predefined: { ERC721Ownership }, + ConditionSet, +} = conditions; + +describe('CbdStrategy', () => { + const cohortConfig = { + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }; + const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); + const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + Date.now = jest.fn(() => 1487076708000); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('can create CbdStrategy from configuration', async () => { + const mockedUrsulas = fakeUrsulas().slice(0, 3); + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + + const testCohort = await Cohort.create(cohortConfig); + const testStrategy = CbdStrategy.create( + testCohort, + undefined, + bobSecretKey + ); + + const expectedUrsulas = mockedUrsulas.map( + (ursula) => ursula.checksumAddress + ); + expect(getUrsulasSpy).toHaveBeenCalled(); + expect(testStrategy.cohort.ursulaAddresses).toEqual(expectedUrsulas); + }); + + // TODO: Update this test + it('can export to JSON', async () => { + // const mockedUrsulas = fakeUrsulas().slice(0, 3); + // const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + // const testCohort = await Cohort.create(cohortConfig); + // const testStrategy = CbdStrategy.create( + // testCohort, + // undefined, + // bobSecretKey + // ); + // + // const configJSON = testStrategy.toJSON(); + // expect(configJSON).toEqual(strategyJSON); + // expect(getUrsulasSpy).toHaveBeenCalled(); + }); + + it('can import from JSON', async () => { + const testStrategy = CbdStrategy.fromJSON(strategyJSON); + const expectedUrsulas = [ + '0x5cf1703a1c99a4b42eb056535840e93118177232', + '0x7fff551249d223f723557a96a0e1a469c79cc934', + '0x9c7c824239d3159327024459ad69bb215859bd25', + ]; + expect(testStrategy.cohort.ursulaAddresses).toEqual(expectedUrsulas); + }); + + it('can deploy and return DeployedStrategy', async () => { + const fakeRitual = fakeDkgRitual(FerveoVariant.Precomputed); + const mockedUrsulas = fakeUrsulas().slice(0, 3); + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + const initRitualSpy = mockInitializeRitual(fakeRitual); + + const testCohort = await Cohort.create(cohortConfig); + const testStrategy = CbdStrategy.create( + testCohort, + undefined, + aliceSecretKey + ); + const testDeployed = await testStrategy.deploy(aliceProvider); + expect(testDeployed.dkgRitual.id).toEqual(fakeRitual.id); + expect(getUrsulasSpy).toHaveBeenCalled(); + expect(initRitualSpy).toHaveBeenCalled(); + }); +}); + +describe('CbdDeployedStrategy', () => { + const cohortConfig = { + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }; + const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + // TODO: Update this test + // it('can export to JSON', async () => { + // const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + // const mockedUrsulas = fakeUrsulas().slice(0, 3); + // const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + // + // const testCohort = await Cohort.create(cohortConfig); + // const testStrategy = CbdStrategy.create( + // testCohort, + // undefined, + // bobSecretKey + // ); + // const testDeployed = await testStrategy.deploy(aliceProvider); + // expect(getUrsulasSpy).toHaveBeenCalled(); + // + // const configJSON = testDeployed.toJSON(); + // expect(configJSON).toEqual(deployedStrategyJSON); + // }); + + // TODO: Update this test + // it('can import from JSON', async () => { + // const importedStrategy = DeployedCbdStrategy.fromJSON(deployedStrategyJSON); + // const configJSON = importedStrategy.toJSON(); + // expect(configJSON).toEqual(deployedStrategyJSON); + // }); + + it('can encrypt and decrypt', async () => { + const variant = FerveoVariant.Precomputed; + const fakeRitual = fakeDkgRitual(variant); + const { decryptionShares } = fakeDkgRitualE2e(variant); + const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + const mockedUrsulas = fakeUrsulas().slice(0, 3); + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + const initializeRitualSpy = mockInitializeRitual(fakeRitual); + + const cohort = await Cohort.create(cohortConfig); + const strategy = CbdStrategy.create(cohort, undefined, bobSecretKey); + + const deployedStrategy = await strategy.deploy(web3Provider); + expect(getUrsulasSpy).toHaveBeenCalled(); + expect(initializeRitualSpy).toHaveBeenCalled(); + + const encrypter = deployedStrategy.encrypter; + const decrypter = deployedStrategy.decrypter; + + const ownsNFT = new ERC721Ownership({ + contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', + parameters: [3591], + chain: 5, + }); + const conditionSet = new ConditionSet([ownsNFT]); + + const plaintext = 'this is a secret'; + const { ciphertext, aad } = encrypter.encryptMessageCbd( + plaintext, + conditionSet + ); + + // Setup mocks for `retrieveAndDecrypt` + const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); + const decryptSpy = mockDecrypt(decryptionShares); + + const decryptedMessage = await decrypter.retrieveAndDecrypt( + web3Provider, + conditionSet, + deployedStrategy.dkgRitual, + variant, + ciphertext, + aad + ); + expect(getUrsulasSpy2).toHaveBeenCalled(); + expect(decryptSpy).toHaveBeenCalled(); + expect(decryptedMessage[0]).toEqual(toBytes(plaintext)); + }); +}); + +// TODO: Update this test +describe('cbd tdec decrypter', () => { + // const importedStrategy = DeployedCbdStrategy.fromJSON(deployedStrategyJSON); + // + // it('can export to JSON', () => { + // const configJSON = importedStrategy.decrypter.toJSON(); + // expect(configJSON).toEqual(decrypterJSON); + // }); + // + // it('can import from JSON', () => { + // const decrypter = PreTDecDecrypter.fromJSON(decrypterJSON); + // const configJSON = decrypter.toJSON(); + // expect(configJSON).toEqual(decrypterJSON); + // }); +}); diff --git a/test/unit/strategy.test.ts b/test/unit/pre-strategy.test.ts similarity index 83% rename from test/unit/strategy.test.ts rename to test/unit/pre-strategy.test.ts index 18b6bd0e0..b28d68b55 100644 --- a/test/unit/strategy.test.ts +++ b/test/unit/pre-strategy.test.ts @@ -6,9 +6,9 @@ import { import { conditions, - DeployedStrategy, - Strategy, - tDecDecrypter, + DeployedPreStrategy, + PreStrategy, + PreTDecDecrypter, } from '../../src'; import { Ursula } from '../../src/characters/porter'; import { fromBase64, toBytes } from '../../src/utils'; @@ -49,14 +49,25 @@ const ownsNFT = new ERC721Ownership({ const conditionSet = new ConditionSet([ownsNFT]); const mockedUrsulas = fakeUrsulas().slice(0, 3); +describe('PreStrategy', () => { + const cohortConfig = { + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }; + const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); + const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + Date.now = jest.fn(() => 1487076708000); + describe('Strategy', () => { afterEach(() => { jest.restoreAllMocks(); }); - it('creates a Strategy', async () => { + it('creates a PreStrategy', async () => { const cohort = await makeCohort(mockedUrsulas); - const strategy = Strategy.create( + const strategy = PreStrategy.create( cohort, conditionSet, aliceSecretKey, @@ -67,20 +78,20 @@ describe('Strategy', () => { it('serializes to plain object', async () => { const cohort = await makeCohort(mockedUrsulas); - const strategy = Strategy.create( + const strategy = PreStrategy.create( cohort, conditionSet, aliceSecretKey, bobSecretKey ); const asObject = strategy.toObj(); - const fromObject = Strategy.fromObj(asObject); + const fromObject = PreStrategy.fromObj(asObject); expect(fromObject.equals(strategy)).toBeTruthy(); }); it('serializes to JSON', async () => { const cohort = await makeCohort(mockedUrsulas); - const strategy = Strategy.create( + const strategy = PreStrategy.create( cohort, conditionSet, aliceSecretKey, @@ -88,7 +99,7 @@ describe('Strategy', () => { ); const asJson = strategy.toJSON(); - const fromJSON = Strategy.fromJSON(asJson); + const fromJSON = PreStrategy.fromJSON(asJson); expect(fromJSON.equals(strategy)).toBeTruthy(); }); @@ -100,7 +111,10 @@ describe('Strategy', () => { const encryptTreasureMapSpy = mockEncryptTreasureMap(); const cohort = await makeCohort(mockedUrsulas); - const strategy = Strategy.create(cohort, conditionSet, aliceSecretKey); + const strategy = PreStrategy.create( + cohort, conditionSet, + aliceSecretKey + ); const label = 'test'; const deployedStrategy = await strategy.deploy(label, aliceProvider); @@ -115,7 +129,7 @@ describe('Strategy', () => { }); }); -describe('Deployed Strategy', () => { +describe('PreDeployedStrategy', () => { afterEach(() => { jest.restoreAllMocks(); }); @@ -131,7 +145,7 @@ describe('Deployed Strategy', () => { ); const cohort = await makeCohort(mockedUrsulas); - const strategy = Strategy.create( + const strategy = PreStrategy.create( cohort, conditionSet, aliceSecretKey, @@ -149,7 +163,7 @@ describe('Deployed Strategy', () => { expect(encryptTreasureMapSpy).toHaveBeenCalled(); const asJson = deployedStrategy.toJSON(); - const fromJson = DeployedStrategy.fromJSON(asJson); + const fromJson = DeployedPreStrategy.fromJSON(asJson); expect(fromJson.equals(deployedStrategy)).toBeTruthy(); }); @@ -164,7 +178,7 @@ describe('Deployed Strategy', () => { const encryptTreasureMapSpy = mockEncryptTreasureMap(); const cohort = await makeCohort(mockedUrsulas); - const strategy = Strategy.create( + const strategy = PreStrategy.create( cohort, conditionSet, aliceSecretKey, @@ -183,7 +197,7 @@ describe('Deployed Strategy', () => { const plaintext = 'this is a secret'; encrypter.conditions = conditionSet; - const encryptedMessageKit = encrypter.encryptMessage(plaintext); + const encryptedMessageKit = encrypter.encryptMessagePre(plaintext); // Setup mocks for `retrieveAndDecrypt` const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); @@ -212,12 +226,12 @@ describe('Deployed Strategy', () => { }); }); -describe('tDecDecrypter', () => { - const decrypter = DeployedStrategy.fromJSON(deployedStrategyJSON).decrypter; +describe('pre tdec decrypter', () => { + const decrypter = DeployedPreStrategy.fromJSON(deployedStrategyJSON).decrypter; it('serializes to JSON', () => { const asJson = decrypter.toJSON(); - const fromJson = tDecDecrypter.fromJSON(asJson); + const fromJson = PreTDecDecrypter.fromJSON(asJson); expect(fromJson.equals(decrypter)).toBeTruthy(); }); }); diff --git a/test/utils.ts b/test/utils.ts index a0299b502..5d8bfad79 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -9,6 +9,7 @@ import { PublicKey, reencrypt, SecretKey, + ThresholdDecryptionResponse, VerifiedCapsuleFrag, VerifiedKeyFrag, } from '@nucypher/nucypher-core'; @@ -32,13 +33,14 @@ import { } from 'ferveo-wasm'; import { Alice, Bob, Cohort, Configuration, RemoteBob } from '../src'; -import { DkgParticipant, DkgRitual } from '../src/agents/coordinator'; +import { CoordinatorRitual, DkgParticipant } from '../src/agents/coordinator'; import { GetUrsulasResponse, Porter, RetrieveCFragsResponse, Ursula, } from '../src/characters/porter'; +import { DkgClient, DkgRitual, FerveoVariant } from '../src/dkg'; import { BlockchainPolicy, PreEnactedPolicy } from '../src/policies/policy'; import { ChecksumAddress } from '../src/types'; import { toBytes, toHexString, zip } from '../src/utils'; @@ -229,8 +231,11 @@ export const mockDetectEthereumProvider = () => { }); }; -const fakeDkgRitualE2e = (variant: 'precomputed' | 'simple') => { - if (variant !== 'precomputed' && variant !== 'simple') { +export const fakeDkgRitualE2e = (variant: FerveoVariant) => { + if ( + variant !== FerveoVariant.Simple && + variant !== FerveoVariant.Precomputed + ) { throw new Error(`Invalid variant: ${variant}`); } @@ -299,7 +304,7 @@ const fakeDkgRitualE2e = (variant: 'precomputed' | 'simple') => { let decryptionShare; - if (variant === 'precomputed') { + if (variant === FerveoVariant.Precomputed) { decryptionShare = aggregate.createDecryptionSharePrecomputed( dkg, ciphertext, @@ -321,7 +326,7 @@ const fakeDkgRitualE2e = (variant: 'precomputed' | 'simple') => { // This part is in the client API let sharedSecret; - if (variant === 'precomputed') { + if (variant === FerveoVariant.Precomputed) { sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares); } else { sharedSecret = combineDecryptionSharesSimple( @@ -354,11 +359,12 @@ const fakeDkgRitualE2e = (variant: 'precomputed' | 'simple') => { aad, ciphertext, serverAggregate, + decryptionShares, }; }; -export const fakeDkgRitual = (ritualId: number): DkgRitual => { - const ritual = fakeDkgRitualE2e('precomputed'); +export const fakeCoordinatorRitual = (ritualId: number): CoordinatorRitual => { + const ritual = fakeDkgRitualE2e(FerveoVariant.Precomputed); return { id: ritualId, initiator: ritual.validators[0].address.toString(), @@ -375,7 +381,7 @@ export const fakeDkgRitual = (ritualId: number): DkgRitual => { }; export const fakeDkgParticipants = (): DkgParticipant[] => { - const ritual = fakeDkgRitualE2e('precomputed'); + const ritual = fakeDkgRitualE2e(FerveoVariant.Precomputed); return zip( zip(ritual.validators, ritual.transcripts), ritual.validator_keypairs @@ -389,6 +395,30 @@ export const fakeDkgParticipants = (): DkgParticipant[] => { }); }; +export const mockDecrypt = ( + decryptionShares: (DecryptionSharePrecomputed | DecryptionShareSimple)[] +) => { + const result = decryptionShares.map( + (share) => new ThresholdDecryptionResponse(share.toBytes()) + ); + return jest.spyOn(Porter.prototype, 'decrypt').mockImplementation(() => { + return Promise.resolve(result); + }); +}; + +export const fakeDkgRitual = (variant: FerveoVariant) => { + const ritual = fakeDkgRitualE2e(variant); + return new DkgRitual(1, ritual.dkg.finalKey(), ritual.dkg.publicParams()); +}; + +export const mockInitializeRitual = (fakeRitual: unknown) => { + return jest + .spyOn(DkgClient.prototype as any, 'initializeRitual') + .mockImplementation(() => { + return Promise.resolve(fakeRitual); + }); +}; + export const makeCohort = async (ursulas: Ursula[]) => { const getUrsulasSpy = mockGetUrsulas(ursulas); const config = { From b24af3ff2dc10b89d9f9128c01fa6ed7ebe7692a Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 19 May 2023 12:48:44 +0200 Subject: [PATCH 42/98] fix e2e test --- src/characters/cbd-universal-bob.ts | 37 +------ src/sdk/strategy/cbd-strategy.ts | 42 ++------ test/unit/cbd-strategy.test.ts | 159 +++++++++++++++------------- test/unit/pre-strategy.test.ts | 17 +-- test/unit/testVariables.ts | 45 +++++++- test/utils.ts | 99 ++++++++++++----- 6 files changed, 214 insertions(+), 185 deletions(-) diff --git a/src/characters/cbd-universal-bob.ts b/src/characters/cbd-universal-bob.ts index 2a1f96fc9..01c50e4bd 100644 --- a/src/characters/cbd-universal-bob.ts +++ b/src/characters/cbd-universal-bob.ts @@ -1,11 +1,4 @@ -import { - Context, - MessageKit, - PublicKey, - SecretKey, - Signer, - ThresholdDecryptionRequest, -} from '@nucypher/nucypher-core'; +import { Context, ThresholdDecryptionRequest } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { combineDecryptionSharesPrecomputed, @@ -20,8 +13,6 @@ import { Ciphertext } from 'ferveo-wasm'; import { ConditionSet } from '../conditions'; import { DkgRitual, FerveoVariant } from '../dkg'; -import { Keyring } from '../keyring'; -import { PolicyMessageKit } from '../kits/message'; import { base64ToU8Receiver, u8ToBase64Replacer } from '../utils'; import { Porter } from './porter'; @@ -29,34 +20,15 @@ import { Porter } from './porter'; type CbdTDecDecrypterJSON = { porterUri: string; encryptingKeyBytes: Uint8Array; - bobSecretKeyBytes: Uint8Array; }; export class CbdTDecDecrypter { private readonly porter: Porter; - private readonly keyring: Keyring; // private readonly verifyingKey: Keyring; - constructor( - porterUri: string, - private readonly dkgPublicKey: DkgPublicKey, - secretKey: SecretKey - ) { + constructor(porterUri: string, private readonly dkgPublicKey: DkgPublicKey) { this.porter = new Porter(porterUri); - this.keyring = new Keyring(secretKey); - } - - public get decryptingKey(): PublicKey { - return this.keyring.publicKey; - } - - public get signer(): Signer { - return this.keyring.signer; - } - - public decrypt(messageKit: MessageKit | PolicyMessageKit): Uint8Array { - return this.keyring.decrypt(messageKit); } public async retrieveAndDecrypt( @@ -138,7 +110,6 @@ export class CbdTDecDecrypter { return { porterUri: this.porter.porterUrl.toString(), encryptingKeyBytes: this.dkgPublicKey.toBytes(), - bobSecretKeyBytes: this.keyring.secretKey.toBEBytes(), }; } @@ -149,12 +120,10 @@ export class CbdTDecDecrypter { private static fromObj({ porterUri, encryptingKeyBytes, - bobSecretKeyBytes, }: CbdTDecDecrypterJSON) { return new CbdTDecDecrypter( porterUri, - DkgPublicKey.fromBytes(encryptingKeyBytes), - SecretKey.fromBEBytes(bobSecretKeyBytes) + DkgPublicKey.fromBytes(encryptingKeyBytes) ); } diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 6f60b5a9f..6219afbe7 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -1,4 +1,3 @@ -import { SecretKey } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { CbdTDecDecrypter } from '../../characters/cbd-universal-bob'; @@ -10,33 +9,23 @@ import { Cohort, CohortJSON } from '../cohort'; type StrategyJSON = { cohort: CohortJSON; - bobSecretKeyBytes: Uint8Array; conditionSet?: ConditionSet; }; type DeployedStrategyJSON = { dkgRitual: DkgRitualJSON; cohortConfig: CohortJSON; - bobSecretKeyBytes: Uint8Array; conditionSet?: ConditionSet; }; export class CbdStrategy { private constructor( public readonly cohort: Cohort, - private readonly bobSecretKey: SecretKey, private readonly conditionSet?: ConditionSet ) {} - public static create( - cohort: Cohort, - conditionSet?: ConditionSet, - bobSecretKey?: SecretKey - ) { - if (!bobSecretKey) { - bobSecretKey = SecretKey.random(); - } - return new CbdStrategy(cohort, bobSecretKey, conditionSet); + public static create(cohort: Cohort, conditionSet?: ConditionSet) { + return new CbdStrategy(cohort, conditionSet); } public async deploy( @@ -60,8 +49,7 @@ export class CbdStrategy { const decrypter = new CbdTDecDecrypter( this.cohort.configuration.porterUri, - dkgRitual.dkgPublicKey, - this.bobSecretKey + dkgRitual.dkgPublicKey ); return new DeployedCbdStrategy( @@ -69,7 +57,6 @@ export class CbdStrategy { dkgRitual, encrypter, decrypter, - this.bobSecretKey, this.conditionSet ); } @@ -83,22 +70,13 @@ export class CbdStrategy { return JSON.stringify(this.toObj(), u8ToBase64Replacer); } - private static fromObj({ - cohort, - bobSecretKeyBytes, - conditionSet, - }: StrategyJSON) { - return new CbdStrategy( - Cohort.fromObj(cohort), - SecretKey.fromBEBytes(bobSecretKeyBytes), - conditionSet - ); + private static fromObj({ cohort, conditionSet }: StrategyJSON) { + return new CbdStrategy(Cohort.fromObj(cohort), conditionSet); } public toObj(): StrategyJSON { return { cohort: this.cohort.toObj(), - bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), conditionSet: this.conditionSet, }; } @@ -110,7 +88,6 @@ export class DeployedCbdStrategy { public dkgRitual: DkgRitual, public encrypter: Enrico, public decrypter: CbdTDecDecrypter, - private bobSecretKey: SecretKey, public conditionSet?: ConditionSet ) {} @@ -126,34 +103,29 @@ export class DeployedCbdStrategy { private static fromObj({ dkgRitual, cohortConfig, - bobSecretKeyBytes, conditionSet, }: DeployedStrategyJSON) { const ritual = DkgRitual.fromObj(dkgRitual); - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); const cohort = Cohort.fromObj(cohortConfig); const encrypter = new Enrico(ritual.dkgPublicKey, undefined, conditionSet); const decrypter = new CbdTDecDecrypter( cohort.configuration.porterUri, - ritual.dkgPublicKey, - bobSecretKey + ritual.dkgPublicKey ); return new DeployedCbdStrategy( cohort, ritual, encrypter, decrypter, - bobSecretKey, conditionSet ); } - private toObj(): DeployedStrategyJSON { + public toObj(): DeployedStrategyJSON { return { dkgRitual: this.dkgRitual.toObj(), cohortConfig: this.cohort.toObj(), - bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), conditionSet: this.conditionSet, }; } diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index c32fa3cca..8959f0006 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -1,12 +1,18 @@ import { SecretKey } from '@nucypher/nucypher-core'; import { Cohort, conditions } from '../../src'; +import { CbdTDecDecrypter } from '../../src/characters/cbd-universal-bob'; import { FerveoVariant } from '../../src/dkg'; -import { CbdStrategy } from '../../src/sdk/strategy/cbd-strategy'; +import { + CbdStrategy, + DeployedCbdStrategy, +} from '../../src/sdk/strategy/cbd-strategy'; import { toBytes } from '../../src/utils'; import { + fakeDkgFlow, fakeDkgRitual, - fakeDkgRitualE2e, + fakeDkgTDecFlowE2e, + fakeTDecFlow, fakeUrsulas, fakeWeb3Provider, mockDecrypt, @@ -16,8 +22,10 @@ import { import { aliceSecretKeyBytes, - bobSecretKeyBytes, - strategyJSON, + cbdDecrypterJSON, + cbdStrategyJSON, + deployedCbdStrategyObj, + preStrategyJSON, } from './testVariables'; const { @@ -32,7 +40,6 @@ describe('CbdStrategy', () => { porterUri: 'https://_this.should.crash', }; const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); Date.now = jest.fn(() => 1487076708000); @@ -45,11 +52,7 @@ describe('CbdStrategy', () => { const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const testCohort = await Cohort.create(cohortConfig); - const testStrategy = CbdStrategy.create( - testCohort, - undefined, - bobSecretKey - ); + const testStrategy = CbdStrategy.create(testCohort, undefined); const expectedUrsulas = mockedUrsulas.map( (ursula) => ursula.checksumAddress @@ -60,22 +63,18 @@ describe('CbdStrategy', () => { // TODO: Update this test it('can export to JSON', async () => { - // const mockedUrsulas = fakeUrsulas().slice(0, 3); - // const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - // const testCohort = await Cohort.create(cohortConfig); - // const testStrategy = CbdStrategy.create( - // testCohort, - // undefined, - // bobSecretKey - // ); - // - // const configJSON = testStrategy.toJSON(); - // expect(configJSON).toEqual(strategyJSON); - // expect(getUrsulasSpy).toHaveBeenCalled(); + const mockedUrsulas = fakeUrsulas().slice(0, 3); + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + const testCohort = await Cohort.create(cohortConfig); + const testStrategy = CbdStrategy.create(testCohort, undefined); + + const configJSON = testStrategy.toJSON(); + expect(configJSON).toEqual(cbdStrategyJSON); + expect(getUrsulasSpy).toHaveBeenCalled(); }); it('can import from JSON', async () => { - const testStrategy = CbdStrategy.fromJSON(strategyJSON); + const testStrategy = CbdStrategy.fromJSON(preStrategyJSON); const expectedUrsulas = [ '0x5cf1703a1c99a4b42eb056535840e93118177232', '0x7fff551249d223f723557a96a0e1a469c79cc934', @@ -85,17 +84,15 @@ describe('CbdStrategy', () => { }); it('can deploy and return DeployedStrategy', async () => { - const fakeRitual = fakeDkgRitual(FerveoVariant.Precomputed); + const variant = FerveoVariant.Precomputed; + const fakeE2ERitual = fakeDkgTDecFlowE2e(variant); + const fakeRitual = fakeDkgRitual(fakeE2ERitual); const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const initRitualSpy = mockInitializeRitual(fakeRitual); const testCohort = await Cohort.create(cohortConfig); - const testStrategy = CbdStrategy.create( - testCohort, - undefined, - aliceSecretKey - ); + const testStrategy = CbdStrategy.create(testCohort, undefined); const testDeployed = await testStrategy.deploy(aliceProvider); expect(testDeployed.dkgRitual.id).toEqual(fakeRitual.id); expect(getUrsulasSpy).toHaveBeenCalled(); @@ -110,49 +107,57 @@ describe('CbdDeployedStrategy', () => { porterUri: 'https://_this.should.crash', }; const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); afterEach(() => { jest.restoreAllMocks(); }); - // TODO: Update this test - // it('can export to JSON', async () => { - // const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - // const mockedUrsulas = fakeUrsulas().slice(0, 3); - // const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - // - // const testCohort = await Cohort.create(cohortConfig); - // const testStrategy = CbdStrategy.create( - // testCohort, - // undefined, - // bobSecretKey - // ); - // const testDeployed = await testStrategy.deploy(aliceProvider); - // expect(getUrsulasSpy).toHaveBeenCalled(); - // - // const configJSON = testDeployed.toJSON(); - // expect(configJSON).toEqual(deployedStrategyJSON); - // }); + it('can import from JSON', async () => { + const json = JSON.stringify(deployedCbdStrategyObj); + const importedStrategy = DeployedCbdStrategy.fromJSON(json); + const configJSON = importedStrategy.toJSON(); + expect(configJSON).toEqual(json); + }); - // TODO: Update this test - // it('can import from JSON', async () => { - // const importedStrategy = DeployedCbdStrategy.fromJSON(deployedStrategyJSON); - // const configJSON = importedStrategy.toJSON(); - // expect(configJSON).toEqual(deployedStrategyJSON); - // }); + it('can export to JSON', async () => { + const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + const mockedUrsulas = fakeUrsulas().slice(0, 3); + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + const fakeDkg = fakeDkgFlow(FerveoVariant.Precomputed, 0); + const fakeRitual = fakeDkgRitual(fakeDkg); + const initializeRitualSpy = mockInitializeRitual(fakeRitual); + + const testCohort = await Cohort.create(cohortConfig); + const testStrategy = CbdStrategy.create(testCohort, undefined); + const testDeployed = await testStrategy.deploy(aliceProvider); + expect(getUrsulasSpy).toHaveBeenCalled(); + expect(initializeRitualSpy).toHaveBeenCalled(); + + const asJson = testDeployed.toJSON(); + expect(asJson).toBeDefined(); + const asObj = JSON.parse(asJson); + // TODO: Will not match until we hardcode validator keypairs in tests + // expect(asJson).toEqual(deployedCbdStrategyJSON); + // So instead we do: + delete asObj.dkgRitual.dkgPublicKey; + const expectedObj: Partial = Object.assign( + deployedCbdStrategyObj + ); + delete expectedObj.dkgRitual.dkgPublicKey; + expect(asObj).toEqual(expectedObj); + }); it('can encrypt and decrypt', async () => { const variant = FerveoVariant.Precomputed; - const fakeRitual = fakeDkgRitual(variant); - const { decryptionShares } = fakeDkgRitualE2e(variant); + const fakeDkg = fakeDkgFlow(variant, 0); + const fakeRitual = fakeDkgRitual(fakeDkg); const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); const mockedUrsulas = fakeUrsulas().slice(0, 3); const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const initializeRitualSpy = mockInitializeRitual(fakeRitual); const cohort = await Cohort.create(cohortConfig); - const strategy = CbdStrategy.create(cohort, undefined, bobSecretKey); + const strategy = CbdStrategy.create(cohort, undefined); const deployedStrategy = await strategy.deploy(web3Provider); expect(getUrsulasSpy).toHaveBeenCalled(); @@ -168,13 +173,20 @@ describe('CbdDeployedStrategy', () => { }); const conditionSet = new ConditionSet([ownsNFT]); - const plaintext = 'this is a secret'; + const message = 'this is a secret'; const { ciphertext, aad } = encrypter.encryptMessageCbd( - plaintext, + message, conditionSet ); // Setup mocks for `retrieveAndDecrypt` + const { decryptionShares } = fakeTDecFlow({ + ...fakeDkg, + variant, + message: toBytes(message), + aad, + ciphertext, + }); const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); const decryptSpy = mockDecrypt(decryptionShares); @@ -188,22 +200,23 @@ describe('CbdDeployedStrategy', () => { ); expect(getUrsulasSpy2).toHaveBeenCalled(); expect(decryptSpy).toHaveBeenCalled(); - expect(decryptedMessage[0]).toEqual(toBytes(plaintext)); + expect(decryptedMessage[0]).toEqual(toBytes(message)); }); }); -// TODO: Update this test describe('cbd tdec decrypter', () => { - // const importedStrategy = DeployedCbdStrategy.fromJSON(deployedStrategyJSON); - // - // it('can export to JSON', () => { - // const configJSON = importedStrategy.decrypter.toJSON(); - // expect(configJSON).toEqual(decrypterJSON); - // }); - // - // it('can import from JSON', () => { - // const decrypter = PreTDecDecrypter.fromJSON(decrypterJSON); - // const configJSON = decrypter.toJSON(); - // expect(configJSON).toEqual(decrypterJSON); - // }); + const importedStrategy = DeployedCbdStrategy.fromJSON( + JSON.stringify(deployedCbdStrategyObj) + ); + + it('can export to JSON', () => { + const configJSON = importedStrategy.decrypter.toJSON(); + expect(configJSON).toEqual(cbdDecrypterJSON); + }); + + it('can import from JSON', () => { + const decrypter = CbdTDecDecrypter.fromJSON(cbdDecrypterJSON); + const configJSON = decrypter.toJSON(); + expect(configJSON).toEqual(cbdDecrypterJSON); + }); }); diff --git a/test/unit/pre-strategy.test.ts b/test/unit/pre-strategy.test.ts index b28d68b55..4cbd38143 100644 --- a/test/unit/pre-strategy.test.ts +++ b/test/unit/pre-strategy.test.ts @@ -27,7 +27,7 @@ import { import { aliceSecretKeyBytes, bobSecretKeyBytes, - deployedStrategyJSON, + deployedPreStrategyJSON, encryptedTreasureMapBase64, } from './testVariables'; @@ -50,17 +50,6 @@ const conditionSet = new ConditionSet([ownsNFT]); const mockedUrsulas = fakeUrsulas().slice(0, 3); describe('PreStrategy', () => { - const cohortConfig = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); - const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - Date.now = jest.fn(() => 1487076708000); - -describe('Strategy', () => { afterEach(() => { jest.restoreAllMocks(); }); @@ -227,7 +216,9 @@ describe('PreDeployedStrategy', () => { }); describe('pre tdec decrypter', () => { - const decrypter = DeployedPreStrategy.fromJSON(deployedStrategyJSON).decrypter; + const decrypter = DeployedPreStrategy.fromJSON( + deployedPreStrategyJSON + ).decrypter; it('serializes to JSON', () => { const asJson = decrypter.toJSON(); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index a1a6c3b7e..7e6fad8bd 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -1,4 +1,4 @@ -export const strategyJSON = JSON.stringify({ +export const preStrategyJSON = JSON.stringify({ cohort: { ursulaAddresses: [ '0x5cf1703a1c99a4b42eb056535840e93118177232', @@ -15,10 +15,23 @@ export const strategyJSON = JSON.stringify({ endDate: '2123-03-16T12:51:48.000Z', }); +export const cbdStrategyJSON = JSON.stringify({ + cohort: { + ursulaAddresses: [ + '0x5cf1703a1c99a4b42eb056535840e93118177232', + '0x7fff551249d223f723557a96a0e1a469c79cc934', + '0x9c7c824239d3159327024459ad69bb215859bd25', + ], + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }, +}); + export const encryptedTreasureMapBase64 = 'RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw='; -export const deployedStrategyJSON = JSON.stringify({ +export const deployedPreStrategyJSON = JSON.stringify({ policy: { id: 'base64:tKuR/HipMeU4trCdytbZCQ==', label: 'test', @@ -43,6 +56,26 @@ export const deployedStrategyJSON = JSON.stringify({ }, bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', }); + +export const deployedCbdStrategyObj = { + dkgRitual: { + id: 1, + dkgPublicKey: + 'base64:MAAAAAAAAACGxOoiK8/cO6vl2gb12nlQSFgUP+Mo5OHt6vhBqlMsOzWiSuejdy2z3AxAnz2Du8k=', + dkgPublicParams: + 'base64:MAAAAAAAAAC38dOnMZfXlCaVY4xPqawPw2iMT5d0uQWhTjo/FxusWGxV6D/5ehrv+zrwCtsixruIAAAAAAAAAAQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAA3YCAAPs0AQDds7MUY0AAAAAAAAAAAAAAAD//////lv+/wKkvVMF2KEJCNg5M0h9nSlTp+1zAQAAAP///v/+W/uJAKS6ZzTTnpM5C+ilR32dKVOn7XM=', + }, + cohortConfig: { + ursulaAddresses: [ + '0x5cf1703a1c99a4b42eb056535840e93118177232', + '0x7fff551249d223f723557a96a0e1a469c79cc934', + '0x9c7c824239d3159327024459ad69bb215859bd25', + ], + threshold: 2, + shares: 3, + porterUri: 'https://_this.should.crash', + }, +}; export const aliceSecretKeyBytes = new Uint8Array([ 55, 82, 190, 189, 203, 164, 60, 148, 36, 86, 46, 123, 63, 152, 215, 113, 174, 86, 244, 44, 23, 227, 197, 68, 5, 85, 116, 31, 208, 152, 88, 53, @@ -53,7 +86,7 @@ export const bobSecretKeyBytes = new Uint8Array([ 229, 2, 106, 176, 205, 33, 168, 23, 213, 233, 200, 238, 11, 193, 153, ]); -export const decrypterJSON = JSON.stringify({ +export const preDecrypterJSON = JSON.stringify({ porterUri: 'https://_this.should.crash/', policyEncryptingKeyBytes: 'base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C', @@ -64,6 +97,12 @@ export const decrypterJSON = JSON.stringify({ bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', }); +export const cbdDecrypterJSON = JSON.stringify({ + porterUri: 'https://_this.should.crash/', + encryptingKeyBytes: + 'base64:MAAAAAAAAACGxOoiK8/cO6vl2gb12nlQSFgUP+Mo5OHt6vhBqlMsOzWiSuejdy2z3AxAnz2Du8k=', +}); + export const TEST_CONTRACT_ADDR = '0x0000000000000000000000000000000000000001'; export const TEST_CONTRACT_ADDR_2 = '0x0000000000000000000000000000000000000002'; diff --git a/test/utils.ts b/test/utils.ts index 5d8bfad79..cfd1a96f7 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -18,6 +18,7 @@ import { ethers, providers, Wallet } from 'ethers'; import { keccak256 } from 'ethers/lib/utils'; import { AggregatedTranscript, + Ciphertext, combineDecryptionSharesPrecomputed, combineDecryptionSharesSimple, DecryptionSharePrecomputed, @@ -231,7 +232,12 @@ export const mockDetectEthereumProvider = () => { }); }; -export const fakeDkgRitualE2e = (variant: FerveoVariant) => { +export const fakeDkgFlow = ( + variant: FerveoVariant | FerveoVariant.Precomputed, + tau: number, + sharesNum = 4, + threshold = 3 +) => { if ( variant !== FerveoVariant.Simple && variant !== FerveoVariant.Precomputed @@ -244,11 +250,6 @@ export const fakeDkgRitualE2e = (variant: FerveoVariant) => { '0x' + '0'.repeat(40 - i.toString(16).length) + i.toString(16); return EthereumAddress.fromString(ethAddr); }; - - const tau = 1; - const sharesNum = 4; - const threshold = Math.floor((sharesNum * 2) / 3); - const validator_keypairs: Keypair[] = []; const validators: Validator[] = []; for (let i = 0; i < sharesNum; i++) { @@ -283,12 +284,46 @@ export const fakeDkgRitualE2e = (variant: FerveoVariant) => { // Client can also aggregate the transcripts and verify them const clientAggregate = new AggregatedTranscript(receivedMessages); expect(clientAggregate.verify(sharesNum, receivedMessages)).toBeTruthy(); + return { + tau, + sharesNum, + threshold, + validator_keypairs, + validators, + transcripts, + dkg, + receivedMessages, + serverAggregate, + }; +}; - // In the meantime, the client creates a ciphertext and decryption request - const msg = Buffer.from('my-msg'); - const aad = Buffer.from('my-aad'); - const ciphertext = encrypt(msg, aad, dkg.finalKey()); - +interface FakeDkgRitualFlow { + validators: Validator[]; + validator_keypairs: Keypair[]; + tau: number; + sharesNum: number; + threshold: number; + receivedMessages: ValidatorMessage[]; + variant: FerveoVariant; + ciphertext: Ciphertext; + aad: Uint8Array; + dkg: Dkg; + message: Uint8Array; +} + +export const fakeTDecFlow = ({ + validators, + validator_keypairs, + tau, + sharesNum, + threshold, + receivedMessages, + variant, + ciphertext, + aad, + dkg, + message, +}: FakeDkgRitualFlow) => { // Having aggregated the transcripts, the validators can now create decryption shares const decryptionShares: ( | DecryptionSharePrecomputed @@ -303,7 +338,6 @@ export const fakeDkgRitualE2e = (variant: FerveoVariant) => { } let decryptionShare; - if (variant === FerveoVariant.Precomputed) { decryptionShare = aggregate.createDecryptionSharePrecomputed( dkg, @@ -342,29 +376,41 @@ export const fakeDkgRitualE2e = (variant: FerveoVariant) => { sharedSecret, dkg.publicParams() ); - if (!bytesEqual(plaintext, msg)) { + if (!bytesEqual(plaintext, message)) { throw new Error('Decryption failed'); } + return { decryptionShares, sharedSecret, plaintext }; +}; + +export const fakeDkgTDecFlowE2e = ( + variant: FerveoVariant, + message = toBytes('fake-message'), + aad = toBytes('fake-aad'), + ritualId = 0 +) => { + const ritual = fakeDkgFlow(variant, ritualId, 4, 3); + + // In the meantime, the client creates a ciphertext and decryption request + const ciphertext = encrypt(message, aad, ritual.dkg.finalKey()); + const { decryptionShares } = fakeTDecFlow({ + ...ritual, + variant, + ciphertext, + aad, + message, + }); return { - tau, - sharesNum, - threshold, - validator_keypairs, - validators, - transcripts, - dkg, - receivedMessages, - msg, + ...ritual, + message, aad, ciphertext, - serverAggregate, decryptionShares, }; }; export const fakeCoordinatorRitual = (ritualId: number): CoordinatorRitual => { - const ritual = fakeDkgRitualE2e(FerveoVariant.Precomputed); + const ritual = fakeDkgTDecFlowE2e(FerveoVariant.Precomputed); return { id: ritualId, initiator: ritual.validators[0].address.toString(), @@ -381,7 +427,7 @@ export const fakeCoordinatorRitual = (ritualId: number): CoordinatorRitual => { }; export const fakeDkgParticipants = (): DkgParticipant[] => { - const ritual = fakeDkgRitualE2e(FerveoVariant.Precomputed); + const ritual = fakeDkgTDecFlowE2e(FerveoVariant.Precomputed); return zip( zip(ritual.validators, ritual.transcripts), ritual.validator_keypairs @@ -406,8 +452,7 @@ export const mockDecrypt = ( }); }; -export const fakeDkgRitual = (variant: FerveoVariant) => { - const ritual = fakeDkgRitualE2e(variant); +export const fakeDkgRitual = (ritual: { dkg: Dkg }) => { return new DkgRitual(1, ritual.dkg.finalKey(), ritual.dkg.publicParams()); }; From e08197ddf6a6dc183f19bc92386ddb5595eaa88f Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 23 May 2023 16:45:12 +0200 Subject: [PATCH 43/98] silence tsc warnings --- src/dkg.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dkg.ts b/src/dkg.ts index ece1d66ef..bbdc2ae0d 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -54,7 +54,9 @@ export class DkgClient { // eslint-disable-next-line @typescript-eslint/no-unused-vars public async initializeRitual( + // eslint-disable-next-line @typescript-eslint/no-unused-vars _provider: ethers.providers.Web3Provider, + // eslint-disable-next-line @typescript-eslint/no-unused-vars _ritualParams: unknown ): Promise { // TODO: Create a new DKG ritual here From 5280f4f8f9490b21e7cb2934d506b270b3006a5b Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 23 May 2023 16:45:27 +0200 Subject: [PATCH 44/98] remove unused fields --- src/characters/cbd-universal-bob.ts | 15 +++------------ src/sdk/strategy/cbd-strategy.ts | 10 ++-------- test/unit/testVariables.ts | 2 -- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/characters/cbd-universal-bob.ts b/src/characters/cbd-universal-bob.ts index 01c50e4bd..d491c8be2 100644 --- a/src/characters/cbd-universal-bob.ts +++ b/src/characters/cbd-universal-bob.ts @@ -6,7 +6,6 @@ import { DecryptionSharePrecomputed, DecryptionShareSimple, decryptWithSharedSecret, - DkgPublicKey, } from 'ferveo-wasm'; import { SharedSecret } from 'ferveo-wasm'; import { Ciphertext } from 'ferveo-wasm'; @@ -19,7 +18,6 @@ import { Porter } from './porter'; type CbdTDecDecrypterJSON = { porterUri: string; - encryptingKeyBytes: Uint8Array; }; export class CbdTDecDecrypter { @@ -27,7 +25,7 @@ export class CbdTDecDecrypter { // private readonly verifyingKey: Keyring; - constructor(porterUri: string, private readonly dkgPublicKey: DkgPublicKey) { + constructor(porterUri: string) { this.porter = new Porter(porterUri); } @@ -109,7 +107,6 @@ export class CbdTDecDecrypter { public toObj(): CbdTDecDecrypterJSON { return { porterUri: this.porter.porterUrl.toString(), - encryptingKeyBytes: this.dkgPublicKey.toBytes(), }; } @@ -117,14 +114,8 @@ export class CbdTDecDecrypter { return JSON.stringify(this.toObj(), u8ToBase64Replacer); } - private static fromObj({ - porterUri, - encryptingKeyBytes, - }: CbdTDecDecrypterJSON) { - return new CbdTDecDecrypter( - porterUri, - DkgPublicKey.fromBytes(encryptingKeyBytes) - ); + private static fromObj({ porterUri }: CbdTDecDecrypterJSON) { + return new CbdTDecDecrypter(porterUri); } public static fromJSON(json: string) { diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 6219afbe7..677af979b 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -47,10 +47,7 @@ export class CbdStrategy { this.conditionSet ); - const decrypter = new CbdTDecDecrypter( - this.cohort.configuration.porterUri, - dkgRitual.dkgPublicKey - ); + const decrypter = new CbdTDecDecrypter(this.cohort.configuration.porterUri); return new DeployedCbdStrategy( this.cohort, @@ -109,10 +106,7 @@ export class DeployedCbdStrategy { const cohort = Cohort.fromObj(cohortConfig); const encrypter = new Enrico(ritual.dkgPublicKey, undefined, conditionSet); - const decrypter = new CbdTDecDecrypter( - cohort.configuration.porterUri, - ritual.dkgPublicKey - ); + const decrypter = new CbdTDecDecrypter(cohort.configuration.porterUri); return new DeployedCbdStrategy( cohort, ritual, diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index 7e6fad8bd..909908c09 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -99,8 +99,6 @@ export const preDecrypterJSON = JSON.stringify({ export const cbdDecrypterJSON = JSON.stringify({ porterUri: 'https://_this.should.crash/', - encryptingKeyBytes: - 'base64:MAAAAAAAAACGxOoiK8/cO6vl2gb12nlQSFgUP+Mo5OHt6vhBqlMsOzWiSuejdy2z3AxAnz2Du8k=', }); export const TEST_CONTRACT_ADDR = '0x0000000000000000000000000000000000000001'; From 063621b251c3a2903e7ebb0a2a5f87c705efa539 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 30 May 2023 12:44:55 +0200 Subject: [PATCH 45/98] simplify naming --- src/characters/{cbd-universal-bob.ts => cbd-recipient.ts} | 0 src/characters/{pre-universal-bob.ts => pre-recipient.ts} | 0 src/index.ts | 2 +- src/sdk/strategy/cbd-strategy.ts | 2 +- src/sdk/strategy/pre-strategy.ts | 2 +- test/unit/cbd-strategy.test.ts | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) rename src/characters/{cbd-universal-bob.ts => cbd-recipient.ts} (100%) rename src/characters/{pre-universal-bob.ts => pre-recipient.ts} (100%) diff --git a/src/characters/cbd-universal-bob.ts b/src/characters/cbd-recipient.ts similarity index 100% rename from src/characters/cbd-universal-bob.ts rename to src/characters/cbd-recipient.ts diff --git a/src/characters/pre-universal-bob.ts b/src/characters/pre-recipient.ts similarity index 100% rename from src/characters/pre-universal-bob.ts rename to src/characters/pre-recipient.ts diff --git a/src/index.ts b/src/index.ts index 3ce372dbd..f755a3aff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ export { Alice } from './characters/alice'; export { Bob, RemoteBob } from './characters/bob'; export { Enrico } from './characters/enrico'; -export { PreTDecDecrypter } from './characters/pre-universal-bob'; +export { PreTDecDecrypter } from './characters/pre-recipient'; export { Porter } from './characters/porter'; // Policies diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 677af979b..1c044d2f9 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -1,6 +1,6 @@ import { ethers } from 'ethers'; -import { CbdTDecDecrypter } from '../../characters/cbd-universal-bob'; +import { CbdTDecDecrypter } from '../../characters/cbd-recipient'; import { Enrico } from '../../characters/enrico'; import { ConditionSet } from '../../conditions'; import { DkgClient, DkgRitual, DkgRitualJSON } from '../../dkg'; diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index dd29a16b7..b56f83631 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -9,7 +9,7 @@ import { ethers } from 'ethers'; import { Alice } from '../../characters/alice'; import { Bob } from '../../characters/bob'; import { Enrico } from '../../characters/enrico'; -import { PreTDecDecrypter } from '../../characters/pre-universal-bob'; +import { PreTDecDecrypter } from '../../characters/pre-recipient'; import { ConditionSet, ConditionSetJSON } from '../../conditions'; import { EnactedPolicy, EnactedPolicyJSON } from '../../policies/policy'; import { base64ToU8Receiver, bytesEquals, toJson } from '../../utils'; diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 8959f0006..965242a50 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -1,7 +1,7 @@ import { SecretKey } from '@nucypher/nucypher-core'; import { Cohort, conditions } from '../../src'; -import { CbdTDecDecrypter } from '../../src/characters/cbd-universal-bob'; +import { CbdTDecDecrypter } from '../../src/characters/cbd-recipient'; import { FerveoVariant } from '../../src/dkg'; import { CbdStrategy, From a7a4cc8053d411321b528eb7f8b74683fba888dd Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 6 Jun 2023 11:36:48 +0200 Subject: [PATCH 46/98] update after rebase --- src/characters/cbd-recipient.ts | 15 +- src/characters/pre-recipient.ts | 8 +- src/conditions/condition-set.ts | 10 +- src/conditions/context/context.ts | 6 +- src/conditions/index.ts | 1 + src/dkg.ts | 13 +- src/sdk/cohort.ts | 2 +- src/sdk/strategy/cbd-strategy.ts | 55 +++++-- src/sdk/strategy/pre-strategy.ts | 20 +-- src/utils.ts | 8 +- test/unit/cbd-strategy.test.ts | 245 +++++++++++------------------ test/unit/pre-strategy.test.ts | 249 ++++++++++++------------------ test/unit/testVariables.ts | 93 ----------- test/utils.ts | 14 +- 14 files changed, 289 insertions(+), 450 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index d491c8be2..a0419ec84 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -12,7 +12,7 @@ import { Ciphertext } from 'ferveo-wasm'; import { ConditionSet } from '../conditions'; import { DkgRitual, FerveoVariant } from '../dkg'; -import { base64ToU8Receiver, u8ToBase64Replacer } from '../utils'; +import { fromJSON, toJSON } from '../utils'; import { Porter } from './porter'; @@ -111,15 +111,20 @@ export class CbdTDecDecrypter { } public toJSON(): string { - return JSON.stringify(this.toObj(), u8ToBase64Replacer); + return toJSON(this.toObj()); } - private static fromObj({ porterUri }: CbdTDecDecrypterJSON) { + public static fromObj({ porterUri }: CbdTDecDecrypterJSON) { return new CbdTDecDecrypter(porterUri); } public static fromJSON(json: string) { - const config = JSON.parse(json, base64ToU8Receiver); - return CbdTDecDecrypter.fromObj(config); + return CbdTDecDecrypter.fromObj(fromJSON(json)); + } + + public equals(other: CbdTDecDecrypter): boolean { + return ( + this.porter.porterUrl.toString() === other.porter.porterUrl.toString() + ); } } diff --git a/src/characters/pre-recipient.ts b/src/characters/pre-recipient.ts index 01b7d30ab..a566b8fb3 100644 --- a/src/characters/pre-recipient.ts +++ b/src/characters/pre-recipient.ts @@ -12,7 +12,7 @@ import { ConditionSet } from '../conditions'; import { Keyring } from '../keyring'; import { PolicyMessageKit } from '../kits/message'; import { RetrievalResult } from '../kits/retrieval'; -import { base64ToU8Receiver, bytesEquals, toJson, zip } from '../utils'; +import { base64ToU8Receiver, bytesEquals, toJSON, zip } from '../utils'; import { Porter } from './porter'; @@ -150,10 +150,10 @@ export class PreTDecDecrypter { } public toJSON(): string { - return toJson(this.toObj()); + return toJSON(this.toObj()); } - private static fromObj({ + public static fromObj({ porterUri, policyEncryptingKeyBytes, encryptedTreasureMapBytes, @@ -174,7 +174,7 @@ export class PreTDecDecrypter { return PreTDecDecrypter.fromObj(config); } - public equals(other: tDecDecrypter): boolean { + public equals(other: PreTDecDecrypter): boolean { return ( this.porter.porterUrl.toString() === other.porter.porterUrl.toString() && bytesEquals( diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index 31361b3c3..b80f51049 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -1,7 +1,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { toJson } from '../utils'; +import { objectEquals, toJSON } from '../utils'; import { Condition } from './base'; import { ConditionContext } from './context'; @@ -67,7 +67,7 @@ export class ConditionSet { } public toJson(): string { - return toJson(this.toObj()); + return toJSON(this.toObj()); } public static fromJSON(json: string): ConditionSet { @@ -75,7 +75,7 @@ export class ConditionSet { } public toWASMConditions(): WASMConditions { - return new WASMConditions(toJson(this.toObj())); + return new WASMConditions(toJSON(this.toObj())); } public buildContext( @@ -83,4 +83,8 @@ export class ConditionSet { ): ConditionContext { return new ConditionContext(this.toWASMConditions(), provider); } + + public equals(other: ConditionContext): boolean { + return objectEquals(this.toObj(), other.toObj()); + } } diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index 02e7156a0..f966dfc5c 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -1,7 +1,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { fromJson, toJson } from '../../utils'; +import { fromJSON, toJSON } from '../../utils'; import { USER_ADDRESS_PARAM } from '../const'; import { TypedSignature, WalletAuthenticationProvider } from './providers'; @@ -42,7 +42,7 @@ export class ConditionContext { const requestedParameters = new Set(); // Search conditions for parameters - const { conditions } = fromJson(this.conditions.toString()); + const { conditions } = fromJSON(this.conditions.toString()); for (const cond of conditions) { // Check return value test const rvt = cond.returnValueTest.value; @@ -93,7 +93,7 @@ export class ConditionContext { public toJson = async (): Promise => { const parameters = await this.toObj(); - return toJson(parameters); + return toJSON(parameters); }; public withCustomParams = ( diff --git a/src/conditions/index.ts b/src/conditions/index.ts index 32a45fc46..8047d8129 100644 --- a/src/conditions/index.ts +++ b/src/conditions/index.ts @@ -3,6 +3,7 @@ import * as predefined from './predefined'; export { predefined, base }; export { Condition } from './base/condition'; +export type { ConditionSetJSON } from './condition-set'; export { ConditionSet } from './condition-set'; export { Operator } from './operator'; export type { CustomContextParam } from './context'; diff --git a/src/dkg.ts b/src/dkg.ts index bbdc2ae0d..596e6c31a 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -11,7 +11,7 @@ import { } from 'ferveo-wasm'; import { DkgCoordinatorAgent } from './agents/coordinator'; -import { fromHexString } from './utils'; +import { bytesEquals, fromHexString } from './utils'; // TOOD: Move to nucypher-core export enum FerveoVariant { @@ -47,6 +47,17 @@ export class DkgRitual { DkgPublicParameters.fromBytes(json.dkgPublicParams) ); } + + public equals(other: DkgRitual): boolean { + return ( + this.id === other.id && + bytesEquals(this.dkgPublicKey.toBytes(), other.dkgPublicKey.toBytes()) && + bytesEquals( + this.dkgPublicParams.toBytes(), + other.dkgPublicParams.toBytes() + ) + ); + } } export class DkgClient { diff --git a/src/sdk/cohort.ts b/src/sdk/cohort.ts index 99f2831db..8571f3816 100644 --- a/src/sdk/cohort.ts +++ b/src/sdk/cohort.ts @@ -73,6 +73,6 @@ export class Cohort { } public equals(other: Cohort): boolean { - return objectEquals(this, other); + return objectEquals(this.toObj(), other.toObj()); } } diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 1c044d2f9..ed15d9b15 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -2,20 +2,20 @@ import { ethers } from 'ethers'; import { CbdTDecDecrypter } from '../../characters/cbd-recipient'; import { Enrico } from '../../characters/enrico'; -import { ConditionSet } from '../../conditions'; +import { ConditionSet, ConditionSetJSON } from '../../conditions'; import { DkgClient, DkgRitual, DkgRitualJSON } from '../../dkg'; -import { base64ToU8Receiver, u8ToBase64Replacer } from '../../utils'; +import { fromJSON, objectEquals, toJSON } from '../../utils'; import { Cohort, CohortJSON } from '../cohort'; type StrategyJSON = { cohort: CohortJSON; - conditionSet?: ConditionSet; + conditionSet?: ConditionSetJSON | undefined; }; type DeployedStrategyJSON = { dkgRitual: DkgRitualJSON; cohortConfig: CohortJSON; - conditionSet?: ConditionSet; + conditionSet?: ConditionSetJSON | undefined; }; export class CbdStrategy { @@ -59,24 +59,33 @@ export class CbdStrategy { } public static fromJSON(json: string) { - const config = JSON.parse(json, base64ToU8Receiver); - return CbdStrategy.fromObj(config); + return CbdStrategy.fromObj(fromJSON(json)); } public toJSON() { - return JSON.stringify(this.toObj(), u8ToBase64Replacer); + return toJSON(this.toObj()); } - private static fromObj({ cohort, conditionSet }: StrategyJSON) { - return new CbdStrategy(Cohort.fromObj(cohort), conditionSet); + public static fromObj({ cohort, conditionSet }: StrategyJSON) { + const maybeConditionSet = conditionSet + ? ConditionSet.fromObj(conditionSet) + : undefined; + return new CbdStrategy(Cohort.fromObj(cohort), maybeConditionSet); } public toObj(): StrategyJSON { return { cohort: this.cohort.toObj(), - conditionSet: this.conditionSet, + conditionSet: this.conditionSet?.toObj(), }; } + + public equals(other: CbdStrategy) { + return ( + this.cohort.equals(other.cohort) && + objectEquals(this.conditionSet?.toObj(), other.conditionSet?.toObj()) + ); + } } export class DeployedCbdStrategy { @@ -89,12 +98,12 @@ export class DeployedCbdStrategy { ) {} public static fromJSON(json: string) { - const config = JSON.parse(json, base64ToU8Receiver); + const config = fromJSON(json); return DeployedCbdStrategy.fromObj(config); } public toJSON() { - return JSON.stringify(this.toObj(), u8ToBase64Replacer); + return toJSON(this.toObj()); } private static fromObj({ @@ -104,15 +113,21 @@ export class DeployedCbdStrategy { }: DeployedStrategyJSON) { const ritual = DkgRitual.fromObj(dkgRitual); const cohort = Cohort.fromObj(cohortConfig); - const encrypter = new Enrico(ritual.dkgPublicKey, undefined, conditionSet); - + const maybeConditionSet = conditionSet + ? ConditionSet.fromObj(conditionSet) + : undefined; + const encrypter = new Enrico( + ritual.dkgPublicKey, + undefined, + maybeConditionSet + ); const decrypter = new CbdTDecDecrypter(cohort.configuration.porterUri); return new DeployedCbdStrategy( cohort, ritual, encrypter, decrypter, - conditionSet + maybeConditionSet ); } @@ -120,7 +135,15 @@ export class DeployedCbdStrategy { return { dkgRitual: this.dkgRitual.toObj(), cohortConfig: this.cohort.toObj(), - conditionSet: this.conditionSet, + conditionSet: this.conditionSet?.toObj(), }; } + + public equals(other: DeployedCbdStrategy) { + return ( + this.cohort.equals(other.cohort) && + objectEquals(this.conditionSet?.toObj(), other.conditionSet?.toObj()) && + objectEquals(this.dkgRitual.toObj(), other.dkgRitual.toObj()) + ); + } } diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index b56f83631..171375544 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -12,10 +12,10 @@ import { Enrico } from '../../characters/enrico'; import { PreTDecDecrypter } from '../../characters/pre-recipient'; import { ConditionSet, ConditionSetJSON } from '../../conditions'; import { EnactedPolicy, EnactedPolicyJSON } from '../../policies/policy'; -import { base64ToU8Receiver, bytesEquals, toJson } from '../../utils'; +import { base64ToU8Receiver, bytesEquals, toJSON } from '../../utils'; import { Cohort, CohortJSON } from '../cohort'; -type StrategyJSON = { +type PreStrategyJSON = { cohort: CohortJSON; aliceSecretKeyBytes: Uint8Array; bobSecretKeyBytes: Uint8Array; @@ -124,7 +124,7 @@ export class PreStrategy { } public toJSON() { - return toJson(this.toObj()); + return toJSON(this.toObj()); } public static fromObj({ @@ -134,7 +134,7 @@ export class PreStrategy { conditionSet, startDate, endDate, - }: StrategyJSON) { + }: PreStrategyJSON) { return new PreStrategy( Cohort.fromObj(cohort), SecretKey.fromBEBytes(aliceSecretKeyBytes), @@ -145,7 +145,7 @@ export class PreStrategy { ); } - public toObj(): StrategyJSON { + public toObj(): PreStrategyJSON { return { cohort: this.cohort.toObj(), aliceSecretKeyBytes: this.aliceSecretKey.toBEBytes(), @@ -156,7 +156,7 @@ export class PreStrategy { }; } - public equals(other: Strategy) { + public equals(other: PreStrategy) { return ( this.cohort.equals(other.cohort) && // TODO: Add equality to WASM bindings @@ -193,10 +193,10 @@ export class DeployedPreStrategy { } public toJSON() { - return toJson(this.toObj()); + return toJSON(this.toObj()); } - private static fromObj({ + public static fromObj({ policy, cohortConfig, bobSecretKeyBytes, @@ -252,7 +252,7 @@ export class DeployedPreStrategy { ); } - private toObj(): DeployedStrategyJSON { + public toObj(): DeployedStrategyJSON { const policy = { ...this.policy, id: this.policy.id.toBytes(), @@ -267,7 +267,7 @@ export class DeployedPreStrategy { }; } - public equals(other: DeployedStrategy) { + public equals(other: DeployedPreStrategy) { return ( this.label === other.label && this.cohort.equals(other.cohort) && diff --git a/src/utils.ts b/src/utils.ts index dcacd59aa..8b80d5463 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -49,10 +49,10 @@ const sortedSerializingReplacer = (_key: string, value: unknown): unknown => { return sortedReplacer(_key, serializedValue); }; -export const toJson = (obj: unknown) => +export const toJSON = (obj: unknown) => JSON.stringify(obj, sortedSerializingReplacer); -export const fromJson = (json: string) => JSON.parse(json, base64ToU8Receiver); +export const fromJSON = (json: string) => JSON.parse(json, base64ToU8Receiver); export const zip = ( a: ReadonlyArray, @@ -65,5 +65,5 @@ export const bytesEquals = (first: Uint8Array, second: Uint8Array): boolean => first.length === second.length && first.every((value, index) => value === second[index]); -export const objectEquals = (a: unknown, b: unknown): boolean => - deepEqual(a, b, { strict: true }); +export const objectEquals = (a: unknown, b: unknown, strict = true): boolean => + deepEqual(a, b, { strict }); diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 965242a50..9e4b097bb 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -1,6 +1,6 @@ import { SecretKey } from '@nucypher/nucypher-core'; -import { Cohort, conditions } from '../../src'; +import { conditions } from '../../src'; import { CbdTDecDecrypter } from '../../src/characters/cbd-recipient'; import { FerveoVariant } from '../../src/dkg'; import { @@ -11,177 +11,104 @@ import { toBytes } from '../../src/utils'; import { fakeDkgFlow, fakeDkgRitual, - fakeDkgTDecFlowE2e, fakeTDecFlow, fakeUrsulas, fakeWeb3Provider, + makeCohort, mockDecrypt, mockGetUrsulas, mockInitializeRitual, } from '../utils'; -import { - aliceSecretKeyBytes, - cbdDecrypterJSON, - cbdStrategyJSON, - deployedCbdStrategyObj, - preStrategyJSON, -} from './testVariables'; +import { aliceSecretKeyBytes } from './testVariables'; const { predefined: { ERC721Ownership }, ConditionSet, } = conditions; -describe('CbdStrategy', () => { - const cohortConfig = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - Date.now = jest.fn(() => 1487076708000); +// Shared test variables +const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); +const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); +const ownsNFT = new ERC721Ownership({ + contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', + parameters: [3591], + chain: 5, +}); +const conditionSet = new ConditionSet([ownsNFT]); +const mockedUrsulas = fakeUrsulas().slice(0, 3); +const variant = FerveoVariant.Precomputed; - afterEach(() => { - jest.restoreAllMocks(); - }); +const makeCbdStrategy = async () => { + const cohort = await makeCohort(mockedUrsulas); + const strategy = CbdStrategy.create(cohort, conditionSet); + expect(strategy.cohort).toEqual(cohort); + return strategy; +}; - it('can create CbdStrategy from configuration', async () => { - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); +async function makeDeployedCbdStrategy() { + const strategy = await makeCbdStrategy(); - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = CbdStrategy.create(testCohort, undefined); + const mockedDkg = fakeDkgFlow(variant, 0); + const mockedDkgRitual = fakeDkgRitual(mockedDkg); + const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + const initializeRitualSpy = mockInitializeRitual(mockedDkgRitual); + const deployedStrategy = await strategy.deploy(web3Provider); - const expectedUrsulas = mockedUrsulas.map( - (ursula) => ursula.checksumAddress - ); - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(testStrategy.cohort.ursulaAddresses).toEqual(expectedUrsulas); - }); + expect(getUrsulasSpy).toHaveBeenCalled(); + expect(initializeRitualSpy).toHaveBeenCalled(); - // TODO: Update this test - it('can export to JSON', async () => { - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = CbdStrategy.create(testCohort, undefined); + return { mockedDkg, deployedStrategy }; +} - const configJSON = testStrategy.toJSON(); - expect(configJSON).toEqual(cbdStrategyJSON); - expect(getUrsulasSpy).toHaveBeenCalled(); +describe('CbdStrategy', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('creates a strategy', async () => { + await makeCbdStrategy(); }); - it('can import from JSON', async () => { - const testStrategy = CbdStrategy.fromJSON(preStrategyJSON); - const expectedUrsulas = [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ]; - expect(testStrategy.cohort.ursulaAddresses).toEqual(expectedUrsulas); + it('can deploy and return a CbdDeployedStrategy', async () => { + await makeDeployedCbdStrategy(); }); - it('can deploy and return DeployedStrategy', async () => { - const variant = FerveoVariant.Precomputed; - const fakeE2ERitual = fakeDkgTDecFlowE2e(variant); - const fakeRitual = fakeDkgRitual(fakeE2ERitual); - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - const initRitualSpy = mockInitializeRitual(fakeRitual); - - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = CbdStrategy.create(testCohort, undefined); - const testDeployed = await testStrategy.deploy(aliceProvider); - expect(testDeployed.dkgRitual.id).toEqual(fakeRitual.id); - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(initRitualSpy).toHaveBeenCalled(); + describe('serialization', () => { + it('serializes to a plain object', async () => { + const strategy = await makeCbdStrategy(); + const asObj = strategy.toObj(); + const fromObj = CbdStrategy.fromObj(asObj); + expect(fromObj.equals(strategy)).toBeTruthy(); + }); + + it('serializes to a JSON', async () => { + const strategy = await makeCbdStrategy(); + const asJson = strategy.toJSON(); + const fromJson = CbdStrategy.fromJSON(asJson); + expect(fromJson.equals(strategy)).toBeTruthy(); + }); }); }); describe('CbdDeployedStrategy', () => { - const cohortConfig = { - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }; - const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); - afterEach(() => { jest.restoreAllMocks(); }); - it('can import from JSON', async () => { - const json = JSON.stringify(deployedCbdStrategyObj); - const importedStrategy = DeployedCbdStrategy.fromJSON(json); - const configJSON = importedStrategy.toJSON(); - expect(configJSON).toEqual(json); - }); - - it('can export to JSON', async () => { - const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - const fakeDkg = fakeDkgFlow(FerveoVariant.Precomputed, 0); - const fakeRitual = fakeDkgRitual(fakeDkg); - const initializeRitualSpy = mockInitializeRitual(fakeRitual); - - const testCohort = await Cohort.create(cohortConfig); - const testStrategy = CbdStrategy.create(testCohort, undefined); - const testDeployed = await testStrategy.deploy(aliceProvider); - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(initializeRitualSpy).toHaveBeenCalled(); - - const asJson = testDeployed.toJSON(); - expect(asJson).toBeDefined(); - const asObj = JSON.parse(asJson); - // TODO: Will not match until we hardcode validator keypairs in tests - // expect(asJson).toEqual(deployedCbdStrategyJSON); - // So instead we do: - delete asObj.dkgRitual.dkgPublicKey; - const expectedObj: Partial = Object.assign( - deployedCbdStrategyObj - ); - delete expectedObj.dkgRitual.dkgPublicKey; - expect(asObj).toEqual(expectedObj); - }); - it('can encrypt and decrypt', async () => { - const variant = FerveoVariant.Precomputed; - const fakeDkg = fakeDkgFlow(variant, 0); - const fakeRitual = fakeDkgRitual(fakeDkg); - const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - const initializeRitualSpy = mockInitializeRitual(fakeRitual); - - const cohort = await Cohort.create(cohortConfig); - const strategy = CbdStrategy.create(cohort, undefined); - - const deployedStrategy = await strategy.deploy(web3Provider); - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(initializeRitualSpy).toHaveBeenCalled(); - - const encrypter = deployedStrategy.encrypter; - const decrypter = deployedStrategy.decrypter; - - const ownsNFT = new ERC721Ownership({ - contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', - parameters: [3591], - chain: 5, - }); - const conditionSet = new ConditionSet([ownsNFT]); + const { mockedDkg, deployedStrategy } = await makeDeployedCbdStrategy(); const message = 'this is a secret'; - const { ciphertext, aad } = encrypter.encryptMessageCbd( + const { ciphertext, aad } = deployedStrategy.encrypter.encryptMessageCbd( message, conditionSet ); // Setup mocks for `retrieveAndDecrypt` const { decryptionShares } = fakeTDecFlow({ - ...fakeDkg, + ...mockedDkg, variant, message: toBytes(message), aad, @@ -190,33 +117,49 @@ describe('CbdDeployedStrategy', () => { const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); const decryptSpy = mockDecrypt(decryptionShares); - const decryptedMessage = await decrypter.retrieveAndDecrypt( - web3Provider, - conditionSet, - deployedStrategy.dkgRitual, - variant, - ciphertext, - aad - ); + const decryptedMessage = + await deployedStrategy.decrypter.retrieveAndDecrypt( + aliceProvider, + conditionSet, + deployedStrategy.dkgRitual, + variant, + ciphertext, + aad + ); expect(getUrsulasSpy2).toHaveBeenCalled(); expect(decryptSpy).toHaveBeenCalled(); expect(decryptedMessage[0]).toEqual(toBytes(message)); }); -}); -describe('cbd tdec decrypter', () => { - const importedStrategy = DeployedCbdStrategy.fromJSON( - JSON.stringify(deployedCbdStrategyObj) - ); + describe('serialization', () => { + it('serializes to a plaintext object', async () => { + const { deployedStrategy } = await makeDeployedCbdStrategy(); + const asJson = deployedStrategy.toJSON(); + const fromJson = DeployedCbdStrategy.fromJSON(asJson); + expect(fromJson.equals(deployedStrategy)).toBeTruthy(); + }); + + it('serializes to a JSON', async () => { + const { deployedStrategy } = await makeDeployedCbdStrategy(); + const asJson = deployedStrategy.toJSON(); + const fromJson = DeployedCbdStrategy.fromJSON(asJson); + expect(fromJson.equals(deployedStrategy)).toBeTruthy(); + }); + }); +}); - it('can export to JSON', () => { - const configJSON = importedStrategy.decrypter.toJSON(); - expect(configJSON).toEqual(cbdDecrypterJSON); +describe('CbdTDecDecrypter', () => { + it('serializes to a plain object', async () => { + const { deployedStrategy } = await makeDeployedCbdStrategy(); + const configObj = deployedStrategy.decrypter.toObj(); + const fromObj = CbdTDecDecrypter.fromObj(configObj); + expect(fromObj.equals(deployedStrategy.decrypter)).toBeTruthy(); }); - it('can import from JSON', () => { - const decrypter = CbdTDecDecrypter.fromJSON(cbdDecrypterJSON); - const configJSON = decrypter.toJSON(); - expect(configJSON).toEqual(cbdDecrypterJSON); + it('serializes to a JSON', async () => { + const { deployedStrategy } = await makeDeployedCbdStrategy(); + const configJSON = deployedStrategy.decrypter.toJSON(); + const fromJSON = CbdTDecDecrypter.fromJSON(configJSON); + expect(fromJSON.equals(deployedStrategy.decrypter)).toBeTruthy(); }); }); diff --git a/test/unit/pre-strategy.test.ts b/test/unit/pre-strategy.test.ts index 4cbd38143..8f5a401ef 100644 --- a/test/unit/pre-strategy.test.ts +++ b/test/unit/pre-strategy.test.ts @@ -1,8 +1,4 @@ -import { - EncryptedTreasureMap, - SecretKey, - VerifiedKeyFrag, -} from '@nucypher/nucypher-core'; +import { SecretKey, VerifiedKeyFrag } from '@nucypher/nucypher-core'; import { conditions, @@ -11,7 +7,7 @@ import { PreTDecDecrypter, } from '../../src'; import { Ursula } from '../../src/characters/porter'; -import { fromBase64, toBytes } from '../../src/utils'; +import { toBytes } from '../../src/utils'; import { fakeUrsulas, fakeWeb3Provider, @@ -24,12 +20,7 @@ import { mockRetrieveCFragsRequest, } from '../utils'; -import { - aliceSecretKeyBytes, - bobSecretKeyBytes, - deployedPreStrategyJSON, - encryptedTreasureMapBase64, -} from './testVariables'; +import { aliceSecretKeyBytes, bobSecretKeyBytes } from './testVariables'; const { predefined: { ERC721Ownership }, @@ -40,7 +31,7 @@ const { const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes); const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); -Date.now = jest.fn(() => 1487076708000); +const bobProvider = fakeWeb3Provider(bobSecretKey.toBEBytes()); const ownsNFT = new ERC721Ownership({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', parameters: [3591], @@ -49,72 +40,71 @@ const ownsNFT = new ERC721Ownership({ const conditionSet = new ConditionSet([ownsNFT]); const mockedUrsulas = fakeUrsulas().slice(0, 3); +const makePreStrategy = async () => { + const cohort = await makeCohort(mockedUrsulas); + const strategy = PreStrategy.create( + cohort, + conditionSet, + aliceSecretKey, + bobSecretKey + ); + expect(strategy.cohort).toEqual(cohort); + return strategy; +}; + +const makeDeployedPreStrategy = async () => { + const strategy = await makePreStrategy(); + const generateKFragsSpy = mockGenerateKFrags(); + const publishToBlockchainSpy = mockPublishToBlockchain(); + const makeTreasureMapSpy = mockMakeTreasureMap(); + const encryptTreasureMapSpy = mockEncryptTreasureMap(); + + const deployedStrategy = await strategy.deploy('test', aliceProvider); + + expect(generateKFragsSpy).toHaveBeenCalled(); + expect(publishToBlockchainSpy).toHaveBeenCalled(); + expect(makeTreasureMapSpy).toHaveBeenCalled(); + expect(encryptTreasureMapSpy).toHaveBeenCalled(); + + expect(deployedStrategy.conditionSet).toEqual(conditionSet); + expect(deployedStrategy.cohort).toEqual(strategy.cohort); + + const ursulaAddresses = ( + makeTreasureMapSpy.mock.calls[0][0] as readonly Ursula[] + ).map((u) => u.checksumAddress); + const verifiedKFrags = makeTreasureMapSpy.mock + .calls[0][1] as readonly VerifiedKeyFrag[]; + + return { deployedStrategy, ursulaAddresses, verifiedKFrags }; +}; + describe('PreStrategy', () => { afterEach(() => { jest.restoreAllMocks(); }); - it('creates a PreStrategy', async () => { - const cohort = await makeCohort(mockedUrsulas); - const strategy = PreStrategy.create( - cohort, - conditionSet, - aliceSecretKey, - bobSecretKey - ); - expect(strategy.cohort).toEqual(cohort); + it('creates a strategy', async () => { + await makePreStrategy(); }); - it('serializes to plain object', async () => { - const cohort = await makeCohort(mockedUrsulas); - const strategy = PreStrategy.create( - cohort, - conditionSet, - aliceSecretKey, - bobSecretKey - ); - const asObject = strategy.toObj(); - const fromObject = PreStrategy.fromObj(asObject); - expect(fromObject.equals(strategy)).toBeTruthy(); + it('deploys a strategy', async () => { + await makeDeployedPreStrategy(); }); - it('serializes to JSON', async () => { - const cohort = await makeCohort(mockedUrsulas); - const strategy = PreStrategy.create( - cohort, - conditionSet, - aliceSecretKey, - bobSecretKey - ); - - const asJson = strategy.toJSON(); - const fromJSON = PreStrategy.fromJSON(asJson); - expect(fromJSON.equals(strategy)).toBeTruthy(); - }); - - it('deploys strategy', async () => { - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const generateKFragsSpy = mockGenerateKFrags(); - const publishToBlockchainSpy = mockPublishToBlockchain(); - const makeTreasureMapSpy = mockMakeTreasureMap(); - const encryptTreasureMapSpy = mockEncryptTreasureMap(); - - const cohort = await makeCohort(mockedUrsulas); - const strategy = PreStrategy.create( - cohort, conditionSet, - aliceSecretKey - ); - const label = 'test'; - - const deployedStrategy = await strategy.deploy(label, aliceProvider); - expect(generateKFragsSpy).toHaveBeenCalled(); - expect(publishToBlockchainSpy).toHaveBeenCalled(); - expect(encryptTreasureMapSpy).toHaveBeenCalled(); - expect(makeTreasureMapSpy).toHaveBeenCalled(); - - expect(deployedStrategy.cohort).toEqual(cohort); - expect(deployedStrategy.conditionSet).toEqual(conditionSet); - expect(deployedStrategy.label).toEqual(label); + describe('serialization', () => { + it('serializes to plain object', async () => { + const strategy = await makePreStrategy(); + const asObject = strategy.toObj(); + const fromObject = PreStrategy.fromObj(asObject); + expect(fromObject.equals(strategy)).toBeTruthy(); + }); + + it('serializes to JSON', async () => { + const strategy = await makePreStrategy(); + const asJson = strategy.toJSON(); + const fromJSON = PreStrategy.fromJSON(asJson); + expect(fromJSON.equals(strategy)).toBeTruthy(); + }); }); }); @@ -123,106 +113,61 @@ describe('PreDeployedStrategy', () => { jest.restoreAllMocks(); }); - it('serializes to JSON', async () => { - const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const generateKFragsSpy = mockGenerateKFrags(); - const publishToBlockchainSpy = mockPublishToBlockchain(); - const makeTreasureMapSpy = mockMakeTreasureMap(); - const encryptTreasureMapSpy = mockEncryptTreasureMap( - EncryptedTreasureMap.fromBytes(fromBase64(encryptedTreasureMapBase64)) - ); - - const cohort = await makeCohort(mockedUrsulas); - const strategy = PreStrategy.create( - cohort, - conditionSet, - aliceSecretKey, - bobSecretKey - ); - - const strategyAsJson = strategy.toJSON(); - const strategyFromJson = Strategy.fromJSON(strategyAsJson); - expect(strategyFromJson.equals(strategy)).toBeTruthy(); - - const deployedStrategy = await strategy.deploy('test', aliceProvider); - expect(generateKFragsSpy).toHaveBeenCalled(); - expect(publishToBlockchainSpy).toHaveBeenCalled(); - expect(makeTreasureMapSpy).toHaveBeenCalled(); - expect(encryptTreasureMapSpy).toHaveBeenCalled(); - - const asJson = deployedStrategy.toJSON(); - const fromJson = DeployedPreStrategy.fromJSON(asJson); - expect(fromJson.equals(deployedStrategy)).toBeTruthy(); - }); - it('encrypts and decrypts', async () => { - const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - const bobProvider = fakeWeb3Provider(bobSecretKey.toBEBytes()); - const mockedUrsulas = fakeUrsulas().slice(0, 3); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); - const generateKFragsSpy = mockGenerateKFrags(); - const publishToBlockchainSpy = mockPublishToBlockchain(); - const makeTreasureMapSpy = mockMakeTreasureMap(); - const encryptTreasureMapSpy = mockEncryptTreasureMap(); - - const cohort = await makeCohort(mockedUrsulas); - const strategy = PreStrategy.create( - cohort, - conditionSet, - aliceSecretKey, - bobSecretKey - ); - const deployedStrategy = await strategy.deploy('test', aliceProvider); - - expect(getUrsulasSpy).toHaveBeenCalled(); - expect(generateKFragsSpy).toHaveBeenCalled(); - expect(publishToBlockchainSpy).toHaveBeenCalled(); - expect(encryptTreasureMapSpy).toHaveBeenCalled(); - expect(makeTreasureMapSpy).toHaveBeenCalled(); - - const encrypter = deployedStrategy.encrypter; - const decrypter = deployedStrategy.decrypter; + const { deployedStrategy, ursulaAddresses, verifiedKFrags } = + await makeDeployedPreStrategy(); const plaintext = 'this is a secret'; - encrypter.conditions = conditionSet; - const encryptedMessageKit = encrypter.encryptMessagePre(plaintext); + const encryptedMessageKit = + deployedStrategy.encrypter.encryptMessagePre(plaintext); // Setup mocks for `retrieveAndDecrypt` - const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); - const ursulaAddresses = ( - makeTreasureMapSpy.mock.calls[0][0] as readonly Ursula[] - ).map((u) => u.checksumAddress); - const verifiedKFrags = makeTreasureMapSpy.mock - .calls[0][1] as readonly VerifiedKeyFrag[]; + const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); const retrieveCFragsSpy = mockRetrieveCFragsRequest( ursulaAddresses, verifiedKFrags, encryptedMessageKit.capsule ); - const decryptedMessage = await decrypter.retrieveAndDecrypt( - [encryptedMessageKit], - bobProvider - ); - expect(getUrsulasSpy2).toHaveBeenCalled(); + const decryptedMessage = + await deployedStrategy.decrypter.retrieveAndDecrypt( + [encryptedMessageKit], + bobProvider + ); + expect(getUrsulasSpy).toHaveBeenCalled(); expect(retrieveCFragsSpy).toHaveBeenCalled(); expect(decryptedMessage[0]).toEqual(toBytes(plaintext)); + }); - const serialized = deployedStrategy.toJSON(); - const deserialized = DeployedStrategy.fromJSON(serialized); - expect(deserialized.equals(deployedStrategy)).toBeTruthy(); + describe('serialization', () => { + it('serializes to a plain object', async () => { + const { deployedStrategy } = await makeDeployedPreStrategy(); + const asObj = deployedStrategy.toObj(); + const fromJson = DeployedPreStrategy.fromObj(asObj); + expect(fromJson.equals(deployedStrategy)).toBeTruthy(); + }); + + it('serializes to a JSON', async () => { + const { deployedStrategy } = await makeDeployedPreStrategy(); + const asJson = deployedStrategy.toJSON(); + const fromJson = DeployedPreStrategy.fromJSON(asJson); + expect(fromJson.equals(deployedStrategy)).toBeTruthy(); + }); }); }); -describe('pre tdec decrypter', () => { - const decrypter = DeployedPreStrategy.fromJSON( - deployedPreStrategyJSON - ).decrypter; +describe('PreTDecDecrypter', () => { + it('serializes to a plain object', async () => { + const { deployedStrategy } = await makeDeployedPreStrategy(); + const asObj = deployedStrategy.decrypter.toObj(); + const fromJson = PreTDecDecrypter.fromObj(asObj); + expect(fromJson.equals(deployedStrategy.decrypter)).toBeTruthy(); + }); - it('serializes to JSON', () => { - const asJson = decrypter.toJSON(); + it('serializes to JSON', async () => { + const { deployedStrategy } = await makeDeployedPreStrategy(); + const asJson = deployedStrategy.decrypter.toJSON(); const fromJson = PreTDecDecrypter.fromJSON(asJson); - expect(fromJson.equals(decrypter)).toBeTruthy(); + expect(fromJson.equals(deployedStrategy.decrypter)).toBeTruthy(); }); }); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index 909908c09..99c219be0 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -1,81 +1,3 @@ -export const preStrategyJSON = JSON.stringify({ - cohort: { - ursulaAddresses: [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ], - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }, - aliceSecretKeyBytes: 'base64:N1K+vcukPJQkVi57P5jXca5W9CwX48VEBVV0H9CYWDU=', - bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', - startDate: '2017-02-14T12:51:48.000Z', - endDate: '2123-03-16T12:51:48.000Z', -}); - -export const cbdStrategyJSON = JSON.stringify({ - cohort: { - ursulaAddresses: [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ], - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }, -}); - -export const encryptedTreasureMapBase64 = - 'RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw='; - -export const deployedPreStrategyJSON = JSON.stringify({ - policy: { - id: 'base64:tKuR/HipMeU4trCdytbZCQ==', - label: 'test', - policyKey: 'base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C', - encryptedTreasureMap: - 'base64:RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw=', - aliceVerifyingKey: 'base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj', - size: 3, - startTimestamp: '2017-02-14T12:51:48.000Z', - endTimestamp: '2123-03-16T12:51:48.000Z', - txHash: '0x1234567890123456789012345678901234567890', - }, - cohortConfig: { - ursulaAddresses: [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ], - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }, - bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', -}); - -export const deployedCbdStrategyObj = { - dkgRitual: { - id: 1, - dkgPublicKey: - 'base64:MAAAAAAAAACGxOoiK8/cO6vl2gb12nlQSFgUP+Mo5OHt6vhBqlMsOzWiSuejdy2z3AxAnz2Du8k=', - dkgPublicParams: - 'base64:MAAAAAAAAAC38dOnMZfXlCaVY4xPqawPw2iMT5d0uQWhTjo/FxusWGxV6D/5ehrv+zrwCtsixruIAAAAAAAAAAQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAA3YCAAPs0AQDds7MUY0AAAAAAAAAAAAAAAD//////lv+/wKkvVMF2KEJCNg5M0h9nSlTp+1zAQAAAP///v/+W/uJAKS6ZzTTnpM5C+ilR32dKVOn7XM=', - }, - cohortConfig: { - ursulaAddresses: [ - '0x5cf1703a1c99a4b42eb056535840e93118177232', - '0x7fff551249d223f723557a96a0e1a469c79cc934', - '0x9c7c824239d3159327024459ad69bb215859bd25', - ], - threshold: 2, - shares: 3, - porterUri: 'https://_this.should.crash', - }, -}; export const aliceSecretKeyBytes = new Uint8Array([ 55, 82, 190, 189, 203, 164, 60, 148, 36, 86, 46, 123, 63, 152, 215, 113, 174, 86, 244, 44, 23, 227, 197, 68, 5, 85, 116, 31, 208, 152, 88, 53, @@ -86,21 +8,6 @@ export const bobSecretKeyBytes = new Uint8Array([ 229, 2, 106, 176, 205, 33, 168, 23, 213, 233, 200, 238, 11, 193, 153, ]); -export const preDecrypterJSON = JSON.stringify({ - porterUri: 'https://_this.should.crash/', - policyEncryptingKeyBytes: - 'base64:A/u/2+G8Hn0UPwRZqVfa41XiT/6W2+98dotTob7xKg5C', - encryptedTreasureMapBytes: - 'base64:RU1hcAADAACSk8QhA5eeVAHFNIHDh/+Ceg9QNqI0TRZDD7ELQFISAPgUGXl1xCEDUDqfEe1maAyxwFF2fmfVXtfDf1yHdvackoiTmI9R8fbEIGmG/yDHtOwmO0VYin3ULlxCtODMVxY99Zf6D3U3VAhuxQdSTPEVl80wcZd2LUuW5e8gkBHKvi7u+B9kb0DFjw99NUWm7loKKQrojitfodp+jIqaVS9ItfXN3mjlzAbGztUegJ9QCg03r2pmW64eWaW1LzRnbvPPi1yE5rTyKEibTkUB3odMdzG0Z2jW3jdBq2Gl3VtTS504ElCv38hjLZKV/oMtGkRc9sQpZoGl9OLoNbh7VYijFGhF2gMP585If1+qMlql07zZjU/ZmQh2VID4i53qU4bgYA00E9vG6CA8SPuW9AajyHXrUh85Br12v+76f2JGnXO/0Z8vKPUXtnzJVptyYNNKBNY+47u+cFzh66SVWl4w5FPVe2br8uGQ4xeMNoIuol9clHsvbchfXCCPMEToMPTT8D4zTq/OnSp6o63vIWgvsc4zi5aGzJq+2feBhtZOkotuBPDvvoGTadUIZa41DfMmMgfLYerO1p8eS6ZUqQgqA/zTh5Dc7Pv0U3lYCwLJhYzr39cZrrAOkpYFQbxrQFRwCJwjZOijq0sF7TsZqL8qaiMymAf3bJskaTYMRs6i0XIa21trjG2ZzrINh+XBR16IoVol0w8dx4FkIlhHb4H16ldX3+xBa21jFqYg1XxCnBVftQP37b+4CIPuxlNjIUrBcb/YbFTa1fCdef6ENoTxuqi6CnbejUZ5kcarfNz3ZzTv8Wa4X3YU85xje/CEk2xlvgMsJVIuq124AMh5/j/ln1NErBI4zu7K96w6IpEeyan0p8eQ27+X7UvHqUP0hfcLikOUa/sPC98k/vH2xr//mLdJtn3v/NKozt/LPzTbEq+gjUWuzhpDV08927GilJ6ypangzlhEwizELQTTNBpesLoM0br2YQUxjyZKJWfF5PbND5BBW9LqV8O91tuHuah1NHCV58x2ALODVozoDXSMXP8SZa1P1UXMYLq9gHFW+Nbm+EcqPXUBX+ixQSGrxp7M7ZsWLa/Y4S72l87+cHJt6ykFxFFnmn6L3NJP1Rd82NUh6pp183vyoCB52Jxxax+RvFIbOUySz/ss2mSjIqu5u1HVL6mXUCu9386+irypeqQp47wQCPcYmxEtE714h4HHOUK2ThAQ+nhOJWUY9Wp/VVFce45p6hqsTCCM7Mt59qJeJ3a1FchMxZnCXEVG8JyfFSjxwylOAVxvUl/DJsQBp4wyJXTLstdb6CbbtSYlNSHvsANQ0bI3WKR+6+E9bced8hlX/ufSVhmRxP98sdeyE7XahM1kHBF2PF3FDmDNE9nEzELp4mUfCfg5iitT4EoLr/nj3lHLVBEq4my0H0WrpvQEz2Y8x54JFUaqdRLWB59KyJYJW79fny2+OPmNoLg7EU3gik+CMMmlWySdevDElVbd7JA1GS4WuDymgoGWRQTuxsY5yhE917EakEyPGmqoITGsHDMkJrwvd/GP95aI0bLKrG49t4bRW0K5ITAU8JcAItTxPyMOqP2L2EiYKqeXya1cdWZnYk3pyTRqL7xZ0ruYiSJrbvoG9dEtRMck7nrij2vkBCND0Syqo2LHnO1GRvY7VnV7oAN+MYlPtnyhFHuEOTcOa44fPRs3TIVSOoNQ2j5iDd75HKRUjPIP4Xe9kgRhXCxCDo3tmfgl5atTY00udmMUO2C9GmhFPsHgwCFKeYbXFAMK5+LLnCtt86Mw03K0m3gd8NWOqJd4mQXXQ07uajyU08Lo+Z2S64VaDOtgClf+s25ZvawMFYhRQJhCCO/MPVczOjnjGoc6jrMttSTAjCDMOlzchXIJV2nYUeFCx0dcnEoF/III/lN8WBYYIOkjXT27rAtKT886MNMQzpIj6fpfylDvQnUc5oOBJdvFF0GWnT7RwulbbxFka8618grLdVStwuHojfn6cfx/oyxOJP9e8yCrGfAhT/pSyQT4pkSOy/4PHqJttXvxuJf4xMTx19Rm9l6n6p0yau3TssPhpQMcsdh1Cc+UA+qCQ+UocTQQoPBSy4Iket0WX66JCMEr/wmsPUiZTvX38kk9uuMrhGCY/KhC00qt9lR/kXM7NKhsLbQMFcSEJWusbN847dBGo1ILIiUvuQWCZMYXTwXgvtzNiLDDLquuDVpMFG9SqSZZwDe4yXXCr4idJTPSxPiod1502B7S7bj5xk8+dpSjQeSu0zGtT+9rY2PydiKxYd9VGonfpwMAtoaO0zDL7MqnX4w6ND2boJQcR8zpMGmAr+0P//YCgpjiGgvBe4IwuEauEQkj5dPkOYI/YGtqhORmEXJu6l7ux+XoQcIKySf6+A+Z8ogAmKObKQlEkowp3DdN8d3BiB/DZ33MJOmWHqeAAXBPp0Dd9gDY5ZVnh+IboEEbvmXI8zNuyFns1+hO+tqWVm2l8Aqz6T/sAJg2o+WZJKSLNQmINVCmqI+z0WqLdhx9Fa3NkFlLp2ImLj26yJV5RRBCgua0jB89KwGfh06sYB3eGvC7vr8Q3fHo+2TNHjzjJcaFemHpCwYLc4Urr9/q4LXN+QH5ckeacvqr+TVvGwHi2fO3SgH4YBw=', - publisherVerifyingKeyBytes: - 'base64:A3yU8aavNj4LJ97eFAaYpU97q70oSogzBZAlo5tj/1Kj', - bobSecretKeyBytes: 'base64:dOs3NGmtXJMdjVMaXf3/m5PlAmqwzSGoF9XpyO4LwZk=', -}); - -export const cbdDecrypterJSON = JSON.stringify({ - porterUri: 'https://_this.should.crash/', -}); - export const TEST_CONTRACT_ADDR = '0x0000000000000000000000000000000000000001'; export const TEST_CONTRACT_ADDR_2 = '0x0000000000000000000000000000000000000002'; diff --git a/test/utils.ts b/test/utils.ts index cfd1a96f7..c82a0350e 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -250,11 +250,11 @@ export const fakeDkgFlow = ( '0x' + '0'.repeat(40 - i.toString(16).length) + i.toString(16); return EthereumAddress.fromString(ethAddr); }; - const validator_keypairs: Keypair[] = []; + const validatorKeypairs: Keypair[] = []; const validators: Validator[] = []; for (let i = 0; i < sharesNum; i++) { const keypair = Keypair.random(); - validator_keypairs.push(keypair); + validatorKeypairs.push(keypair); const validator = new Validator(genEthAddr(i), keypair.publicKey); validators.push(validator); } @@ -288,7 +288,7 @@ export const fakeDkgFlow = ( tau, sharesNum, threshold, - validator_keypairs, + validatorKeypairs, validators, transcripts, dkg, @@ -299,7 +299,7 @@ export const fakeDkgFlow = ( interface FakeDkgRitualFlow { validators: Validator[]; - validator_keypairs: Keypair[]; + validatorKeypairs: Keypair[]; tau: number; sharesNum: number; threshold: number; @@ -313,7 +313,7 @@ interface FakeDkgRitualFlow { export const fakeTDecFlow = ({ validators, - validator_keypairs, + validatorKeypairs, tau, sharesNum, threshold, @@ -329,7 +329,7 @@ export const fakeTDecFlow = ({ | DecryptionSharePrecomputed | DecryptionShareSimple )[] = []; - zip(validators, validator_keypairs).forEach(([validator, keypair]) => { + zip(validators, validatorKeypairs).forEach(([validator, keypair]) => { const dkg = new Dkg(tau, sharesNum, threshold, validators, validator); const aggregate = dkg.aggregateTranscript(receivedMessages); const isValid = aggregate.verify(sharesNum, receivedMessages); @@ -430,7 +430,7 @@ export const fakeDkgParticipants = (): DkgParticipant[] => { const ritual = fakeDkgTDecFlowE2e(FerveoVariant.Precomputed); return zip( zip(ritual.validators, ritual.transcripts), - ritual.validator_keypairs + ritual.validatorKeypairs ).map(([[v, t], k]) => { return { node: v.address.toString(), From c613ca347a49fbae0c29486a4d0173db0de5bc68 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 6 Jun 2023 13:22:03 +0200 Subject: [PATCH 47/98] fix naming --- src/sdk/strategy/cbd-strategy.ts | 8 ++++---- src/sdk/strategy/pre-strategy.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index ed15d9b15..f2150081a 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -7,12 +7,12 @@ import { DkgClient, DkgRitual, DkgRitualJSON } from '../../dkg'; import { fromJSON, objectEquals, toJSON } from '../../utils'; import { Cohort, CohortJSON } from '../cohort'; -type StrategyJSON = { +export type CbdStrategyJSON = { cohort: CohortJSON; conditionSet?: ConditionSetJSON | undefined; }; -type DeployedStrategyJSON = { +export type DeployedStrategyJSON = { dkgRitual: DkgRitualJSON; cohortConfig: CohortJSON; conditionSet?: ConditionSetJSON | undefined; @@ -66,14 +66,14 @@ export class CbdStrategy { return toJSON(this.toObj()); } - public static fromObj({ cohort, conditionSet }: StrategyJSON) { + public static fromObj({ cohort, conditionSet }: CbdStrategyJSON) { const maybeConditionSet = conditionSet ? ConditionSet.fromObj(conditionSet) : undefined; return new CbdStrategy(Cohort.fromObj(cohort), maybeConditionSet); } - public toObj(): StrategyJSON { + public toObj(): CbdStrategyJSON { return { cohort: this.cohort.toObj(), conditionSet: this.conditionSet?.toObj(), diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index 171375544..3954263cf 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -15,7 +15,7 @@ import { EnactedPolicy, EnactedPolicyJSON } from '../../policies/policy'; import { base64ToU8Receiver, bytesEquals, toJSON } from '../../utils'; import { Cohort, CohortJSON } from '../cohort'; -type PreStrategyJSON = { +export type PreStrategyJSON = { cohort: CohortJSON; aliceSecretKeyBytes: Uint8Array; bobSecretKeyBytes: Uint8Array; @@ -24,7 +24,7 @@ type PreStrategyJSON = { endDate: Date; }; -type DeployedStrategyJSON = { +export type DeployedStrategyJSON = { policy: EnactedPolicyJSON; cohortConfig: CohortJSON; bobSecretKeyBytes: Uint8Array; From 1c596a0b7eb97f275c803e088022c13d5685d67f Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 7 Jun 2023 12:55:08 +0200 Subject: [PATCH 48/98] fix tests --- src/conditions/condition-set.ts | 23 +++++++++++++++++------ src/conditions/operator.ts | 4 ++-- src/sdk/strategy/cbd-strategy.ts | 15 ++++++++++----- src/sdk/strategy/pre-strategy.ts | 22 +++++++++++++++------- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index b80f51049..29417b068 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -1,7 +1,8 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; +import deepEqual from 'deep-equal'; import { ethers } from 'ethers'; -import { objectEquals, toJSON } from '../utils'; +import { toJSON } from '../utils'; import { Condition } from './base'; import { ConditionContext } from './context'; @@ -41,9 +42,7 @@ export class ConditionSet { } public toObj(): ConditionSetJSON { - const conditions = this.conditions.map((cnd) => { - return cnd.toObj(); - }); + const conditions = this.conditions.map((cnd) => cnd.toObj()); return { conditions }; } @@ -84,7 +83,19 @@ export class ConditionSet { return new ConditionContext(this.toWASMConditions(), provider); } - public equals(other: ConditionContext): boolean { - return objectEquals(this.toObj(), other.toObj()); + public equals(other: ConditionSet): boolean { + // TODO: This is a hack to make the equals method work for Condition + // TODO: Implement proper casting from Conditon to _class type + const thisConditions = this.conditions.map((cnd) => { + const asObj = cnd.toObj(); + delete asObj._class; + return asObj; + }); + const otherConditions = other.conditions.map((cnd) => { + const asObj = cnd.toObj(); + delete asObj._class; + return asObj; + }); + return deepEqual(thisConditions, otherConditions); } } diff --git a/src/conditions/operator.ts b/src/conditions/operator.ts index 0c96e36b3..51a6cdb2e 100644 --- a/src/conditions/operator.ts +++ b/src/conditions/operator.ts @@ -12,11 +12,11 @@ export class Operator { this.operator = operator; } - toObj() { + public toObj() { return { operator: this.operator }; } - static fromObj(obj: Record) { + public static fromObj(obj: Record) { return new Operator(obj.operator); } diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index f2150081a..007716957 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -81,10 +81,11 @@ export class CbdStrategy { } public equals(other: CbdStrategy) { - return ( - this.cohort.equals(other.cohort) && - objectEquals(this.conditionSet?.toObj(), other.conditionSet?.toObj()) - ); + const conditionSetEquals = + this.conditionSet && other.conditionSet + ? this.conditionSet.equals(other.conditionSet) + : false; + return this.cohort.equals(other.cohort) && conditionSetEquals; } } @@ -140,9 +141,13 @@ export class DeployedCbdStrategy { } public equals(other: DeployedCbdStrategy) { + const conditionSetEquals = + this.conditionSet && other.conditionSet + ? this.conditionSet.equals(other.conditionSet) + : false; return ( this.cohort.equals(other.cohort) && - objectEquals(this.conditionSet?.toObj(), other.conditionSet?.toObj()) && + conditionSetEquals && objectEquals(this.dkgRitual.toObj(), other.dkgRitual.toObj()) ); } diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index 3954263cf..2c0dce71c 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -24,7 +24,7 @@ export type PreStrategyJSON = { endDate: Date; }; -export type DeployedStrategyJSON = { +export type DeployedPreStrategyJSON = { policy: EnactedPolicyJSON; cohortConfig: CohortJSON; bobSecretKeyBytes: Uint8Array; @@ -157,6 +157,10 @@ export class PreStrategy { } public equals(other: PreStrategy) { + const conditionSetEquals = + this.conditionSet && other.conditionSet + ? this.conditionSet.equals(other.conditionSet) + : false; return ( this.cohort.equals(other.cohort) && // TODO: Add equality to WASM bindings @@ -168,10 +172,9 @@ export class PreStrategy { this.bobSecretKey.toBEBytes(), other.bobSecretKey.toBEBytes() ) && + conditionSetEquals && this.startDate.toString() === other.startDate.toString() && - this.endDate.toString() === other.endDate.toString() && - this.conditionSet?.toWASMConditions === - other.conditionSet?.toWASMConditions + this.endDate.toString() === other.endDate.toString() ); } } @@ -201,7 +204,7 @@ export class DeployedPreStrategy { cohortConfig, bobSecretKeyBytes, conditionSet, - }: DeployedStrategyJSON) { + }: DeployedPreStrategyJSON) { const id = HRAC.fromBytes(policy.id); const policyKey = PublicKey.fromCompressedBytes(policy.policyKey); const encryptedTreasureMap = EncryptedTreasureMap.fromBytes( @@ -252,7 +255,7 @@ export class DeployedPreStrategy { ); } - public toObj(): DeployedStrategyJSON { + public toObj(): DeployedPreStrategyJSON { const policy = { ...this.policy, id: this.policy.id.toBytes(), @@ -268,6 +271,10 @@ export class DeployedPreStrategy { } public equals(other: DeployedPreStrategy) { + const conditionSetEquals = + this.conditionSet && other.conditionSet + ? this.conditionSet.equals(other.conditionSet) + : false; return ( this.label === other.label && this.cohort.equals(other.cohort) && @@ -277,7 +284,8 @@ export class DeployedPreStrategy { bytesEquals( this.policy.encryptedTreasureMap.toBytes(), other.policy.encryptedTreasureMap.toBytes() - ) + ) && + conditionSetEquals ); } } From e707abf44665deda4df60ee838d72f1e743a52aa Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 7 Jun 2023 14:33:04 +0200 Subject: [PATCH 49/98] feat: update nucypher-core to 0.9.0 --- package.json | 3 +- src/characters/cbd-recipient.ts | 16 +- src/characters/enrico.ts | 12 +- src/dkg.ts | 6 +- test/unit/cbd-strategy.test.ts | 2 +- test/utils.ts | 26 +- yarn.lock | 805 +++++++++++++++++--------------- 7 files changed, 450 insertions(+), 420 deletions(-) diff --git a/package.json b/package.json index d1853adae..b3a930bde 100644 --- a/package.json +++ b/package.json @@ -52,11 +52,10 @@ "prebuild": "yarn typechain" }, "dependencies": { - "@nucypher/nucypher-core": "^0.7.0", + "@nucypher/nucypher-core": "^0.9.0", "axios": "^0.21.1", "deep-equal": "^2.2.1", "ethers": "^5.4.1", - "ferveo-wasm": "^0.1.1", "joi": "^17.7.0", "qs": "^6.10.1" }, diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index a0419ec84..9918da54a 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -1,14 +1,15 @@ -import { Context, ThresholdDecryptionRequest } from '@nucypher/nucypher-core'; -import { ethers } from 'ethers'; import { + Ciphertext, combineDecryptionSharesPrecomputed, combineDecryptionSharesSimple, + Context, DecryptionSharePrecomputed, DecryptionShareSimple, decryptWithSharedSecret, -} from 'ferveo-wasm'; -import { SharedSecret } from 'ferveo-wasm'; -import { Ciphertext } from 'ferveo-wasm'; + SharedSecret, + ThresholdDecryptionRequest, +} from '@nucypher/nucypher-core'; +import { ethers } from 'ethers'; import { ConditionSet } from '../conditions'; import { DkgRitual, FerveoVariant } from '../dkg'; @@ -49,8 +50,7 @@ export class CbdTDecDecrypter { let sharedSecret: SharedSecret; if (variant === FerveoVariant.Simple) { sharedSecret = combineDecryptionSharesSimple( - decryptionShares as DecryptionShareSimple[], - dkgRitual.dkgPublicParams + decryptionShares as DecryptionShareSimple[] ); } else if (variant === FerveoVariant.Precomputed) { sharedSecret = combineDecryptionSharesPrecomputed( @@ -82,7 +82,7 @@ export class CbdTDecDecrypter { const tDecRequest = new ThresholdDecryptionRequest( ritualId, variant, - ciphertext.toBytes(), + ciphertext, conditionSet.toWASMConditions(), new Context(contextStr) ); diff --git a/src/characters/enrico.ts b/src/characters/enrico.ts index 488b6ae33..a089455d9 100644 --- a/src/characters/enrico.ts +++ b/src/characters/enrico.ts @@ -1,5 +1,11 @@ -import { MessageKit, PublicKey, SecretKey } from '@nucypher/nucypher-core'; -import { Ciphertext, DkgPublicKey, encrypt } from 'ferveo-wasm'; +import { + Ciphertext, + DkgPublicKey, + ferveoEncrypt, + MessageKit, + PublicKey, + SecretKey, +} from '@nucypher/nucypher-core'; import { ConditionSet } from '../conditions'; import { Keyring } from '../keyring'; @@ -56,7 +62,7 @@ export class Enrico { } const aad = toBytes(withConditions?.toJson() ?? ''); - const ciphertext = encrypt( + const ciphertext = ferveoEncrypt( plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), aad, this.encryptingKey diff --git a/src/dkg.ts b/src/dkg.ts index 596e6c31a..faaa6778f 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -1,14 +1,14 @@ -import { ethers } from 'ethers'; import { AggregatedTranscript, DkgPublicKey, DkgPublicParameters, EthereumAddress, - PublicKey as FerveoPublicKey, + FerveoPublicKey, Transcript, Validator, ValidatorMessage, -} from 'ferveo-wasm'; +} from '@nucypher/nucypher-core'; +import { ethers } from 'ethers'; import { DkgCoordinatorAgent } from './agents/coordinator'; import { bytesEquals, fromHexString } from './utils'; diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 9e4b097bb..c58fb47d3 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -115,7 +115,7 @@ describe('CbdDeployedStrategy', () => { ciphertext, }); const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); - const decryptSpy = mockDecrypt(decryptionShares); + const decryptSpy = mockDecrypt(mockedDkg.tau, decryptionShares); const decryptedMessage = await deployedStrategy.decrypter.retrieveAndDecrypt( diff --git a/test/utils.ts b/test/utils.ts index c82a0350e..3113bf791 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -6,6 +6,7 @@ import { Capsule, CapsuleFrag, EncryptedTreasureMap, + ferveoEncrypt, PublicKey, reencrypt, SecretKey, @@ -13,9 +14,6 @@ import { VerifiedCapsuleFrag, VerifiedKeyFrag, } from '@nucypher/nucypher-core'; -import axios from 'axios'; -import { ethers, providers, Wallet } from 'ethers'; -import { keccak256 } from 'ethers/lib/utils'; import { AggregatedTranscript, Ciphertext, @@ -25,13 +23,15 @@ import { DecryptionShareSimple, decryptWithSharedSecret, Dkg, - encrypt, EthereumAddress, Keypair, Transcript, Validator, ValidatorMessage, -} from 'ferveo-wasm'; +} from '@nucypher/nucypher-core'; +import axios from 'axios'; +import { ethers, providers, Wallet } from 'ethers'; +import { keccak256 } from 'ethers/lib/utils'; import { Alice, Bob, Cohort, Configuration, RemoteBob } from '../src'; import { CoordinatorRitual, DkgParticipant } from '../src/agents/coordinator'; @@ -363,10 +363,7 @@ export const fakeTDecFlow = ({ if (variant === FerveoVariant.Precomputed) { sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares); } else { - sharedSecret = combineDecryptionSharesSimple( - decryptionShares, - dkg.publicParams() - ); + sharedSecret = combineDecryptionSharesSimple(decryptionShares); } // The client should have access to the public parameters of the DKG @@ -391,7 +388,7 @@ export const fakeDkgTDecFlowE2e = ( const ritual = fakeDkgFlow(variant, ritualId, 4, 3); // In the meantime, the client creates a ciphertext and decryption request - const ciphertext = encrypt(message, aad, ritual.dkg.finalKey()); + const ciphertext = ferveoEncrypt(message, aad, ritual.dkg.publicKey()); const { decryptionShares } = fakeTDecFlow({ ...ritual, variant, @@ -421,8 +418,8 @@ export const fakeCoordinatorRitual = (ritualId: number): CoordinatorRitual => { aggregatedTranscriptHash: keccak256(ritual.serverAggregate.toBytes()), aggregationMismatch: false, // Assuming the ritual is correct aggregatedTranscript: toHexString(ritual.serverAggregate.toBytes()), - publicKey: toHexString(ritual.dkg.finalKey().toBytes()), - publicKeyHash: keccak256(ritual.dkg.finalKey().toBytes()), + publicKey: toHexString(ritual.dkg.publicKey().toBytes()), + publicKeyHash: keccak256(ritual.dkg.publicKey().toBytes()), }; }; @@ -442,10 +439,11 @@ export const fakeDkgParticipants = (): DkgParticipant[] => { }; export const mockDecrypt = ( + ritualId: number, decryptionShares: (DecryptionSharePrecomputed | DecryptionShareSimple)[] ) => { const result = decryptionShares.map( - (share) => new ThresholdDecryptionResponse(share.toBytes()) + (share) => new ThresholdDecryptionResponse(ritualId, share.toBytes()) ); return jest.spyOn(Porter.prototype, 'decrypt').mockImplementation(() => { return Promise.resolve(result); @@ -453,7 +451,7 @@ export const mockDecrypt = ( }; export const fakeDkgRitual = (ritual: { dkg: Dkg }) => { - return new DkgRitual(1, ritual.dkg.finalKey(), ritual.dkg.publicParams()); + return new DkgRitual(1, ritual.dkg.publicKey(), ritual.dkg.publicParams()); }; export const mockInitializeRitual = (fakeRitual: unknown) => { diff --git a/yarn.lock b/yarn.lock index 95d1fd5b1..6f4792ae7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17,45 +17,45 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5": - version "7.21.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" - integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.22.0", "@babel/compat-data@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.3.tgz#cd502a6a0b6e37d7ad72ce7e71a7160a3ae36f7e" + integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.18.10", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" - integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.1.tgz#5de51c5206f4c6f5533562838337a603c1033cfd" + integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.5" - "@babel/helper-compilation-targets" "^7.21.5" - "@babel/helper-module-transforms" "^7.21.5" - "@babel/helpers" "^7.21.5" - "@babel/parser" "^7.21.8" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/generator" "^7.22.0" + "@babel/helper-compilation-targets" "^7.22.1" + "@babel/helper-module-transforms" "^7.22.1" + "@babel/helpers" "^7.22.0" + "@babel/parser" "^7.22.0" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.21.5", "@babel/generator@^7.7.2": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f" - integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w== +"@babel/generator@^7.22.0", "@babel/generator@^7.22.3", "@babel/generator@^7.7.2": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" + integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== dependencies: - "@babel/types" "^7.21.5" + "@babel/types" "^7.22.3" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -68,51 +68,51 @@ "@babel/types" "^7.18.6" "@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz#817f73b6c59726ab39f6ba18c234268a519e5abb" - integrity sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g== + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz#c9b83d1ba74e163e023f008a3d3204588a7ceb60" + integrity sha512-ahEoxgqNoYXm0k22TvOke48i1PkavGu0qGCmcq9ugi6gnmvKNaMjKBSrZTnWUi1CFEeNAUiVba0Wtzm03aSkJg== dependencies: - "@babel/types" "^7.21.5" + "@babel/types" "^7.22.3" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366" - integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz#bfcd6b7321ffebe33290d68550e2c9d7eb7c7a58" + integrity sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ== dependencies: - "@babel/compat-data" "^7.21.5" + "@babel/compat-data" "^7.22.0" "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz#205b26330258625ef8869672ebca1e0dee5a0f02" - integrity sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw== +"@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz#ae3de70586cc757082ae3eba57240d42f468c41b" + integrity sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-environment-visitor" "^7.22.1" "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.21.5" + "@babel/helper-member-expression-to-functions" "^7.22.0" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.21.5" + "@babel/helper-replace-supers" "^7.22.1" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" semver "^6.3.0" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz#a7886f61c2e29e21fd4aaeaf1e473deba6b571dc" - integrity sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz#a7ed9a8488b45b467fca353cd1a44dc5f0cf5c70" + integrity sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.3.1" semver "^6.3.0" -"@babel/helper-define-polyfill-provider@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" - integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== +"@babel/helper-define-polyfill-provider@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz#487053f103110f25b9755c5980e031e93ced24d8" + integrity sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg== dependencies: "@babel/helper-compilation-targets" "^7.17.7" "@babel/helper-plugin-utils" "^7.16.7" @@ -121,10 +121,10 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" - integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" + integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": version "7.21.0" @@ -141,12 +141,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz#3b1a009af932e586af77c1030fba9ee0bde396c0" - integrity sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg== +"@babel/helper-member-expression-to-functions@^7.22.0": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz#4b77a12c1b4b8e9e28736ed47d8b91f00976911f" + integrity sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA== dependencies: - "@babel/types" "^7.21.5" + "@babel/types" "^7.22.3" "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": version "7.21.4" @@ -155,19 +155,19 @@ dependencies: "@babel/types" "^7.21.4" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420" - integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw== +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz#e0cad47fedcf3cae83c11021696376e2d5a50c63" + integrity sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw== dependencies: - "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-environment-visitor" "^7.22.1" "@babel/helper-module-imports" "^7.21.4" "@babel/helper-simple-access" "^7.21.5" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -191,17 +191,17 @@ "@babel/helper-wrap-function" "^7.18.9" "@babel/types" "^7.18.9" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz#a6ad005ba1c7d9bc2973dfde05a1bba7065dde3c" - integrity sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg== +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz#38cf6e56f7dc614af63a21b45565dd623f0fdc95" + integrity sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ== dependencies: - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-member-expression-to-functions" "^7.21.5" + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-member-expression-to-functions" "^7.22.0" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" "@babel/helper-simple-access@^7.21.5": version "7.21.5" @@ -249,14 +249,14 @@ "@babel/traverse" "^7.20.5" "@babel/types" "^7.20.5" -"@babel/helpers@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.5.tgz#5bac66e084d7a4d2d9696bdf0175a93f7fb63c08" - integrity sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA== +"@babel/helpers@^7.22.0": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.3.tgz#53b74351da9684ea2f694bf0877998da26dd830e" + integrity sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w== dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.3" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -267,10 +267,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" - integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.9", "@babel/parser@^7.22.0", "@babel/parser@^7.22.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" + integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -279,137 +279,26 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" - integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.7" - -"@babel/plugin-proposal-async-generator-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-class-static-block@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" - integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz#a75be1365c0c3188c51399a662168c1c98108659" + integrity sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.21.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" - integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" - integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" - integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - -"@babel/plugin-proposal-optional-catch-binding@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" - integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" - integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-optional-chaining" "^7.22.3" "@babel/plugin-proposal-private-property-in-object@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" - integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== + version "7.21.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c" + integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-create-class-features-plugin" "^7.21.0" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== @@ -466,6 +355,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" +"@babel/plugin-syntax-import-attributes@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.3.tgz#d7168f22b9b49a6cc1792cec78e06a18ad2e7b4b" + integrity sha512-i35jZJv6aO7hxEbIWQ41adVfOzjm9dcYDNeWlBMd8p0ZQRtNUCBrmGwZt+H5lb+oOC9a3svp956KP0oWGA1YsA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -543,6 +439,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-arrow-functions@^7.21.5": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" @@ -550,6 +454,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.21.5" +"@babel/plugin-transform-async-generator-functions@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.3.tgz#3ed99924c354fb9e80dabb2cc8d002c702e94527" + integrity sha512-36A4Aq48t66btydbZd5Fk0/xJqbpg/v4QWI4AH4cYHBXy9Mu42UOupZpebKFiCFNT9S9rJFcsld0gsv0ayLjtA== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-transform-async-to-generator@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" @@ -573,6 +487,23 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-class-properties@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.3.tgz#3407145e513830df77f0cef828b8b231c166fe4c" + integrity sha512-mASLsd6rhOrLZ5F3WbCxkzl67mmOnqik0zrg5W6D/X0QMW7HtvnoL1dRARLKIbMP3vXwkwziuLesPqWVGIl6Bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-class-static-block@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.3.tgz#e352cf33567385c731a8f21192efeba760358773" + integrity sha512-5BirgNWNOx7cwbTJCOmKFJ1pZjwk5MUfMIwiBBvsirCJMZeQgs5pk6i1OlkVg+1Vef5LfBahFOrdCnAWvkVKMw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-transform-classes@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" @@ -618,6 +549,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-dynamic-import@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.1.tgz#6c56afaf896a07026330cf39714532abed8d9ed1" + integrity sha512-rlhWtONnVBPdmt+jeewS0qSnMz/3yLFrqAP8hHC6EDcrYRSyuz9f9yQhHvVn2Ad6+yO9fHXac5piudeYrInxwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" @@ -626,6 +565,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-export-namespace-from@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.3.tgz#9b8700aa495007d3bebac8358d1c562434b680b9" + integrity sha512-5Ti1cHLTDnt3vX61P9KZ5IG09bFXp4cDVFJIAeCZuxu9OXXJJZp5iP0n/rzM2+iAutJY+KWEyyHcRaHlpQ/P5g== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-transform-for-of@^7.21.5": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" @@ -642,6 +589,14 @@ "@babel/helper-function-name" "^7.18.9" "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-json-strings@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.3.tgz#a181b8679cf7c93e9d0e3baa5b1776d65be601a9" + integrity sha512-IuvOMdeOOY2X4hRNAT6kwbePtK21BUyrAEgLKviL8pL6AEEVUVcqtRdN/HJXBLGIbt9T3ETmXRnFedRRmQNTYw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-transform-literals@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" @@ -649,6 +604,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-logical-assignment-operators@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.3.tgz#9e021455810f33b0baccb82fb759b194f5dc36f0" + integrity sha512-CbayIfOw4av2v/HYZEsH+Klks3NC2/MFIR3QR8gnpGNNPEaq2fdlVCRYG/paKs7/5hvBLQ+H70pGWOHtlNEWNA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-transform-member-expression-literals@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" @@ -673,14 +636,14 @@ "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-simple-access" "^7.21.5" -"@babel/plugin-transform-modules-systemjs@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" - integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== +"@babel/plugin-transform-modules-systemjs@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz#cc507e03e88d87b016feaeb5dae941e6ef50d91e" + integrity sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw== dependencies: "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-module-transforms" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-validator-identifier" "^7.19.1" "@babel/plugin-transform-modules-umd@^7.18.6": @@ -691,20 +654,47 @@ "@babel/helper-module-transforms" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz#db6fb77e6b3b53ec3b8d370246f0b7cf67d35ab4" + integrity sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" -"@babel/plugin-transform-new-target@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== +"@babel/plugin-transform-new-target@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz#deb0377d741cbee2f45305868b9026dcd6dd96e2" + integrity sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.3.tgz#8c519f8bf5af94a9ca6f65cf422a9d3396e542b9" + integrity sha512-CpaoNp16nX7ROtLONNuCyenYdY/l7ZsR6aoVa7rW7nMWisoNoQNIH5Iay/4LDyRjKMuElMqXiBoOQCDLTMGZiw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.3.tgz#02493070ca6685884b0eee705363ee4da2132ab0" + integrity sha512-+AF88fPDJrnseMh5vD9+SH6wq4ZMvpiTMHh58uLs+giMEyASFVhcT3NkoyO+NebFCNnpHJEq5AXO2txV4AGPDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.3.tgz#da6fba693effb8c203d8c3bdf7bf4e2567e802e9" + integrity sha512-38bzTsqMMCI46/TQnJwPPpy33EjLCc1Gsm2hRTF6zTMWnKsN61vdrpuzIEGQyKEhDSYDKyZHrrd5FMj4gcUHhw== + dependencies: + "@babel/compat-data" "^7.22.3" + "@babel/helper-compilation-targets" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.22.3" "@babel/plugin-transform-object-super@^7.18.6": version "7.18.6" @@ -714,12 +704,47 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" - integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== +"@babel/plugin-transform-optional-catch-binding@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.3.tgz#e971a083fc7d209d9cd18253853af1db6d8dc42f" + integrity sha512-bnDFWXFzWY0BsOyqaoSXvMQ2F35zutQipugog/rqotL2S4ciFOKlRYUu9djt4iq09oh2/34hqfRR2k1dIvuu4g== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz#5fd24a4a7843b76da6aeec23c7f551da5d365290" + integrity sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz#24477acfd2fd2bc901df906c9bf17fbcfeee900d" + integrity sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-private-methods@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.3.tgz#adac38020bab5047482d3297107c1f58e9c574f6" + integrity sha512-fC7jtjBPFqhqpPAE+O4LKwnLq7gGkD3ZmC2E3i4qWH34mH3gOg2Xrq5YMHUq6DM30xhqM1DNftiRaSqVjEG+ug== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-private-property-in-object@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.3.tgz#031621b02c7b7d95389de1a3dba2fe9e8c548e56" + integrity sha512-C7MMl4qWLpgVCbXfj3UW8rR1xeCnisQ0cU7YJHV//8oNBS0aCIVg1vFnZXxOckHhEpQyqNNkWmvSEWnMLlc+Vw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-transform-property-literals@^7.18.6": version "7.18.6" @@ -786,6 +811,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.21.5" +"@babel/plugin-transform-unicode-property-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.3.tgz#597b6a614dc93eaae605ee293e674d79d32eb380" + integrity sha512-5ScJ+OmdX+O6HRuMGW4kv7RL9vIKdtdAj9wuWUKy1wbHY3jaM/UlyIiC1G7J6UJiiyMukjjK0QwL3P0vBd0yYg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" @@ -794,38 +827,33 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-unicode-sets-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.3.tgz#7c14ee33fa69782b0101d0f7143d3fc73ce00700" + integrity sha512-hNufLdkF8vqywRp+P55j4FHXqAX2LRUccoZHH7AFn1pq5ZOO2ISKW9w13bFZVjBoTqeve2HOgoJCcaziJVhGNw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/preset-env@^7.15.6": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb" - integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg== + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.4.tgz#c86a82630f0e8c61d9bb9327b7b896732028cbed" + integrity sha512-c3lHOjbwBv0TkhYCr+XCR6wKcSZ1QbQTVdSkZUaVpLv8CVWotBMArWUi5UAJrcrQaEnleVkkvaV8F/pmc/STZQ== dependencies: - "@babel/compat-data" "^7.21.5" - "@babel/helper-compilation-targets" "^7.21.5" + "@babel/compat-data" "^7.22.3" + "@babel/helper-compilation-targets" "^7.22.1" "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" - "@babel/plugin-proposal-async-generator-functions" "^7.20.7" - "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.21.0" - "@babel/plugin-proposal-dynamic-import" "^7.18.6" - "@babel/plugin-proposal-export-namespace-from" "^7.18.9" - "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" - "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.20.7" - "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.21.0" - "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.3" "@babel/plugin-proposal-private-property-in-object" "^7.21.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-attributes" "^7.22.3" "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -836,28 +864,43 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.21.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.3" "@babel/plugin-transform-async-to-generator" "^7.20.7" "@babel/plugin-transform-block-scoped-functions" "^7.18.6" "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-class-properties" "^7.22.3" + "@babel/plugin-transform-class-static-block" "^7.22.3" "@babel/plugin-transform-classes" "^7.21.0" "@babel/plugin-transform-computed-properties" "^7.21.5" "@babel/plugin-transform-destructuring" "^7.21.3" "@babel/plugin-transform-dotall-regex" "^7.18.6" "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-dynamic-import" "^7.22.1" "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-export-namespace-from" "^7.22.3" "@babel/plugin-transform-for-of" "^7.21.5" "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-json-strings" "^7.22.3" "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.3" "@babel/plugin-transform-member-expression-literals" "^7.18.6" "@babel/plugin-transform-modules-amd" "^7.20.11" "@babel/plugin-transform-modules-commonjs" "^7.21.5" - "@babel/plugin-transform-modules-systemjs" "^7.20.11" + "@babel/plugin-transform-modules-systemjs" "^7.22.3" "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" - "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.3" + "@babel/plugin-transform-new-target" "^7.22.3" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.3" + "@babel/plugin-transform-numeric-separator" "^7.22.3" + "@babel/plugin-transform-object-rest-spread" "^7.22.3" "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.21.3" + "@babel/plugin-transform-optional-catch-binding" "^7.22.3" + "@babel/plugin-transform-optional-chaining" "^7.22.3" + "@babel/plugin-transform-parameters" "^7.22.3" + "@babel/plugin-transform-private-methods" "^7.22.3" + "@babel/plugin-transform-private-property-in-object" "^7.22.3" "@babel/plugin-transform-property-literals" "^7.18.6" "@babel/plugin-transform-regenerator" "^7.21.5" "@babel/plugin-transform-reserved-words" "^7.18.6" @@ -867,13 +910,15 @@ "@babel/plugin-transform-template-literals" "^7.18.9" "@babel/plugin-transform-typeof-symbol" "^7.18.9" "@babel/plugin-transform-unicode-escapes" "^7.21.5" + "@babel/plugin-transform-unicode-property-regex" "^7.22.3" "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.3" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.21.5" - babel-plugin-polyfill-corejs2 "^0.3.3" - babel-plugin-polyfill-corejs3 "^0.6.0" - babel-plugin-polyfill-regenerator "^0.4.1" - core-js-compat "^3.25.1" + "@babel/types" "^7.22.4" + babel-plugin-polyfill-corejs2 "^0.4.3" + babel-plugin-polyfill-corejs3 "^0.8.1" + babel-plugin-polyfill-regenerator "^0.5.0" + core-js-compat "^3.30.2" semver "^6.3.0" "@babel/preset-modules@^0.1.5": @@ -893,41 +938,41 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.8.4": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" - integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" + integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.21.9", "@babel/template@^7.3.3": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" + integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/code-frame" "^7.21.4" + "@babel/parser" "^7.21.9" + "@babel/types" "^7.21.5" -"@babel/traverse@^7.20.5", "@babel/traverse@^7.21.5", "@babel/traverse@^7.7.2": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" - integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== +"@babel/traverse@^7.20.5", "@babel/traverse@^7.22.1", "@babel/traverse@^7.7.2": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.4.tgz#c3cf96c5c290bd13b55e29d025274057727664c0" + integrity sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ== dependencies: "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.5" - "@babel/helper-environment-visitor" "^7.21.5" + "@babel/generator" "^7.22.3" + "@babel/helper-environment-visitor" "^7.22.1" "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/parser" "^7.22.4" + "@babel/types" "^7.22.4" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" - integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" + integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== dependencies: "@babel/helper-string-parser" "^7.21.5" "@babel/helper-validator-identifier" "^7.19.1" @@ -1631,14 +1676,6 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@morgan-stanley/ts-mocking-bird@^0.6.2": - version "0.6.4" - resolved "https://registry.yarnpkg.com/@morgan-stanley/ts-mocking-bird/-/ts-mocking-bird-0.6.4.tgz#2e4b60d42957bab3b50b67dbf14c3da2f62a39f7" - integrity sha512-57VJIflP8eR2xXa9cD1LUawh+Gh+BVQfVu0n6GALyg/AqV/Nz25kDRvws3i9kIe1PTrbsZZOYpsYp6bXPd6nVA== - dependencies: - lodash "^4.17.16" - uuid "^7.0.3" - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1660,10 +1697,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nucypher/nucypher-core@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.7.0.tgz#2549364f39087a32be3abd8510baa45c1089d205" - integrity sha512-L8jDHIkEPgK4rPOwf7UkePGI/AHnP77TyE7iktO1G/bMGPZzsZ4YVg0Tw13T/3mPLEVjFOFZqA+h75wK3rHU6g== +"@nucypher/nucypher-core@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.9.0.tgz#7bb8a41cce4978121256ca3e03fc9dc37ce7de91" + integrity sha512-X0aI14W1wSWm08agKrShpvvDMbvkqc7QJTi0KhdwxkMZxwDLxzunzPbLmet8KzinvmpsoCIssWmYr11V0XmfdQ== "@sideway/address@^4.1.3": version "4.1.4" @@ -1735,9 +1772,9 @@ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@typechain/ethers-v5@^9.0.0": version "9.0.0" @@ -1748,9 +1785,9 @@ ts-essentials "^7.0.1" "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" - integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" + integrity sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw== dependencies: "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" @@ -1774,11 +1811,11 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.5.tgz#c107216842905afafd3b6e774f6f935da6f5db80" - integrity sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q== + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.1.tgz#dd6f1d2411ae677dcb2db008c962598be31d6acf" + integrity sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg== dependencies: - "@babel/types" "^7.3.0" + "@babel/types" "^7.20.7" "@types/deep-equal@^1.0.1": version "1.0.1" @@ -1820,9 +1857,9 @@ pretty-format "^26.0.0" "@types/json-schema@^7.0.7": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== "@types/json5@^0.0.29": version "0.0.29" @@ -1835,9 +1872,9 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "18.16.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" - integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== + version "20.2.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb" + integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -1845,9 +1882,9 @@ integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== "@types/prettier@^2.1.1", "@types/prettier@^2.1.5": - version "2.7.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" - integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== "@types/qs@^6.9.7": version "6.9.7" @@ -2237,29 +2274,29 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" - integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== +babel-plugin-polyfill-corejs2@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz#75044d90ba5043a5fb559ac98496f62f3eb668fd" + integrity sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw== dependencies: "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.3" + "@babel/helper-define-polyfill-provider" "^0.4.0" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" - integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== +babel-plugin-polyfill-corejs3@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz#39248263c38191f0d226f928d666e6db1b4b3a8a" + integrity sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - core-js-compat "^3.25.1" + "@babel/helper-define-polyfill-provider" "^0.4.0" + core-js-compat "^3.30.1" -babel-plugin-polyfill-regenerator@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" - integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== +babel-plugin-polyfill-regenerator@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz#e7344d88d9ef18a3c47ded99362ae4a757609380" + integrity sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" + "@babel/helper-define-polyfill-provider" "^0.4.0" babel-preset-current-node-syntax@^1.0.0: version "1.0.1" @@ -2354,14 +2391,14 @@ browser-process-hrtime@^1.0.0: integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + version "4.21.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.7.tgz#e2b420947e5fb0a58e8f4668ae6e23488127e551" + integrity sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA== dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" + caniuse-lite "^1.0.30001489" + electron-to-chromium "^1.4.411" + node-releases "^2.0.12" + update-browserslist-db "^1.0.11" bs-logger@0.x: version "0.2.6" @@ -2427,10 +2464,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001449: - version "1.0.30001482" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001482.tgz#8b3fad73dc35b2674a5c96df2d4f9f1c561435de" - integrity sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ== +caniuse-lite@^1.0.30001489: + version "1.0.30001495" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz#64a0ccef1911a9dcff647115b4430f8eff1ef2d9" + integrity sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg== chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" @@ -2787,10 +2824,10 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== -core-js-compat@^3.25.1: - version "3.30.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.1.tgz#961541e22db9c27fc48bfc13a3cafa8734171dfe" - integrity sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw== +core-js-compat@^3.30.1, core-js-compat@^3.30.2: + version "3.30.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" + integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== dependencies: browserslist "^4.21.5" @@ -2805,9 +2842,9 @@ cosmiconfig-typescript-loader@^4.0.0: integrity sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q== cosmiconfig@^8.0.0: - version "8.1.3" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.3.tgz#0e614a118fcc2d9e5afc2f87d53cd09931015689" - integrity sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw== + version "8.2.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" + integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== dependencies: import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -3058,10 +3095,10 @@ dotgitignore@^2.1.0: find-up "^3.0.0" minimatch "^3.0.4" -electron-to-chromium@^1.4.284: - version "1.4.382" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.382.tgz#87e659b0f0d5f7b19759038871bac0a327191f82" - integrity sha512-czMavlW52VIPgutbVL9JnZIZuFijzsG1ww/1z2Otu1r1q+9Qe2bTsH3My3sZarlvwyqHM6+mnZfEnt2Vr4dsIg== +electron-to-chromium@^1.4.411: + version "1.4.423" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.423.tgz#99567f3a0563fe0d1d0931e9ce851bca239f6658" + integrity sha512-y4A7YfQcDGPAeSWM1IuoWzXpg9RY1nwHzHSwRtCSQFp9FgAVDgdWlFf0RbdWfLWQ2WUI+bddUgk5RgTjqRE6FQ== elliptic@6.5.4: version "6.5.4" @@ -3512,11 +3549,6 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -ferveo-wasm@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ferveo-wasm/-/ferveo-wasm-0.1.1.tgz#40419ed3eeab795abb929d5fb5806e300e363847" - integrity sha512-xaYOIrX6VT1rdjbMvykLH9exU9mmlxa8z26LHnnD2HYMoH14/fpC3R7QBFWsOXi2xlHbQd2x28iD7/p9bdBo4A== - figures@^3.0.0, figures@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -3739,12 +3771,13 @@ get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== dependencies: function-bind "^1.1.1" has "^1.0.3" + has-proto "^1.0.1" has-symbols "^1.0.3" get-package-type@^0.1.0: @@ -4220,9 +4253,9 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-core-module@^2.11.0, is-core-module@^2.5.0: - version "2.12.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" - integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== dependencies: has "^1.0.3" @@ -5161,7 +5194,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16, lodash@^4.17.21, lodash@^4.7.0: +lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5404,10 +5437,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== +node-releases@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" + integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" @@ -5457,9 +5490,9 @@ npm-run-path@^4.0.1: path-key "^3.0.0" nwsapi@^2.2.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" - integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== + version "2.2.5" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.5.tgz#a52744c61b3889dd44b0a158687add39b8d935e2" + integrity sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ== object-assign@^4.0.1: version "4.1.1" @@ -5838,9 +5871,9 @@ q@^1.5.1: integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== qs@^6.10.1: - version "6.11.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" - integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== dependencies: side-channel "^1.0.4" @@ -6139,9 +6172,9 @@ scrypt-js@3.0.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.x, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + version "7.5.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" + integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== dependencies: lru-cache "^6.0.0" @@ -6605,9 +6638,9 @@ token-types@^2.0.0: ieee754 "^1.2.1" tough-cookie@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" @@ -6634,11 +6667,10 @@ trim-repeated@^1.0.0: escape-string-regexp "^1.0.2" ts-command-line-args@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.0.tgz#7eeed3a6937b2612ea08a0794cf9d43fbbea89c4" - integrity sha512-Ff7Xt04WWCjj/cmPO9eWTJX3qpBZWuPWyQYG1vnxJao+alWWYjwJBc5aYz3h5p5dE08A6AnpkgiCtP/0KXXBYw== + version "2.5.1" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0" + integrity sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw== dependencies: - "@morgan-stanley/ts-mocking-bird" "^0.6.2" chalk "^4.1.0" command-line-args "^5.1.1" command-line-usage "^6.1.0" @@ -6706,9 +6738,9 @@ tslib@^1.8.1: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.1.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== tsutils@^3.21.0: version "3.21.0" @@ -6820,9 +6852,9 @@ typedoc@^0.22.11: shiki "^0.10.1" "typescript@^4.6.4 || ^5.0.0": - version "5.0.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" - integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + version "5.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" + integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== typescript@^4.7.0: version "4.9.5" @@ -6892,7 +6924,7 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -update-browserslist-db@^1.0.10: +update-browserslist-db@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== @@ -6925,11 +6957,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 938a2a3c8d2bcb32900846248d4849c7a5092a83 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 8 Jun 2023 14:35:58 -0400 Subject: [PATCH 50/98] Deprecate "timelock" condition in favor of TimeCondition with method name "blocktime". --- CHANGELOG.md | 2 ++ src/conditions/base/evm.ts | 1 + src/conditions/base/index.ts | 2 +- src/conditions/base/{timelock.ts => time.ts} | 11 +++++++--- .../base/{timelock.test.ts => time.test.ts} | 21 +++++++++++-------- 5 files changed, 24 insertions(+), 13 deletions(-) rename src/conditions/base/{timelock.ts => time.ts} (73%) rename test/unit/conditions/base/{timelock.test.ts => time.test.ts} (59%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 821f1b312..da3cef5ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. See [standa ## [1.0.0-beta.1](https://github.com/nucypher/nucypher-ts/compare/v1.0.0-beta.0...v1.0.0-beta.1) (2023-03-27) +### ⚠ BREAKING CHANGES +* `TimeLockCondition` no longer supported; instead `TimeCondition` with method name `blocktime` can be used ### ⚠ BREAKING CHANGES diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index 79e7dfac3..8d992b48c 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -10,6 +10,7 @@ export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; export class EvmCondition extends Condition { public readonly schema = Joi.object({ contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), + // TODO why is chain a string here? chain: Joi.string() .valid(...SUPPORTED_CHAINS) .required(), diff --git a/src/conditions/base/index.ts b/src/conditions/base/index.ts index 2857b9d63..dc2fcf522 100644 --- a/src/conditions/base/index.ts +++ b/src/conditions/base/index.ts @@ -1,4 +1,4 @@ export { Condition } from './condition'; export { EvmCondition } from './evm'; export { RpcCondition } from './rpc'; -export { TimelockCondition } from './timelock'; +export { TimeCondition } from './time'; diff --git a/src/conditions/base/timelock.ts b/src/conditions/base/time.ts similarity index 73% rename from src/conditions/base/timelock.ts rename to src/conditions/base/time.ts index e347524fb..78fa28795 100644 --- a/src/conditions/base/timelock.ts +++ b/src/conditions/base/time.ts @@ -1,19 +1,24 @@ import Joi from 'joi'; +import { SUPPORTED_CHAINS } from '../const'; + import { Condition } from './condition'; import { returnValueTestSchema } from './schema'; -export class TimelockCondition extends Condition { +export class TimeCondition extends Condition { // TODO: This is the only condition that uses defaults, and also the only condition that uses `method` in order - // to determine the schema. I.e. the only method that used `METHOD = 'timelock'` in `nucypher/nucypher`. + // to determine the schema. I.e. the only method that used `METHOD = 'blocktime'` in `nucypher/nucypher`. // TODO: Consider introducing a different field for this, e.g. `conditionType` or `type`. Use this field in a // condition factory. public readonly defaults: Record = { - method: 'timelock', + method: 'blocktime', }; public readonly schema = Joi.object({ method: Joi.string().valid(this.defaults.method).required(), returnValueTest: returnValueTestSchema.required(), + chain: Joi.number() + .valid(...SUPPORTED_CHAINS) + .required(), }); } diff --git a/test/unit/conditions/base/timelock.test.ts b/test/unit/conditions/base/time.test.ts similarity index 59% rename from test/unit/conditions/base/timelock.test.ts rename to test/unit/conditions/base/time.test.ts index b0ecdc3ca..05b459dfc 100644 --- a/test/unit/conditions/base/timelock.test.ts +++ b/test/unit/conditions/base/time.test.ts @@ -1,4 +1,4 @@ -import { TimelockCondition } from '../../../../src/conditions/base'; +import { TimeCondition } from '../../../../src/conditions/base'; describe('validation', () => { const returnValueTest = { @@ -8,31 +8,34 @@ describe('validation', () => { }; it('accepts a valid schema', () => { - const timelock = new TimelockCondition({ + const timeCondition = new TimeCondition({ returnValueTest, + chain: 5, }); - expect(timelock.toObj()).toEqual({ + expect(timeCondition.toObj()).toEqual({ returnValueTest, - method: 'timelock', - _class: 'TimelockCondition', + chain: 5, + method: 'blocktime', + _class: 'TimeCondition', }); }); it('rejects an invalid schema', () => { - const badTimelockObj = { + const badTimeObj = { // Intentionally replacing `returnValueTest` with an invalid test returnValueTest: { ...returnValueTest, comparator: 'not-a-comparator', }, + chain: 5, }; - const badTimelock = new TimelockCondition(badTimelockObj); - expect(() => badTimelock.toObj()).toThrow( + const badTimeCondition = new TimeCondition(badTimeObj); + expect(() => badTimeCondition.toObj()).toThrow( 'Invalid condition: "returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' ); - const { error } = badTimelock.validate(badTimelockObj); + const { error } = badTimeCondition.validate(badTimeObj); expect(error?.message).toEqual( '"returnValueTest.comparator" must be one of [==, >, <, >=, <=, !=]' ); From 454a2672ca3331013609b85034c67915d8375d58 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 9 Jun 2023 08:48:54 -0400 Subject: [PATCH 51/98] Chain value for EvmCondition should be a number instead of a string. --- src/conditions/base/evm.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index 8d992b48c..604817948 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -10,8 +10,7 @@ export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; export class EvmCondition extends Condition { public readonly schema = Joi.object({ contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), - // TODO why is chain a string here? - chain: Joi.string() + chain: Joi.number() .valid(...SUPPORTED_CHAINS) .required(), standardContractType: Joi.string() From f78a55fb362c5c2eccc07f34d35ab89cc34118f7 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 9 Jun 2023 12:24:55 +0200 Subject: [PATCH 52/98] feat: use e2e-encrypted decryption requests --- src/characters/cbd-recipient.ts | 109 ++++++++++++++++++++++++++----- src/characters/porter.ts | 77 ++++++++++++++++------ src/sdk/strategy/cbd-strategy.ts | 12 +++- test/unit/cbd-strategy.test.ts | 8 ++- test/utils.ts | 43 +++++++++--- 5 files changed, 198 insertions(+), 51 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 9918da54a..8ccaaf919 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -6,6 +6,8 @@ import { DecryptionSharePrecomputed, DecryptionShareSimple, decryptWithSharedSecret, + SessionSecretFactory, + SessionSharedSecret, SharedSecret, ThresholdDecryptionRequest, } from '@nucypher/nucypher-core'; @@ -13,12 +15,15 @@ import { ethers } from 'ethers'; import { ConditionSet } from '../conditions'; import { DkgRitual, FerveoVariant } from '../dkg'; -import { fromJSON, toJSON } from '../utils'; +import { ChecksumAddress } from '../types'; +import { fromJSON, toBytes, toJSON } from '../utils'; -import { Porter } from './porter'; +import { CbdDecryptResult, Porter } from './porter'; -type CbdTDecDecrypterJSON = { +export type CbdTDecDecrypterJSON = { porterUri: string; + ursulas: Array; + threshold: number; }; export class CbdTDecDecrypter { @@ -26,7 +31,11 @@ export class CbdTDecDecrypter { // private readonly verifyingKey: Keyring; - constructor(porterUri: string) { + constructor( + porterUri: string, + private readonly ursulas: Array, + private readonly threshold: number + ) { this.porter = new Porter(porterUri); } @@ -79,34 +88,94 @@ export class CbdTDecDecrypter { const contextStr = await conditionSet.buildContext(provider).toJson(); // TODO: Move ThresholdDecryptionRequest creation and parsing to Porter? - const tDecRequest = new ThresholdDecryptionRequest( - ritualId, - variant, - ciphertext, - conditionSet.toWASMConditions(), - new Context(contextStr) + const { sessionSharedSecret, encryptedRequest } = + this.makeDecryptionRequest( + ritualId, + variant, + ciphertext, + conditionSet, + contextStr + ); + + const cbdDecryptResult = await this.porter.cbdDecrypt( + encryptedRequest, + this.ursulas, + this.threshold ); - // TODO: This should return multiple responses - const resp = await this.porter.decrypt(tDecRequest); + return this.makeDecryptionShares( + cbdDecryptResult, + sessionSharedSecret, + variant + ); + } + private makeDecryptionShares( + cbdDecryptResult: CbdDecryptResult, + sessionSharedSecret: SessionSharedSecret, + variant: number + ) { + const decryptedResponses = Object.entries( + cbdDecryptResult.encryptedResponses + ).map(([, encryptedResponse]) => + encryptedResponse.decrypt(sessionSharedSecret) + ); + const variants = decryptedResponses.map((resp) => resp.ritualId); + if (variants.some((v) => v !== variant)) { + throw new Error('Decryption shares are not of the same variant'); + } + + const decryptionShares = decryptedResponses.map( + (resp) => resp.decryptionShare + ); // TODO: Replace with a factory method if (variant === FerveoVariant.Simple) { - return resp.map((r) => - DecryptionShareSimple.fromBytes(r.decryptionShare) + return decryptionShares.map((share) => + DecryptionShareSimple.fromBytes(share) ); } else if (variant === FerveoVariant.Precomputed) { - return resp.map((r) => - DecryptionSharePrecomputed.fromBytes(r.decryptionShare) + return decryptionShares.map((share) => + DecryptionSharePrecomputed.fromBytes(share) ); } else { throw new Error(`Unknown variant ${variant}`); } } + private makeDecryptionRequest( + ritualId: number, + variant: number, + ciphertext: Ciphertext, + conditionSet: ConditionSet, + contextStr: string + ) { + const decryptionRequest = new ThresholdDecryptionRequest( + ritualId, + variant, + ciphertext, + conditionSet.toWASMConditions(), + new Context(contextStr) + ); + + const secretFactory = SessionSecretFactory.random(); + const label = toBytes(`${ritualId}`); + const ursulaPublicKey = secretFactory.makeKey(label).publicKey(); + const requesterSecretKey = secretFactory.makeKey(label); + const sessionSharedSecret = + requesterSecretKey.deriveSharedSecret(ursulaPublicKey); + + const encryptedRequest = decryptionRequest.encrypt( + sessionSharedSecret, + requesterSecretKey.publicKey() + ); + return { sessionSharedSecret, encryptedRequest }; + } + public toObj(): CbdTDecDecrypterJSON { return { porterUri: this.porter.porterUrl.toString(), + ursulas: this.ursulas, + threshold: this.threshold, }; } @@ -114,8 +183,12 @@ export class CbdTDecDecrypter { return toJSON(this.toObj()); } - public static fromObj({ porterUri }: CbdTDecDecrypterJSON) { - return new CbdTDecDecrypter(porterUri); + public static fromObj({ + porterUri, + ursulas, + threshold, + }: CbdTDecDecrypterJSON) { + return new CbdTDecDecrypter(porterUri, ursulas, threshold); } public static fromJSON(json: string) { diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 47f403a8e..366e49eaa 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -1,9 +1,9 @@ import { CapsuleFrag, + EncryptedThresholdDecryptionRequest, + EncryptedThresholdDecryptionResponse, PublicKey, RetrievalKit, - ThresholdDecryptionRequest, - ThresholdDecryptionResponse, TreasureMap, } from '@nucypher/nucypher-core'; import axios, { AxiosResponse } from 'axios'; @@ -13,6 +13,7 @@ import { ConditionContext } from '../conditions'; import { Base64EncodedBytes, ChecksumAddress, HexEncodedBytes } from '../types'; import { fromBase64, fromHexString, toBase64, toHexString } from '../utils'; +// /get_ursulas export type Ursula = { readonly checksumAddress: ChecksumAddress; readonly uri: string; @@ -31,13 +32,14 @@ type UrsulaResponse = { readonly encrypting_key: HexEncodedBytes; }; -export type GetUrsulasResponse = { +export type GetUrsulasResult = { readonly result: { readonly ursulas: readonly UrsulaResponse[]; }; readonly version: string; }; +// /retrieve_cfrags type PostRetrieveCFragsRequest = { readonly treasure_map: Base64EncodedBytes; readonly retrieval_kits: readonly Base64EncodedBytes[]; @@ -47,7 +49,7 @@ type PostRetrieveCFragsRequest = { readonly context?: string; }; -type PostRetrieveCFragsResult = { +type PostRetrieveCFragsResponse = { readonly result: { readonly retrieval_results: readonly { readonly cfrags: { @@ -61,14 +63,30 @@ type PostRetrieveCFragsResult = { readonly version: string; }; -export type RetrieveCFragsResponse = { - cFrags: Record; - errors: Record; +export type RetrieveCFragsResult = { + readonly cFrags: Record; + readonly errors: Record; }; -type PostDecryptRequest = Uint8Array; +// /cbd_decrypt + +type PostCbdDecryptRequest = { + readonly threshold: number; + readonly encrypted_decryption_requests: Record< + ChecksumAddress, + Base64EncodedBytes + >; +}; + +type PostCbdDecryptResponse = { + encrypted_decryption_responses: Record; + errors: Record; +}; -type PostDecryptResult = Uint8Array; +export type CbdDecryptResult = { + encryptedResponses: Record; + errors: Record; +}; export class Porter { readonly porterUrl: URL; @@ -87,7 +105,7 @@ export class Porter { exclude_ursulas: excludeUrsulas, include_ursulas: includeUrsulas, }; - const resp: AxiosResponse = await axios.get( + const resp: AxiosResponse = await axios.get( new URL('/get_ursulas', this.porterUrl).toString(), { params, @@ -112,7 +130,7 @@ export class Porter { bobEncryptingKey: PublicKey, bobVerifyingKey: PublicKey, conditionsContext?: ConditionContext - ): Promise { + ): Promise { const context = conditionsContext ? await conditionsContext.toJson() : undefined; @@ -124,7 +142,7 @@ export class Porter { bob_verifying_key: toHexString(bobVerifyingKey.toCompressedBytes()), context, }; - const resp: AxiosResponse = await axios.post( + const resp: AxiosResponse = await axios.post( new URL('/retrieve_cfrags', this.porterUrl).toString(), data ); @@ -139,15 +157,34 @@ export class Porter { }); } - public async decrypt( - tDecRequest: ThresholdDecryptionRequest - ): Promise { - const data: PostDecryptRequest = tDecRequest.toBytes(); - const resp: AxiosResponse = await axios.post( - new URL('/decrypt', this.porterUrl).toString(), + public async cbdDecrypt( + encryptedRequest: EncryptedThresholdDecryptionRequest, + ursulas: Array, + threshold: number + ): Promise { + const encodedEncryptedRequest = toBase64(encryptedRequest.toBytes()); + const data: PostCbdDecryptRequest = { + encrypted_decryption_requests: Object.fromEntries( + ursulas.map((ursula) => [ursula, encodedEncryptedRequest]) + ), + threshold, + }; + const resp: AxiosResponse = await axios.post( + new URL('/cbd_decrypt', this.porterUrl).toString(), data ); - // TODO: In /cbd_decrypt, the response is a list of ThresholdDecryptionResponse - return [ThresholdDecryptionResponse.fromBytes(resp.data)]; + const decryptionResponses = Object.entries( + resp.data.encrypted_decryption_responses + ).map(([address, encryptedResponseBase64]) => { + const encryptedResponse = EncryptedThresholdDecryptionResponse.fromBytes( + fromBase64(encryptedResponseBase64) + ); + return [address, encryptedResponse]; + }); + const encryptedResponses: Record< + string, + EncryptedThresholdDecryptionResponse + > = Object.fromEntries(decryptionResponses); + return { encryptedResponses, errors: resp.data.errors }; } } diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 007716957..5f2729fb7 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -47,7 +47,11 @@ export class CbdStrategy { this.conditionSet ); - const decrypter = new CbdTDecDecrypter(this.cohort.configuration.porterUri); + const decrypter = new CbdTDecDecrypter( + this.cohort.configuration.porterUri, + this.cohort.ursulaAddresses, + this.cohort.configuration.threshold + ); return new DeployedCbdStrategy( this.cohort, @@ -122,7 +126,11 @@ export class DeployedCbdStrategy { undefined, maybeConditionSet ); - const decrypter = new CbdTDecDecrypter(cohort.configuration.porterUri); + const decrypter = new CbdTDecDecrypter( + cohort.configuration.porterUri, + cohort.ursulaAddresses, + cohort.configuration.threshold + ); return new DeployedCbdStrategy( cohort, ritual, diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index c58fb47d3..9cf5621e0 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -15,7 +15,7 @@ import { fakeUrsulas, fakeWeb3Provider, makeCohort, - mockDecrypt, + mockCbdDecrypt, mockGetUrsulas, mockInitializeRitual, } from '../utils'; @@ -115,7 +115,11 @@ describe('CbdDeployedStrategy', () => { ciphertext, }); const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); - const decryptSpy = mockDecrypt(mockedDkg.tau, decryptionShares); + const decryptSpy = mockCbdDecrypt( + mockedDkg.tau, + decryptionShares, + mockedUrsulas.map((u) => u.checksumAddress) + ); const decryptedMessage = await deployedStrategy.decrypter.retrieveAndDecrypt( diff --git a/test/utils.ts b/test/utils.ts index 3113bf791..b62544e55 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -5,11 +5,13 @@ import { Block } from '@ethersproject/providers'; import { Capsule, CapsuleFrag, + EncryptedThresholdDecryptionResponse, EncryptedTreasureMap, ferveoEncrypt, PublicKey, reencrypt, SecretKey, + SessionSecretFactory, ThresholdDecryptionResponse, VerifiedCapsuleFrag, VerifiedKeyFrag, @@ -36,9 +38,10 @@ import { keccak256 } from 'ethers/lib/utils'; import { Alice, Bob, Cohort, Configuration, RemoteBob } from '../src'; import { CoordinatorRitual, DkgParticipant } from '../src/agents/coordinator'; import { - GetUrsulasResponse, + CbdDecryptResult, + GetUrsulasResult, Porter, - RetrieveCFragsResponse, + RetrieveCFragsResult, Ursula, } from '../src/characters/porter'; import { DkgClient, DkgRitual, FerveoVariant } from '../src/dkg'; @@ -139,7 +142,7 @@ export const fakeUrsulas = (): readonly Ursula[] => { export const mockGetUrsulas = (ursulas: readonly Ursula[]) => { const fakePorterUrsulas = ( mockUrsulas: readonly Ursula[] - ): GetUrsulasResponse => { + ): GetUrsulasResult => { return { result: { ursulas: mockUrsulas.map(({ encryptingKey, uri, checksumAddress }) => ({ @@ -167,7 +170,7 @@ const fakeCFragResponse = ( ursulas: readonly ChecksumAddress[], verifiedKFrags: readonly VerifiedKeyFrag[], capsule: Capsule -): readonly RetrieveCFragsResponse[] => { +): readonly RetrieveCFragsResult[] => { const reencrypted = verifiedKFrags .map((kFrag) => reencrypt(capsule, kFrag)) .map((cFrag) => CapsuleFrag.fromBytes(cFrag.toBytes())); @@ -438,14 +441,36 @@ export const fakeDkgParticipants = (): DkgParticipant[] => { }); }; -export const mockDecrypt = ( +export const mockCbdDecrypt = ( ritualId: number, - decryptionShares: (DecryptionSharePrecomputed | DecryptionShareSimple)[] + decryptionShares: (DecryptionSharePrecomputed | DecryptionShareSimple)[], + ursulas: ChecksumAddress[], + errors: Record = {} ) => { - const result = decryptionShares.map( - (share) => new ThresholdDecryptionResponse(ritualId, share.toBytes()) + const encryptedResponses: Record< + string, + EncryptedThresholdDecryptionResponse + > = Object.fromEntries( + zip(decryptionShares, ursulas).map(([share, ursula]) => { + const secretFactory = SessionSecretFactory.random(); + const label = toBytes(`${ritualId}`); + const ursulaPublicKey = secretFactory.makeKey(label).publicKey(); + const requesterSecretKey = secretFactory.makeKey(label); + const sessionSharedSecret = + requesterSecretKey.deriveSharedSecret(ursulaPublicKey); + + const resp = new ThresholdDecryptionResponse(ritualId, share.toBytes()); + const encryptedResp = resp.encrypt(sessionSharedSecret); + + return [ursula, encryptedResp]; + }) ); - return jest.spyOn(Porter.prototype, 'decrypt').mockImplementation(() => { + + const result: CbdDecryptResult = { + encryptedResponses, + errors, + }; + return jest.spyOn(Porter.prototype, 'cbdDecrypt').mockImplementation(() => { return Promise.resolve(result); }); }; From 83716979c6232fffd5a25374e09d1e7d6d487703 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 9 Jun 2023 13:49:51 +0200 Subject: [PATCH 53/98] update coordinator contract --- abi/Coordinator.json | 193 ++++++++++++++++++---------- src/agents/coordinator.ts | 6 +- test/integration/dkg-client.test.ts | 2 +- test/utils.ts | 24 +++- 4 files changed, 148 insertions(+), 77 deletions(-) diff --git a/abi/Coordinator.json b/abi/Coordinator.json index eb2c7f72c..604b03c6d 100644 --- a/abi/Coordinator.json +++ b/abi/Coordinator.json @@ -2,6 +2,11 @@ "abi": [ { "inputs": [ + { + "internalType": "contract IAccessControlApplication", + "name": "app", + "type": "address" + }, { "internalType": "uint32", "name": "_timeout", @@ -11,11 +16,6 @@ "internalType": "uint32", "name": "_maxDkgSize", "type": "uint32" - }, - { - "internalType": "contract ApplicationInterface", - "name": "_application", - "type": "address" } ], "stateMutability": "nonpayable", @@ -63,9 +63,9 @@ }, { "indexed": false, - "internalType": "enum Coordinator.RitualState", - "name": "status", - "type": "uint8" + "internalType": "bool", + "name": "successful", + "type": "bool" } ], "name": "EndRitual", @@ -140,26 +140,13 @@ { "indexed": false, "internalType": "address[]", - "name": "nodes", + "name": "participants", "type": "address[]" } ], "name": "StartRitual", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - } - ], - "name": "StartTranscriptRound", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -206,49 +193,77 @@ }, { "inputs": [], - "name": "PUBLIC_KEY_SIZE", + "name": "application", "outputs": [ { - "internalType": "uint256", + "internalType": "contract IAccessControlApplication", "name": "", - "type": "uint256" + "type": "address" } ], "stateMutability": "view", "type": "function" }, { - "inputs": [], - "name": "applicationInterface", + "inputs": [ + { + "internalType": "address[]", + "name": "nodes", + "type": "address[]" + } + ], + "name": "cohortFingerprint", "outputs": [ { - "internalType": "contract ApplicationInterface", + "internalType": "bytes32", "name": "", - "type": "address" + "type": "bytes32" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { "inputs": [ { - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" + "internalType": "uint256", + "name": "ritualID", + "type": "uint256" }, { "internalType": "address", - "name": "node", + "name": "provider", "type": "address" } ], - "name": "getNodeIndex", + "name": "getParticipantFromProvider", "outputs": [ { - "internalType": "uint256", + "components": [ + { + "internalType": "address", + "name": "provider", + "type": "address" + }, + { + "internalType": "bool", + "name": "aggregated", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "transcript", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "decryptionRequestStaticKey", + "type": "bytes" + } + ], + "internalType": "struct Coordinator.Participant", "name": "", - "type": "uint256" + "type": "tuple" } ], "stateMutability": "view", @@ -268,7 +283,7 @@ "components": [ { "internalType": "address", - "name": "node", + "name": "provider", "type": "address" }, { @@ -280,6 +295,11 @@ "internalType": "bytes", "name": "transcript", "type": "bytes" + }, + { + "internalType": "bytes", + "name": "decryptionRequestStaticKey", + "type": "bytes" } ], "internalType": "struct Coordinator.Participant[]", @@ -313,7 +333,7 @@ "inputs": [ { "internalType": "address[]", - "name": "nodes", + "name": "providers", "type": "address[]" } ], @@ -374,19 +394,31 @@ "name": "ritualId", "type": "uint32" }, - { - "internalType": "uint256", - "name": "nodeIndex", - "type": "uint256" - }, { "internalType": "bytes", "name": "aggregatedTranscript", "type": "bytes" }, { - "internalType": "bytes", + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes16", + "name": "word1", + "type": "bytes16" + } + ], + "internalType": "struct BLS12381.G1Point", "name": "publicKey", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "decryptionRequestStaticKey", "type": "bytes" } ], @@ -402,11 +434,6 @@ "name": "ritualId", "type": "uint32" }, - { - "internalType": "uint256", - "name": "nodeIndex", - "type": "uint256" - }, { "internalType": "bytes", "name": "transcript", @@ -435,11 +462,6 @@ ], "name": "rituals", "outputs": [ - { - "internalType": "uint32", - "name": "id", - "type": "uint32" - }, { "internalType": "address", "name": "initiator", @@ -466,9 +488,21 @@ "type": "uint32" }, { - "internalType": "bytes32", - "name": "aggregatedTranscriptHash", - "type": "bytes32" + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes16", + "name": "word1", + "type": "bytes16" + } + ], + "internalType": "struct BLS12381.G1Point", + "name": "publicKey", + "type": "tuple" }, { "internalType": "bool", @@ -479,16 +513,6 @@ "internalType": "bytes", "name": "aggregatedTranscript", "type": "bytes" - }, - { - "internalType": "bytes", - "name": "publicKey", - "type": "bytes" - }, - { - "internalType": "bytes32", - "name": "publicKeyHash", - "type": "bytes32" } ], "stateMutability": "view", @@ -546,5 +570,36 @@ "stateMutability": "nonpayable", "type": "function" } - ] + ], + "contractName": "Coordinator", + "deploymentBytecode": { + "bytecode": "0x60a06040523480156200001157600080fd5b506040516200208b3803806200208b8339810160408190526200003491620000e7565b6200003f336200007d565b6001600160a01b039092166080526002805463ffffffff938416640100000000026001600160401b031990911693909216929092171790556200013e565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805163ffffffff81168114620000e257600080fd5b919050565b600080600060608486031215620000fd57600080fd5b83516001600160a01b03811681146200011557600080fd5b92506200012560208501620000cd565b91506200013560408501620000cd565b90509250925092565b608051611f0e6200017d600039600081816101620152818161049101528181610b0001528181610ba601528181610ed90152610f7f0152611f0e6000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c8063715018a6116100a2578063948c73ea11610071578063948c73ea1461025f5780639c48937b1461027f578063dc80d104146102a0578063f1e0ff19146102b3578063f2fde38b146102bb57600080fd5b8063715018a6146102205780638b5eeb3f146102285780638cf950611461023b5780638da5cb5b1461024e57600080fd5b80633d796abc116100de5780633d796abc146101b45780635e748733146101db5780636b75f53e146101fb57806370dea79a1461021057600080fd5b80631057de0114610110578063214b02ad1461013d57806326e4ca821461015d5780632f2eaebc1461019c575b600080fd5b61012361011e3660046116d7565b6102ce565b60405163ffffffff90911681526020015b60405180910390f35b61015061014b366004611765565b6105aa565b604051610134919061181b565b6101847f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610134565b60025461012390640100000000900463ffffffff1681565b6101c76101c236600461187d565b610785565b604051610134989796959493929190611896565b6101ee6101e936600461192a565b6108ac565b604051610134919061195a565b61020e6102093660046119b6565b610a53565b005b6002546101239063ffffffff1681565b61020e610d9c565b61020e610236366004611765565b610db0565b61020e610249366004611a09565b610e2c565b6000546001600160a01b0316610184565b61027261026d36600461187d565b611315565b6040516101349190611abf565b61029261028d3660046116d7565b611340565b604051908152602001610134565b61020e6102ae366004611765565b611373565b600154610292565b61020e6102c9366004611ae7565b6113db565b600081600281108015906102f25750600254640100000000900463ffffffff168111155b6103435760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206e756d626572206f66206e6f64657300000000000000000060448201526064015b60405180910390fd5b6001805480820182556000918252600781027fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180544263ffffffff908116600160c01b0263ffffffff60c01b19918716600160a01b0263ffffffff60a01b1933166001600160c01b03199094169390931792909217161781559091805b8481101561055257600683018054600181018255600091825260208220600390910201908989848181106103f7576103f7611b04565b905060200201602081019061040c9190611ae7565b9050806001600160a01b0316846001600160a01b03161061046f5760405162461bcd60e51b815260206004820152601860248201527f50726f766964657273206d75737420626520736f727465640000000000000000604482015260640161033a565b60405163c4903d5b60e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063c4903d5b90602401602060405180830381865afa1580156104da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fe9190611b1a565b6001600160601b0316116105245760405162461bcd60e51b815260040161033a90611b43565b81546001600160a01b0319166001600160a01b0382161790915591508061054a81611b90565b9150506103c1565b50336001600160a01b03168363ffffffff167fa4e3b0c0b125669bbe2cfcbf57b6b13b68dc908d343dde9bdf0df75081ae403f8989604051610595929190611ba9565b60405180910390a35090925050505b92915050565b6060600060018363ffffffff16815481106105c7576105c7611b04565b9060005260206000209060070201905080600601805480602002602001604051908101604052809291908181526020016000905b82821015610779576000848152602090819020604080516080810182526003860290920180546001600160a01b0381168452600160a01b900460ff161515938301939093526001830180549293929184019161065690611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461068290611bf7565b80156106cf5780601f106106a4576101008083540402835291602001916106cf565b820191906000526020600020905b8154815290600101906020018083116106b257829003601f168201915b505050505081526020016002820180546106e890611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461071490611bf7565b80156107615780601f1061073657610100808354040283529160200191610761565b820191906000526020600020905b81548152906001019060200180831161074457829003601f168201915b505050505081525050815260200190600101906105fb565b50505050915050919050565b6001818154811061079557600080fd5b600091825260209182902060079190910201805460018201546040805180820190915260028401548152600384015460801b6001600160801b0319169481019490945260048301546005840180546001600160a01b0385169750600160a01b850463ffffffff90811697600160c01b8704821697600160e01b90970482169691909516949360ff1692909161082990611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461085590611bf7565b80156108a25780601f10610877576101008083540402835291602001916108a2565b820191906000526020600020905b81548152906001019060200180831161088557829003601f168201915b5050505050905088565b60408051608081018252600080825260208201526060918101829052818101919091526108f9600184815481106108e5576108e5611b04565b906000526020600020906007020183611454565b6040805160808101825282546001600160a01b0381168252600160a01b900460ff161515602082015260018301805491939284019161093790611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461096390611bf7565b80156109b05780601f10610985576101008083540402835291602001916109b0565b820191906000526020600020905b81548152906001019060200180831161099357829003601f168201915b505050505081526020016002820180546109c990611bf7565b80601f01602080910402602001604051908101604052809291908181526020018280546109f590611bf7565b8015610a425780601f10610a1757610100808354040283529160200191610a42565b820191906000526020600020905b815481529060010190602001808311610a2557829003601f168201915b505050505081525050905092915050565b600060018463ffffffff1681548110610a6e57610a6e611b04565b6000918252602090912060079091020190506001610a8b8261150a565b6005811115610a9c57610a9c611aa9565b14610ae95760405162461bcd60e51b815260206004820152601b60248201527f4e6f742077616974696e6720666f72207472616e736372697074730000000000604482015260640161033a565b60405162dca53b60e81b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dca53b0090602401602060405180830381865afa158015610b4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b739190611c31565b90506000610b818383611454565b60405163c4903d5b60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063c4903d5b90602401602060405180830381865afa158015610bed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c119190611b1a565b6001600160601b031611610c375760405162461bcd60e51b815260040161033a90611b43565b806001018054610c4690611bf7565b159050610c955760405162461bcd60e51b815260206004820152601e60248201527f4e6f646520616c726561647920706f73746564207472616e7363726970740000604482015260640161033a565b60008585604051610ca7929190611c4e565b604051908190039020905060018201610cc1868883611cbf565b50826001600160a01b03168763ffffffff167f66568b934e848078c9787d6a66dae153eaae57f0ec3a553c11939fcdcf9c11fb83604051610d0491815260200190565b60405180910390a38354600160e01b900463ffffffff1684601c610d2783611d80565b82546101009290920a63ffffffff8181021990931691831602179091558554600160a01b81048216600160e01b909104909116039050610d935760405163ffffffff8816907fca79a3f8fdffa27f8c0a30733144db5a5bd2663f1f2561d8d665bf4da6a3dfbe90600090a25b50505050505050565b610da46115f7565b610dae6000611651565b565b610db86115f7565b6002546040805163ffffffff6401000000009093048316815291831660208301527fbb0cedd628c5ad0619627014b51dff9ab8676ce038340c26d817b8229740d0c9910160405180910390a16002805463ffffffff9092166401000000000267ffffffff0000000019909216919091179055565b600060018763ffffffff1681548110610e4757610e47611b04565b6000918252602090912060079091020190506002610e648261150a565b6005811115610e7557610e75611aa9565b14610ec25760405162461bcd60e51b815260206004820152601c60248201527f4e6f742077616974696e6720666f72206167677265676174696f6e7300000000604482015260640161033a565b60405162dca53b60e81b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dca53b0090602401602060405180830381865afa158015610f28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4c9190611c31565b90506000610f5a8383611454565b60405163c4903d5b60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063c4903d5b90602401602060405180830381865afa158015610fc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fea9190611b1a565b6001600160601b0316116110105760405162461bcd60e51b815260040161033a90611b43565b8054600160a01b900460ff16156110695760405162461bcd60e51b815260206004820152601f60248201527f4e6f646520616c726561647920706f73746564206167677265676174696f6e00604482015260640161033a565b80600201805461107890611bf7565b1590506110dc5760405162461bcd60e51b815260206004820152602c60248201527f4e6f646520616c72656164792070726f7669646564207265717565737420656e60448201526b6372797074696e67206b657960a01b606482015260840161033a565b600088886040516110ee929190611c4e565b604051908190039020825460ff60a01b1916600160a01b178355905060028201611119868883611cbf565b50826001600160a01b03168a63ffffffff167f1884446739eb06b60e314b4c3b25f08b5c7377f20538c132ce7c064f38272bac8360405161115c91815260200190565b60405180910390a383600501805461117390611bf7565b90506000036111a3576005840161118b898b83611cbf565b50866002850161119b8282611db9565b90505061126e565b6040805180820190915260028501548152600385015460801b6001600160801b03191660208201526111e3906111de368a90038a018a611de8565b6116a1565b1580611207575080846005016040516111fc9190611e3e565b604051809103902014155b1561126e5760048401805460ff191660011790558354604051600081526001600160a01b039091169063ffffffff8c16907f9dc7c9243191ecf8e0264232a3cb660035e1563d41b1812f0fea00955a291a589060200160405180910390a35050505061130d565b60018401805463ffffffff1690600061128683611d80565b82546101009290920a63ffffffff81810219909316918316021790915585546001870154600160a01b90910482169116039050611308578354604051600181526001600160a01b039091169063ffffffff8c16907f9dc7c9243191ecf8e0264232a3cb660035e1563d41b1812f0fea00955a291a589060200160405180910390a35b505050505b505050505050565b60006105a46001838154811061132d5761132d611b04565b906000526020600020906007020161150a565b60008282604051602001611355929190611ba9565b60405160208183030381529060405280519060200120905092915050565b61137b6115f7565b6002546040805163ffffffff928316815291831660208301527feb65c6287031cadd2d71b59499e985dddd00f14b3a8b2ce8d951da00f29995f6910160405180910390a16002805463ffffffff191663ffffffff92909216919091179055565b6113e36115f7565b6001600160a01b0381166114485760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161033a565b61145181611651565b50565b6006820154600090815b818110156114c157600085600601828154811061147d5761147d611b04565b6000918252602090912060039091020180549091506001600160a01b038087169116036114ae5792506105a4915050565b50806114b981611b90565b91505061145e565b5060405162461bcd60e51b815260206004820152601e60248201527f5061727469636970616e74206e6f742070617274206f662072697475616c0000604482015260640161033a565b805460025460009163ffffffff600160c01b909104811691839161152f911683611eb4565b90508163ffffffff16600003611549575060009392505050565b8354600185015463ffffffff600160a01b9092048216911603611570575060059392505050565b600484015460ff1615611587575060049392505050565b8063ffffffff1642111561159f575060039392505050565b835463ffffffff600160a01b82048116600160e01b9092041610156115c8575060019392505050565b8354600185015463ffffffff600160a01b9092048216911610156115f0575060029392505050565b5050919050565b6000546001600160a01b03163314610dae5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161033a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805182516000911480156116d0575081602001516001600160801b03191683602001516001600160801b031916145b9392505050565b600080602083850312156116ea57600080fd5b823567ffffffffffffffff8082111561170257600080fd5b818501915085601f83011261171657600080fd5b81358181111561172557600080fd5b8660208260051b850101111561173a57600080fd5b60209290920196919550909350505050565b803563ffffffff8116811461176057600080fd5b919050565b60006020828403121561177757600080fd5b6116d08261174c565b6000815180845260005b818110156117a65760208185018101518683018201520161178a565b506000602082860101526020601f19601f83011685010191505092915050565b60018060a01b03815116825260208101511515602083015260006040820151608060408501526117f96080850182611780565b9050606083015184820360608601526118128282611780565b95945050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561187057603f1988860301845261185e8583516117c6565b94509285019290850190600101611842565b5092979650505050505050565b60006020828403121561188f57600080fd5b5035919050565b6001600160a01b038916815263ffffffff888116602080840191909152888216604084015287821660608401529086166080830152845160a08301528401516001600160801b03191660c082015282151560e0820152610120610100820181905260009061190683820185611780565b9b9a5050505050505050505050565b6001600160a01b038116811461145157600080fd5b6000806040838503121561193d57600080fd5b82359150602083013561194f81611915565b809150509250929050565b6020815260006116d060208301846117c6565b60008083601f84011261197f57600080fd5b50813567ffffffffffffffff81111561199757600080fd5b6020830191508360208285010111156119af57600080fd5b9250929050565b6000806000604084860312156119cb57600080fd5b6119d48461174c565b9250602084013567ffffffffffffffff8111156119f057600080fd5b6119fc8682870161196d565b9497909650939450505050565b60008060008060008086880360a0811215611a2357600080fd5b611a2c8861174c565b9650602088013567ffffffffffffffff80821115611a4957600080fd5b611a558b838c0161196d565b90985096508691506040603f1984011215611a6f57600080fd5b60408a01955060808a0135925080831115611a8957600080fd5b5050611a9789828a0161196d565b979a9699509497509295939492505050565b634e487b7160e01b600052602160045260246000fd5b6020810160068310611ae157634e487b7160e01b600052602160045260246000fd5b91905290565b600060208284031215611af957600080fd5b81356116d081611915565b634e487b7160e01b600052603260045260246000fd5b600060208284031215611b2c57600080fd5b81516001600160601b03811681146116d057600080fd5b60208082526018908201527f4e6f7420656e6f75676820617574686f72697a6174696f6e0000000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600060018201611ba257611ba2611b7a565b5060010190565b60208082528181018390526000908460408401835b86811015611bec578235611bd181611915565b6001600160a01b031682529183019190830190600101611bbe565b509695505050505050565b600181811c90821680611c0b57607f821691505b602082108103611c2b57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215611c4357600080fd5b81516116d081611915565b8183823760009101908152919050565b634e487b7160e01b600052604160045260246000fd5b601f821115611cba57600081815260208120601f850160051c81016020861015611c9b5750805b601f850160051c820191505b8181101561130d57828155600101611ca7565b505050565b67ffffffffffffffff831115611cd757611cd7611c5e565b611ceb83611ce58354611bf7565b83611c74565b6000601f841160018114611d1f5760008515611d075750838201355b600019600387901b1c1916600186901b178355611d79565b600083815260209020601f19861690835b82811015611d505786850135825560209485019460019092019101611d30565b5086821015611d6d5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600063ffffffff808316818103611d9957611d99611b7a565b6001019392505050565b6001600160801b03198116811461145157600080fd5b81358155600181016020830135611dcf81611da3565b81546001600160801b03191660809190911c1790555050565b600060408284031215611dfa57600080fd5b6040516040810181811067ffffffffffffffff82111715611e1d57611e1d611c5e565b604052823581526020830135611e3281611da3565b60208201529392505050565b6000808354611e4c81611bf7565b60018281168015611e645760018114611e7957611ea8565b60ff1984168752821515830287019450611ea8565b8760005260208060002060005b85811015611e9f5781548a820152908401908201611e86565b50505082870194505b50929695505050505050565b63ffffffff818116838216019080821115611ed157611ed1611b7a565b509291505056fea26469706673582212200eae299f80274d63d710b26d0bfdf0da4b44dce652d7d9a6bb31ed8cbe3b95c364736f6c63430008140033" + }, + "devdoc": { + "kind": "dev", + "methods": { + "owner()": { + "details": "Returns the address of the current owner." + }, + "renounceOwnership()": { + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner." + }, + "transferOwnership(address)": { + "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner." + } + }, + "title": "Coordinator", + "version": 1 + }, + "runtimeBytecode": { + "bytecode": "0x60a06040523480156200001157600080fd5b506040516200208b3803806200208b8339810160408190526200003491620000e7565b6200003f336200007d565b6001600160a01b039092166080526002805463ffffffff938416640100000000026001600160401b031990911693909216929092171790556200013e565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805163ffffffff81168114620000e257600080fd5b919050565b600080600060608486031215620000fd57600080fd5b83516001600160a01b03811681146200011557600080fd5b92506200012560208501620000cd565b91506200013560408501620000cd565b90509250925092565b608051611f0e6200017d600039600081816101620152818161049101528181610b0001528181610ba601528181610ed90152610f7f0152611f0e6000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c8063715018a6116100a2578063948c73ea11610071578063948c73ea1461025f5780639c48937b1461027f578063dc80d104146102a0578063f1e0ff19146102b3578063f2fde38b146102bb57600080fd5b8063715018a6146102205780638b5eeb3f146102285780638cf950611461023b5780638da5cb5b1461024e57600080fd5b80633d796abc116100de5780633d796abc146101b45780635e748733146101db5780636b75f53e146101fb57806370dea79a1461021057600080fd5b80631057de0114610110578063214b02ad1461013d57806326e4ca821461015d5780632f2eaebc1461019c575b600080fd5b61012361011e3660046116d7565b6102ce565b60405163ffffffff90911681526020015b60405180910390f35b61015061014b366004611765565b6105aa565b604051610134919061181b565b6101847f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610134565b60025461012390640100000000900463ffffffff1681565b6101c76101c236600461187d565b610785565b604051610134989796959493929190611896565b6101ee6101e936600461192a565b6108ac565b604051610134919061195a565b61020e6102093660046119b6565b610a53565b005b6002546101239063ffffffff1681565b61020e610d9c565b61020e610236366004611765565b610db0565b61020e610249366004611a09565b610e2c565b6000546001600160a01b0316610184565b61027261026d36600461187d565b611315565b6040516101349190611abf565b61029261028d3660046116d7565b611340565b604051908152602001610134565b61020e6102ae366004611765565b611373565b600154610292565b61020e6102c9366004611ae7565b6113db565b600081600281108015906102f25750600254640100000000900463ffffffff168111155b6103435760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206e756d626572206f66206e6f64657300000000000000000060448201526064015b60405180910390fd5b6001805480820182556000918252600781027fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180544263ffffffff908116600160c01b0263ffffffff60c01b19918716600160a01b0263ffffffff60a01b1933166001600160c01b03199094169390931792909217161781559091805b8481101561055257600683018054600181018255600091825260208220600390910201908989848181106103f7576103f7611b04565b905060200201602081019061040c9190611ae7565b9050806001600160a01b0316846001600160a01b03161061046f5760405162461bcd60e51b815260206004820152601860248201527f50726f766964657273206d75737420626520736f727465640000000000000000604482015260640161033a565b60405163c4903d5b60e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063c4903d5b90602401602060405180830381865afa1580156104da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fe9190611b1a565b6001600160601b0316116105245760405162461bcd60e51b815260040161033a90611b43565b81546001600160a01b0319166001600160a01b0382161790915591508061054a81611b90565b9150506103c1565b50336001600160a01b03168363ffffffff167fa4e3b0c0b125669bbe2cfcbf57b6b13b68dc908d343dde9bdf0df75081ae403f8989604051610595929190611ba9565b60405180910390a35090925050505b92915050565b6060600060018363ffffffff16815481106105c7576105c7611b04565b9060005260206000209060070201905080600601805480602002602001604051908101604052809291908181526020016000905b82821015610779576000848152602090819020604080516080810182526003860290920180546001600160a01b0381168452600160a01b900460ff161515938301939093526001830180549293929184019161065690611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461068290611bf7565b80156106cf5780601f106106a4576101008083540402835291602001916106cf565b820191906000526020600020905b8154815290600101906020018083116106b257829003601f168201915b505050505081526020016002820180546106e890611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461071490611bf7565b80156107615780601f1061073657610100808354040283529160200191610761565b820191906000526020600020905b81548152906001019060200180831161074457829003601f168201915b505050505081525050815260200190600101906105fb565b50505050915050919050565b6001818154811061079557600080fd5b600091825260209182902060079190910201805460018201546040805180820190915260028401548152600384015460801b6001600160801b0319169481019490945260048301546005840180546001600160a01b0385169750600160a01b850463ffffffff90811697600160c01b8704821697600160e01b90970482169691909516949360ff1692909161082990611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461085590611bf7565b80156108a25780601f10610877576101008083540402835291602001916108a2565b820191906000526020600020905b81548152906001019060200180831161088557829003601f168201915b5050505050905088565b60408051608081018252600080825260208201526060918101829052818101919091526108f9600184815481106108e5576108e5611b04565b906000526020600020906007020183611454565b6040805160808101825282546001600160a01b0381168252600160a01b900460ff161515602082015260018301805491939284019161093790611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461096390611bf7565b80156109b05780601f10610985576101008083540402835291602001916109b0565b820191906000526020600020905b81548152906001019060200180831161099357829003601f168201915b505050505081526020016002820180546109c990611bf7565b80601f01602080910402602001604051908101604052809291908181526020018280546109f590611bf7565b8015610a425780601f10610a1757610100808354040283529160200191610a42565b820191906000526020600020905b815481529060010190602001808311610a2557829003601f168201915b505050505081525050905092915050565b600060018463ffffffff1681548110610a6e57610a6e611b04565b6000918252602090912060079091020190506001610a8b8261150a565b6005811115610a9c57610a9c611aa9565b14610ae95760405162461bcd60e51b815260206004820152601b60248201527f4e6f742077616974696e6720666f72207472616e736372697074730000000000604482015260640161033a565b60405162dca53b60e81b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dca53b0090602401602060405180830381865afa158015610b4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b739190611c31565b90506000610b818383611454565b60405163c4903d5b60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063c4903d5b90602401602060405180830381865afa158015610bed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c119190611b1a565b6001600160601b031611610c375760405162461bcd60e51b815260040161033a90611b43565b806001018054610c4690611bf7565b159050610c955760405162461bcd60e51b815260206004820152601e60248201527f4e6f646520616c726561647920706f73746564207472616e7363726970740000604482015260640161033a565b60008585604051610ca7929190611c4e565b604051908190039020905060018201610cc1868883611cbf565b50826001600160a01b03168763ffffffff167f66568b934e848078c9787d6a66dae153eaae57f0ec3a553c11939fcdcf9c11fb83604051610d0491815260200190565b60405180910390a38354600160e01b900463ffffffff1684601c610d2783611d80565b82546101009290920a63ffffffff8181021990931691831602179091558554600160a01b81048216600160e01b909104909116039050610d935760405163ffffffff8816907fca79a3f8fdffa27f8c0a30733144db5a5bd2663f1f2561d8d665bf4da6a3dfbe90600090a25b50505050505050565b610da46115f7565b610dae6000611651565b565b610db86115f7565b6002546040805163ffffffff6401000000009093048316815291831660208301527fbb0cedd628c5ad0619627014b51dff9ab8676ce038340c26d817b8229740d0c9910160405180910390a16002805463ffffffff9092166401000000000267ffffffff0000000019909216919091179055565b600060018763ffffffff1681548110610e4757610e47611b04565b6000918252602090912060079091020190506002610e648261150a565b6005811115610e7557610e75611aa9565b14610ec25760405162461bcd60e51b815260206004820152601c60248201527f4e6f742077616974696e6720666f72206167677265676174696f6e7300000000604482015260640161033a565b60405162dca53b60e81b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dca53b0090602401602060405180830381865afa158015610f28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4c9190611c31565b90506000610f5a8383611454565b60405163c4903d5b60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063c4903d5b90602401602060405180830381865afa158015610fc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fea9190611b1a565b6001600160601b0316116110105760405162461bcd60e51b815260040161033a90611b43565b8054600160a01b900460ff16156110695760405162461bcd60e51b815260206004820152601f60248201527f4e6f646520616c726561647920706f73746564206167677265676174696f6e00604482015260640161033a565b80600201805461107890611bf7565b1590506110dc5760405162461bcd60e51b815260206004820152602c60248201527f4e6f646520616c72656164792070726f7669646564207265717565737420656e60448201526b6372797074696e67206b657960a01b606482015260840161033a565b600088886040516110ee929190611c4e565b604051908190039020825460ff60a01b1916600160a01b178355905060028201611119868883611cbf565b50826001600160a01b03168a63ffffffff167f1884446739eb06b60e314b4c3b25f08b5c7377f20538c132ce7c064f38272bac8360405161115c91815260200190565b60405180910390a383600501805461117390611bf7565b90506000036111a3576005840161118b898b83611cbf565b50866002850161119b8282611db9565b90505061126e565b6040805180820190915260028501548152600385015460801b6001600160801b03191660208201526111e3906111de368a90038a018a611de8565b6116a1565b1580611207575080846005016040516111fc9190611e3e565b604051809103902014155b1561126e5760048401805460ff191660011790558354604051600081526001600160a01b039091169063ffffffff8c16907f9dc7c9243191ecf8e0264232a3cb660035e1563d41b1812f0fea00955a291a589060200160405180910390a35050505061130d565b60018401805463ffffffff1690600061128683611d80565b82546101009290920a63ffffffff81810219909316918316021790915585546001870154600160a01b90910482169116039050611308578354604051600181526001600160a01b039091169063ffffffff8c16907f9dc7c9243191ecf8e0264232a3cb660035e1563d41b1812f0fea00955a291a589060200160405180910390a35b505050505b505050505050565b60006105a46001838154811061132d5761132d611b04565b906000526020600020906007020161150a565b60008282604051602001611355929190611ba9565b60405160208183030381529060405280519060200120905092915050565b61137b6115f7565b6002546040805163ffffffff928316815291831660208301527feb65c6287031cadd2d71b59499e985dddd00f14b3a8b2ce8d951da00f29995f6910160405180910390a16002805463ffffffff191663ffffffff92909216919091179055565b6113e36115f7565b6001600160a01b0381166114485760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161033a565b61145181611651565b50565b6006820154600090815b818110156114c157600085600601828154811061147d5761147d611b04565b6000918252602090912060039091020180549091506001600160a01b038087169116036114ae5792506105a4915050565b50806114b981611b90565b91505061145e565b5060405162461bcd60e51b815260206004820152601e60248201527f5061727469636970616e74206e6f742070617274206f662072697475616c0000604482015260640161033a565b805460025460009163ffffffff600160c01b909104811691839161152f911683611eb4565b90508163ffffffff16600003611549575060009392505050565b8354600185015463ffffffff600160a01b9092048216911603611570575060059392505050565b600484015460ff1615611587575060049392505050565b8063ffffffff1642111561159f575060039392505050565b835463ffffffff600160a01b82048116600160e01b9092041610156115c8575060019392505050565b8354600185015463ffffffff600160a01b9092048216911610156115f0575060029392505050565b5050919050565b6000546001600160a01b03163314610dae5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161033a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805182516000911480156116d0575081602001516001600160801b03191683602001516001600160801b031916145b9392505050565b600080602083850312156116ea57600080fd5b823567ffffffffffffffff8082111561170257600080fd5b818501915085601f83011261171657600080fd5b81358181111561172557600080fd5b8660208260051b850101111561173a57600080fd5b60209290920196919550909350505050565b803563ffffffff8116811461176057600080fd5b919050565b60006020828403121561177757600080fd5b6116d08261174c565b6000815180845260005b818110156117a65760208185018101518683018201520161178a565b506000602082860101526020601f19601f83011685010191505092915050565b60018060a01b03815116825260208101511515602083015260006040820151608060408501526117f96080850182611780565b9050606083015184820360608601526118128282611780565b95945050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561187057603f1988860301845261185e8583516117c6565b94509285019290850190600101611842565b5092979650505050505050565b60006020828403121561188f57600080fd5b5035919050565b6001600160a01b038916815263ffffffff888116602080840191909152888216604084015287821660608401529086166080830152845160a08301528401516001600160801b03191660c082015282151560e0820152610120610100820181905260009061190683820185611780565b9b9a5050505050505050505050565b6001600160a01b038116811461145157600080fd5b6000806040838503121561193d57600080fd5b82359150602083013561194f81611915565b809150509250929050565b6020815260006116d060208301846117c6565b60008083601f84011261197f57600080fd5b50813567ffffffffffffffff81111561199757600080fd5b6020830191508360208285010111156119af57600080fd5b9250929050565b6000806000604084860312156119cb57600080fd5b6119d48461174c565b9250602084013567ffffffffffffffff8111156119f057600080fd5b6119fc8682870161196d565b9497909650939450505050565b60008060008060008086880360a0811215611a2357600080fd5b611a2c8861174c565b9650602088013567ffffffffffffffff80821115611a4957600080fd5b611a558b838c0161196d565b90985096508691506040603f1984011215611a6f57600080fd5b60408a01955060808a0135925080831115611a8957600080fd5b5050611a9789828a0161196d565b979a9699509497509295939492505050565b634e487b7160e01b600052602160045260246000fd5b6020810160068310611ae157634e487b7160e01b600052602160045260246000fd5b91905290565b600060208284031215611af957600080fd5b81356116d081611915565b634e487b7160e01b600052603260045260246000fd5b600060208284031215611b2c57600080fd5b81516001600160601b03811681146116d057600080fd5b60208082526018908201527f4e6f7420656e6f75676820617574686f72697a6174696f6e0000000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600060018201611ba257611ba2611b7a565b5060010190565b60208082528181018390526000908460408401835b86811015611bec578235611bd181611915565b6001600160a01b031682529183019190830190600101611bbe565b509695505050505050565b600181811c90821680611c0b57607f821691505b602082108103611c2b57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215611c4357600080fd5b81516116d081611915565b8183823760009101908152919050565b634e487b7160e01b600052604160045260246000fd5b601f821115611cba57600081815260208120601f850160051c81016020861015611c9b5750805b601f850160051c820191505b8181101561130d57828155600101611ca7565b505050565b67ffffffffffffffff831115611cd757611cd7611c5e565b611ceb83611ce58354611bf7565b83611c74565b6000601f841160018114611d1f5760008515611d075750838201355b600019600387901b1c1916600186901b178355611d79565b600083815260209020601f19861690835b82811015611d505786850135825560209485019460019092019101611d30565b5086821015611d6d5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600063ffffffff808316818103611d9957611d99611b7a565b6001019392505050565b6001600160801b03198116811461145157600080fd5b81358155600181016020830135611dcf81611da3565b81546001600160801b03191660809190911c1790555050565b600060408284031215611dfa57600080fd5b6040516040810181811067ffffffffffffffff82111715611e1d57611e1d611c5e565b604052823581526020830135611e3281611da3565b60208201529392505050565b6000808354611e4c81611bf7565b60018281168015611e645760018114611e7957611ea8565b60ff1984168752821515830287019450611ea8565b8760005260208060002060005b85811015611e9f5781548a820152908401908201611e86565b50505082870194505b50929695505050505050565b63ffffffff818116838216019080821115611ed157611ed1611b7a565b509291505056fea26469706673582212200eae299f80274d63d710b26d0bfdf0da4b44dce652d7d9a6bb31ed8cbe3b95c364736f6c63430008140033" + }, + "sourceId": "contracts/coordination/Coordinator.sol", + "sourcemap": "282:9240:36:-:0;;;1917:176;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;936:32:17;719:10:24;936:18:17;:32::i;:::-;-1:-1:-1;;;;;2007:17:36;;;;;2034:7;:18;;;2062:24;;;;;-1:-1:-1;;;;;;2062:24:36;;;2034:18;;;;2062:24;;;;;;;282:9240;;2433:187:17;2506:16;2525:6;;-1:-1:-1;;;;;2541:17:17;;;-1:-1:-1;;;;;;2541:17:17;;;;;;2573:40;;2525:6;;;;;;;2573:40;;2506:16;2573:40;2496:124;2433:187;:::o;14:167:63:-;92:13;;145:10;134:22;;124:33;;114:61;;171:1;168;161:12;114:61;14:167;;;:::o;186:491::-;307:6;315;323;376:2;364:9;355:7;351:23;347:32;344:52;;;392:1;389;382:12;344:52;418:16;;-1:-1:-1;;;;;463:31:63;;453:42;;443:70;;509:1;506;499:12;443:70;532:5;-1:-1:-1;556:48:63;600:2;585:18;;556:48;:::i;:::-;546:58;;623:48;667:2;656:9;652:18;623:48;:::i;:::-;613:58;;186:491;;;;;:::o;:::-;282:9240:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;", + "userdoc": { + "kind": "user", + "methods": {}, + "notice": "Coordination layer for DKG-TDec", + "version": 1 + } } diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts index f6fca8176..1c047cde5 100644 --- a/src/agents/coordinator.ts +++ b/src/agents/coordinator.ts @@ -4,21 +4,19 @@ import { Coordinator, Coordinator__factory, } from '../../types/ethers-contracts'; +import { BLS12381 } from '../../types/ethers-contracts/Coordinator'; import { getContract } from './contracts'; export interface CoordinatorRitual { - id: number; initiator: string; dkgSize: number; initTimestamp: number; totalTranscripts: number; totalAggregations: number; - aggregatedTranscriptHash: string; + publicKey: BLS12381.G1PointStructOutput; aggregationMismatch: boolean; aggregatedTranscript: string; - publicKey: string; - publicKeyHash: string; } export interface DkgParticipant { diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts index 824c30ab9..f14273979 100644 --- a/test/integration/dkg-client.test.ts +++ b/test/integration/dkg-client.test.ts @@ -25,7 +25,7 @@ describe('DkgCoordinatorAgent', () => { const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); const ritual = await DkgCoordinatorAgent.getRitual(provider, ritualId); - expect(ritual.id).toEqual(ritualId); + expect(ritual).toBeDefined(); }); it('fetches participants from the coordinator', async () => { diff --git a/test/utils.ts b/test/utils.ts index b62544e55..995835c4c 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -36,7 +36,7 @@ import { ethers, providers, Wallet } from 'ethers'; import { keccak256 } from 'ethers/lib/utils'; import { Alice, Bob, Cohort, Configuration, RemoteBob } from '../src'; -import { CoordinatorRitual, DkgParticipant } from '../src/agents/coordinator'; +import { DkgParticipant } from '../src/agents/coordinator'; import { CbdDecryptResult, GetUrsulasResult, @@ -409,8 +409,23 @@ export const fakeDkgTDecFlowE2e = ( }; }; -export const fakeCoordinatorRitual = (ritualId: number): CoordinatorRitual => { +export const fakeCoordinatorRitual = ( + ritualId: number +): { + aggregationMismatch: boolean; + initTimestamp: number; + aggregatedTranscriptHash: string; + initiator: string; + dkgSize: number; + id: number; + publicKey: { word1: string; word0: string }; + totalTranscripts: number; + aggregatedTranscript: string; + publicKeyHash: string; + totalAggregations: number; +} => { const ritual = fakeDkgTDecFlowE2e(FerveoVariant.Precomputed); + const dkgPkBytes = ritual.dkg.publicKey().toBytes(); return { id: ritualId, initiator: ritual.validators[0].address.toString(), @@ -421,7 +436,10 @@ export const fakeCoordinatorRitual = (ritualId: number): CoordinatorRitual => { aggregatedTranscriptHash: keccak256(ritual.serverAggregate.toBytes()), aggregationMismatch: false, // Assuming the ritual is correct aggregatedTranscript: toHexString(ritual.serverAggregate.toBytes()), - publicKey: toHexString(ritual.dkg.publicKey().toBytes()), + publicKey: { + word0: toHexString(dkgPkBytes.slice(0, 32)), + word1: toHexString(dkgPkBytes.slice(32, 48)), + }, publicKeyHash: keccak256(ritual.dkg.publicKey().toBytes()), }; }; From a47e20879c53006d4a33c9965a240596ab49973c Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 9 Jun 2023 16:16:21 +0200 Subject: [PATCH 54/98] use coordinator provided session keys --- src/agents/coordinator.ts | 7 +- src/characters/cbd-recipient.ts | 111 ++++++++++++++++------------ src/characters/porter.ts | 9 ++- src/dkg.ts | 56 +++++++------- src/sdk/strategy/cbd-strategy.ts | 2 - test/integration/dkg-client.test.ts | 22 +++--- test/unit/cbd-strategy.test.ts | 15 ++-- test/utils.ts | 109 +++++++++++++-------------- 8 files changed, 167 insertions(+), 164 deletions(-) diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts index 1c047cde5..80fd82fc0 100644 --- a/src/agents/coordinator.ts +++ b/src/agents/coordinator.ts @@ -19,12 +19,7 @@ export interface CoordinatorRitual { aggregatedTranscript: string; } -export interface DkgParticipant { - node: string; - aggregated: boolean; - transcript: string; - publicKey: string; -} +export type DkgParticipant = Coordinator.ParticipantStructOutput; export class DkgCoordinatorAgent { public static async getParticipants( diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 8ccaaf919..125b39f6b 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -6,23 +6,25 @@ import { DecryptionSharePrecomputed, DecryptionShareSimple, decryptWithSharedSecret, + EncryptedThresholdDecryptionRequest, + EncryptedThresholdDecryptionResponse, SessionSecretFactory, SessionSharedSecret, + SessionStaticKey, SharedSecret, ThresholdDecryptionRequest, } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; +import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator'; import { ConditionSet } from '../conditions'; import { DkgRitual, FerveoVariant } from '../dkg'; -import { ChecksumAddress } from '../types'; -import { fromJSON, toBytes, toJSON } from '../utils'; +import { fromHexString, fromJSON, toBytes, toJSON } from '../utils'; -import { CbdDecryptResult, Porter } from './porter'; +import { Porter } from './porter'; export type CbdTDecDecrypterJSON = { porterUri: string; - ursulas: Array; threshold: number; }; @@ -31,11 +33,7 @@ export class CbdTDecDecrypter { // private readonly verifyingKey: Keyring; - constructor( - porterUri: string, - private readonly ursulas: Array, - private readonly threshold: number - ) { + constructor(porterUri: string, private readonly threshold: number) { this.porter = new Porter(porterUri); } @@ -87,39 +85,42 @@ export class CbdTDecDecrypter { ): Promise { const contextStr = await conditionSet.buildContext(provider).toJson(); - // TODO: Move ThresholdDecryptionRequest creation and parsing to Porter? - const { sessionSharedSecret, encryptedRequest } = - this.makeDecryptionRequest( - ritualId, - variant, - ciphertext, - conditionSet, - contextStr - ); + const dkgParticipants = await DkgCoordinatorAgent.getParticipants( + provider, + ritualId + ); + + const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests( + ritualId, + variant, + ciphertext, + conditionSet, + contextStr, + dkgParticipants + ); - const cbdDecryptResult = await this.porter.cbdDecrypt( - encryptedRequest, - this.ursulas, + const { encryptedResponses } = await this.porter.cbdDecrypt( + encryptedRequests, this.threshold ); return this.makeDecryptionShares( - cbdDecryptResult, - sessionSharedSecret, + encryptedResponses, + sharedSecrets, variant ); } private makeDecryptionShares( - cbdDecryptResult: CbdDecryptResult, - sessionSharedSecret: SessionSharedSecret, + encryptedResponses: Record, + sessionSharedSecret: Record, variant: number ) { - const decryptedResponses = Object.entries( - cbdDecryptResult.encryptedResponses - ).map(([, encryptedResponse]) => - encryptedResponse.decrypt(sessionSharedSecret) + const decryptedResponses = Object.entries(encryptedResponses).map( + ([ursula, encryptedResponse]) => + encryptedResponse.decrypt(sessionSharedSecret[ursula]) ); + const variants = decryptedResponses.map((resp) => resp.ritualId); if (variants.some((v) => v !== variant)) { throw new Error('Decryption shares are not of the same variant'); @@ -142,13 +143,17 @@ export class CbdTDecDecrypter { } } - private makeDecryptionRequest( + private makeDecryptionRequests( ritualId: number, variant: number, ciphertext: Ciphertext, conditionSet: ConditionSet, - contextStr: string - ) { + contextStr: string, + dkgParticipants: Array + ): { + sharedSecrets: Record; + encryptedRequests: Record; + } { const decryptionRequest = new ThresholdDecryptionRequest( ritualId, variant, @@ -156,25 +161,41 @@ export class CbdTDecDecrypter { conditionSet.toWASMConditions(), new Context(contextStr) ); - const secretFactory = SessionSecretFactory.random(); const label = toBytes(`${ritualId}`); - const ursulaPublicKey = secretFactory.makeKey(label).publicKey(); const requesterSecretKey = secretFactory.makeKey(label); - const sessionSharedSecret = - requesterSecretKey.deriveSharedSecret(ursulaPublicKey); - const encryptedRequest = decryptionRequest.encrypt( - sessionSharedSecret, - requesterSecretKey.publicKey() + const sharedSecrets: Record = + Object.fromEntries( + dkgParticipants.map((participant) => { + const decKey = SessionStaticKey.fromBytes( + fromHexString(participant.decryptionRequestStaticKey) + ); + const sessionSharedSecret = + requesterSecretKey.deriveSharedSecret(decKey); + return [participant.provider, sessionSharedSecret]; + }) + ); + + const encryptedRequests: Record< + string, + EncryptedThresholdDecryptionRequest + > = Object.fromEntries( + Object.entries(sharedSecrets).map(([provider, sessionSharedSecret]) => { + const encryptedRequest = decryptionRequest.encrypt( + sessionSharedSecret, + requesterSecretKey.publicKey() + ); + return [provider, encryptedRequest]; + }) ); - return { sessionSharedSecret, encryptedRequest }; + + return { sharedSecrets, encryptedRequests }; } public toObj(): CbdTDecDecrypterJSON { return { porterUri: this.porter.porterUrl.toString(), - ursulas: this.ursulas, threshold: this.threshold, }; } @@ -183,12 +204,8 @@ export class CbdTDecDecrypter { return toJSON(this.toObj()); } - public static fromObj({ - porterUri, - ursulas, - threshold, - }: CbdTDecDecrypterJSON) { - return new CbdTDecDecrypter(porterUri, ursulas, threshold); + public static fromObj({ porterUri, threshold }: CbdTDecDecrypterJSON) { + return new CbdTDecDecrypter(porterUri, threshold); } public static fromJSON(json: string) { diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 366e49eaa..1fd05f24f 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -158,14 +158,15 @@ export class Porter { } public async cbdDecrypt( - encryptedRequest: EncryptedThresholdDecryptionRequest, - ursulas: Array, + encryptedRequests: Record, threshold: number ): Promise { - const encodedEncryptedRequest = toBase64(encryptedRequest.toBytes()); const data: PostCbdDecryptRequest = { encrypted_decryption_requests: Object.fromEntries( - ursulas.map((ursula) => [ursula, encodedEncryptedRequest]) + Object.entries(encryptedRequests).map(([ursula, encryptedRequest]) => [ + ursula, + toBase64(encryptedRequest.toBytes()), + ]) ), threshold, }; diff --git a/src/dkg.ts b/src/dkg.ts index faaa6778f..5d2c9deb0 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -1,17 +1,7 @@ -import { - AggregatedTranscript, - DkgPublicKey, - DkgPublicParameters, - EthereumAddress, - FerveoPublicKey, - Transcript, - Validator, - ValidatorMessage, -} from '@nucypher/nucypher-core'; +import { DkgPublicKey, DkgPublicParameters } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { DkgCoordinatorAgent } from './agents/coordinator'; -import { bytesEquals, fromHexString } from './utils'; +import { bytesEquals } from './utils'; // TOOD: Move to nucypher-core export enum FerveoVariant { @@ -70,25 +60,31 @@ export class DkgClient { // eslint-disable-next-line @typescript-eslint/no-unused-vars _ritualParams: unknown ): Promise { + // TODO: Remove this check after implementing this method + if (!this.provider._isProvider) { + throw new Error('Invalid provider'); + } // TODO: Create a new DKG ritual here throw new Error('Not implemented'); } - public async verifyRitual(ritualId: number): Promise { - const ritual = await DkgCoordinatorAgent.getRitual(this.provider, ritualId); - const participants = await DkgCoordinatorAgent.getParticipants( - this.provider, - ritualId - ); - - const validatorMessages = participants.map((p) => { - const validatorAddress = EthereumAddress.fromString(p.node); - const publicKey = FerveoPublicKey.fromBytes(fromHexString(p.publicKey)); - const validator = new Validator(validatorAddress, publicKey); - const transcript = Transcript.fromBytes(fromHexString(p.transcript)); - return new ValidatorMessage(validator, transcript); - }); - const aggregate = new AggregatedTranscript(validatorMessages); - - return aggregate.verify(ritual.dkgSize, validatorMessages); - } + // TODO: Without Validator public key in Coordinator, we cannot verify the + // transcript. We need to add it to the Coordinator. + // public async verifyRitual(ritualId: number): Promise { + // const ritual = await DkgCoordinatorAgent.getRitual(this.provider, ritualId); + // const participants = await DkgCoordinatorAgent.getParticipants( + // this.provider, + // ritualId + // ); + // + // const validatorMessages = participants.map((p) => { + // const validatorAddress = EthereumAddress.fromString(p.provider); + // const publicKey = FerveoPublicKey.fromBytes(fromHexString(p.???)); + // const validator = new Validator(validatorAddress, publicKey); + // const transcript = Transcript.fromBytes(fromHexString(p.transcript)); + // return new ValidatorMessage(validator, transcript); + // }); + // const aggregate = new AggregatedTranscript(validatorMessages); + // + // return aggregate.verify(ritual.dkgSize, validatorMessages); + // } } diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 5f2729fb7..951d84d34 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -49,7 +49,6 @@ export class CbdStrategy { const decrypter = new CbdTDecDecrypter( this.cohort.configuration.porterUri, - this.cohort.ursulaAddresses, this.cohort.configuration.threshold ); @@ -128,7 +127,6 @@ export class DeployedCbdStrategy { ); const decrypter = new CbdTDecDecrypter( cohort.configuration.porterUri, - cohort.ursulaAddresses, cohort.configuration.threshold ); return new DeployedCbdStrategy( diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts index f14273979..aeaa282f9 100644 --- a/test/integration/dkg-client.test.ts +++ b/test/integration/dkg-client.test.ts @@ -1,7 +1,6 @@ import { SecretKey } from '@nucypher/nucypher-core'; import { DkgCoordinatorAgent } from '../../src/agents/coordinator'; -import { DkgClient } from '../../src/dkg'; import { fakeCoordinatorRitual, fakeDkgParticipants, @@ -12,7 +11,7 @@ const ritualId = 1; jest.mock('../../src/agents/coordinator', () => ({ DkgCoordinatorAgent: { getRitual: () => Promise.resolve(fakeCoordinatorRitual(ritualId)), - getParticipants: () => Promise.resolve(fakeDkgParticipants()), + getParticipants: () => Promise.resolve(fakeDkgParticipants(ritualId)), }, })); @@ -39,12 +38,13 @@ describe('DkgCoordinatorAgent', () => { }); }); -describe('DkgClient', () => { - it('verifies the dkg ritual', async () => { - const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - - const dkgClient = new DkgClient(provider); - const isValid = await dkgClient.verifyRitual(ritualId); - expect(isValid).toBeTruthy(); - }); -}); +// TODO: Fix this test after the DkgClient.verifyRitual() method is implemented +// describe('DkgClient', () => { +// it('verifies the dkg ritual', async () => { +// const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); +// +// const dkgClient = new DkgClient(provider); +// const isValid = await dkgClient.verifyRitual(ritualId); +// expect(isValid).toBeTruthy(); +// }); +// }); diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 9cf5621e0..12ad90bfd 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -16,6 +16,7 @@ import { fakeWeb3Provider, makeCohort, mockCbdDecrypt, + mockGetParticipants, mockGetUrsulas, mockInitializeRitual, } from '../utils'; @@ -36,11 +37,11 @@ const ownsNFT = new ERC721Ownership({ chain: 5, }); const conditionSet = new ConditionSet([ownsNFT]); -const mockedUrsulas = fakeUrsulas().slice(0, 3); +const ursulas = fakeUrsulas().slice(0, 3); const variant = FerveoVariant.Precomputed; const makeCbdStrategy = async () => { - const cohort = await makeCohort(mockedUrsulas); + const cohort = await makeCohort(ursulas); const strategy = CbdStrategy.create(cohort, conditionSet); expect(strategy.cohort).toEqual(cohort); return strategy; @@ -52,7 +53,7 @@ async function makeDeployedCbdStrategy() { const mockedDkg = fakeDkgFlow(variant, 0); const mockedDkgRitual = fakeDkgRitual(mockedDkg); const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); - const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); + const getUrsulasSpy = mockGetUrsulas(ursulas); const initializeRitualSpy = mockInitializeRitual(mockedDkgRitual); const deployedStrategy = await strategy.deploy(web3Provider); @@ -114,11 +115,12 @@ describe('CbdDeployedStrategy', () => { aad, ciphertext, }); - const getUrsulasSpy2 = mockGetUrsulas(mockedUrsulas); + const getUrsulasSpy2 = mockGetUrsulas(ursulas); + const getParticipantsSpy = mockGetParticipants(mockedDkg.ritualId, variant); const decryptSpy = mockCbdDecrypt( - mockedDkg.tau, + mockedDkg.ritualId, decryptionShares, - mockedUrsulas.map((u) => u.checksumAddress) + ursulas.map((u) => u.checksumAddress) ); const decryptedMessage = @@ -131,6 +133,7 @@ describe('CbdDeployedStrategy', () => { aad ); expect(getUrsulasSpy2).toHaveBeenCalled(); + expect(getParticipantsSpy).toHaveBeenCalled(); expect(decryptSpy).toHaveBeenCalled(); expect(decryptedMessage[0]).toEqual(toBytes(message)); }); diff --git a/test/utils.ts b/test/utils.ts index 995835c4c..146584e72 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -36,7 +36,7 @@ import { ethers, providers, Wallet } from 'ethers'; import { keccak256 } from 'ethers/lib/utils'; import { Alice, Bob, Cohort, Configuration, RemoteBob } from '../src'; -import { DkgParticipant } from '../src/agents/coordinator'; +import { DkgCoordinatorAgent, DkgParticipant } from '../src/agents/coordinator'; import { CbdDecryptResult, GetUrsulasResult, @@ -103,41 +103,16 @@ export const fakeWeb3Provider = ( } as unknown as ethers.providers.Web3Provider; }; -export const fakeUrsulas = (): readonly Ursula[] => { - return [ - { - encryptingKey: SecretKey.random().publicKey(), - checksumAddress: '0x5cF1703A1c99A4b42Eb056535840e93118177232', - uri: 'https://example.a.com:9151', - }, - { - encryptingKey: SecretKey.random().publicKey(), - checksumAddress: '0x7fff551249D223f723557a96a0e1a469C79cC934', - uri: 'https://example.b.com:9151', - }, - { - encryptingKey: SecretKey.random().publicKey(), - checksumAddress: '0x9C7C824239D3159327024459Ad69bB215859Bd25', - uri: 'https://example.c.com:9151', - }, - { - encryptingKey: SecretKey.random().publicKey(), - checksumAddress: '0x9919C9f5CbBAA42CB3bEA153E14E16F85fEA5b5D', - uri: 'https://example.d.com:9151', - }, - { - encryptingKey: SecretKey.random().publicKey(), - checksumAddress: '0xfBeb3368735B3F0A65d1F1E02bf1d188bb5F5BE6', - uri: 'https://example.e.com:9151', - }, - ].map(({ encryptingKey, checksumAddress, uri }) => { - return { - checksumAddress: checksumAddress.toLowerCase(), - encryptingKey, - uri, - }; - }); -}; +const genChecksumAddress = (i: number) => + '0x' + '0'.repeat(40 - i.toString(16).length) + i.toString(16); +const genEthAddr = (i: number) => + EthereumAddress.fromString(genChecksumAddress(i)); +export const fakeUrsulas = (): readonly Ursula[] => + [0, 1, 2, 3, 4].map((i: number) => ({ + encryptingKey: SecretKey.random().publicKey(), + checksumAddress: genChecksumAddress(i).toLowerCase(), + uri: 'https://example.a.com:9151', + })); export const mockGetUrsulas = (ursulas: readonly Ursula[]) => { const fakePorterUrsulas = ( @@ -237,7 +212,7 @@ export const mockDetectEthereumProvider = () => { export const fakeDkgFlow = ( variant: FerveoVariant | FerveoVariant.Precomputed, - tau: number, + ritualId: number, sharesNum = 4, threshold = 3 ) => { @@ -247,12 +222,6 @@ export const fakeDkgFlow = ( ) { throw new Error(`Invalid variant: ${variant}`); } - - const genEthAddr = (i: number) => { - const ethAddr = - '0x' + '0'.repeat(40 - i.toString(16).length) + i.toString(16); - return EthereumAddress.fromString(ethAddr); - }; const validatorKeypairs: Keypair[] = []; const validators: Validator[] = []; for (let i = 0; i < sharesNum; i++) { @@ -267,7 +236,7 @@ export const fakeDkgFlow = ( const messages: ValidatorMessage[] = []; const transcripts: Transcript[] = []; validators.forEach((sender) => { - const dkg = new Dkg(tau, sharesNum, threshold, validators, sender); + const dkg = new Dkg(ritualId, sharesNum, threshold, validators, sender); const transcript = dkg.generateTranscript(); transcripts.push(transcript); const message = new ValidatorMessage(sender, transcript); @@ -276,7 +245,13 @@ export const fakeDkgFlow = ( // Now that every validator holds a dkg instance and a transcript for every other validator, // every validator can aggregate the transcripts - const dkg = new Dkg(tau, sharesNum, threshold, validators, validators[0]); + const dkg = new Dkg( + ritualId, + sharesNum, + threshold, + validators, + validators[0] + ); // Let's say that we've only received `threshold` transcripts const receivedMessages = messages.slice(0, threshold); @@ -288,7 +263,7 @@ export const fakeDkgFlow = ( const clientAggregate = new AggregatedTranscript(receivedMessages); expect(clientAggregate.verify(sharesNum, receivedMessages)).toBeTruthy(); return { - tau, + ritualId, sharesNum, threshold, validatorKeypairs, @@ -303,7 +278,7 @@ export const fakeDkgFlow = ( interface FakeDkgRitualFlow { validators: Validator[]; validatorKeypairs: Keypair[]; - tau: number; + ritualId: number; sharesNum: number; threshold: number; receivedMessages: ValidatorMessage[]; @@ -317,7 +292,7 @@ interface FakeDkgRitualFlow { export const fakeTDecFlow = ({ validators, validatorKeypairs, - tau, + ritualId, sharesNum, threshold, receivedMessages, @@ -333,7 +308,7 @@ export const fakeTDecFlow = ({ | DecryptionShareSimple )[] = []; zip(validators, validatorKeypairs).forEach(([validator, keypair]) => { - const dkg = new Dkg(tau, sharesNum, threshold, validators, validator); + const dkg = new Dkg(ritualId, sharesNum, threshold, validators, validator); const aggregate = dkg.aggregateTranscript(receivedMessages); const isValid = aggregate.verify(sharesNum, receivedMessages); if (!isValid) { @@ -444,21 +419,39 @@ export const fakeCoordinatorRitual = ( }; }; -export const fakeDkgParticipants = (): DkgParticipant[] => { - const ritual = fakeDkgTDecFlowE2e(FerveoVariant.Precomputed); - return zip( - zip(ritual.validators, ritual.transcripts), - ritual.validatorKeypairs - ).map(([[v, t], k]) => { +export const fakeDkgParticipants = ( + ritualId: number, + variant = FerveoVariant.Precomputed +): DkgParticipant[] => { + const ritual = fakeDkgTDecFlowE2e(variant); + const secretFactory = SessionSecretFactory.random(); + + return zip(ritual.validators, ritual.transcripts).map(([v, t]) => { + const label = toBytes(`${ritualId}`); + const decryptionRequestStaticKey = secretFactory.makeKey(label).publicKey(); return { - node: v.address.toString(), + provider: v.address.toString(), aggregated: true, // Assuming all validators already contributed to the aggregate transcript: toHexString(t.toBytes()), - publicKey: toHexString(k.publicKey.toBytes()), - }; + decryptionRequestStaticKey: toHexString( + decryptionRequestStaticKey.toBytes() + ), + } as DkgParticipant; }); }; +export const mockGetParticipants = ( + ritualId: number, + variant = FerveoVariant.Precomputed +) => { + const participants = fakeDkgParticipants(ritualId, variant); + return jest + .spyOn(DkgCoordinatorAgent, 'getParticipants') + .mockImplementation(() => { + return Promise.resolve(participants); + }); +}; + export const mockCbdDecrypt = ( ritualId: number, decryptionShares: (DecryptionSharePrecomputed | DecryptionShareSimple)[], From f9c43d0f75cae8eb28fca3e8c430acadc154c2fe Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 9 Jun 2023 17:39:17 +0200 Subject: [PATCH 55/98] properly establish session keys --- src/characters/cbd-recipient.ts | 83 +++++++++++++++-------------- src/dkg.ts | 32 ++++++++++- test/integration/dkg-client.test.ts | 20 ++++--- test/unit/cbd-strategy.test.ts | 20 +++++-- test/utils.ts | 80 ++++++++++++++++----------- 5 files changed, 148 insertions(+), 87 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 125b39f6b..7cafa258a 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -1,16 +1,14 @@ import { Ciphertext, - combineDecryptionSharesPrecomputed, - combineDecryptionSharesSimple, Context, DecryptionSharePrecomputed, DecryptionShareSimple, decryptWithSharedSecret, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, - SessionSecretFactory, SessionSharedSecret, SessionStaticKey, + SessionStaticSecret, SharedSecret, ThresholdDecryptionRequest, } from '@nucypher/nucypher-core'; @@ -18,8 +16,8 @@ import { ethers } from 'ethers'; import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator'; import { ConditionSet } from '../conditions'; -import { DkgRitual, FerveoVariant } from '../dkg'; -import { fromHexString, fromJSON, toBytes, toJSON } from '../utils'; +import { combineDecryptionSharesMap, DkgRitual, variantMap } from '../dkg'; +import { fromHexString, fromJSON, toJSON } from '../utils'; import { Porter } from './porter'; @@ -53,16 +51,10 @@ export class CbdTDecDecrypter { ciphertext ); - // TODO: Replace with a factory method let sharedSecret: SharedSecret; - if (variant === FerveoVariant.Simple) { - sharedSecret = combineDecryptionSharesSimple( - decryptionShares as DecryptionShareSimple[] - ); - } else if (variant === FerveoVariant.Precomputed) { - sharedSecret = combineDecryptionSharesPrecomputed( - decryptionShares as DecryptionSharePrecomputed[] - ); + const combineDecryptionSharesFn = combineDecryptionSharesMap[variant]; + if (combineDecryptionSharesFn) { + sharedSecret = combineDecryptionSharesFn(decryptionShares); } else { throw new Error(`Unknown variant ${variant}`); } @@ -83,13 +75,11 @@ export class CbdTDecDecrypter { variant: number, ciphertext: Ciphertext ): Promise { - const contextStr = await conditionSet.buildContext(provider).toJson(); - const dkgParticipants = await DkgCoordinatorAgent.getParticipants( provider, ritualId ); - + const contextStr = await conditionSet.buildContext(provider).toJson(); const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests( ritualId, variant, @@ -99,44 +89,50 @@ export class CbdTDecDecrypter { dkgParticipants ); - const { encryptedResponses } = await this.porter.cbdDecrypt( + const { encryptedResponses, errors } = await this.porter.cbdDecrypt( encryptedRequests, this.threshold ); - + // TODO: How many errors are acceptable? Less than (threshold - shares)? + if (Object.keys(errors).length > 0) { + throw new Error( + `CBD decryption failed with errors: ${JSON.stringify(errors)}` + ); + } return this.makeDecryptionShares( encryptedResponses, sharedSecrets, - variant + variant, + ritualId ); } private makeDecryptionShares( encryptedResponses: Record, sessionSharedSecret: Record, - variant: number + variant: number, + expectedRitualId: number ) { const decryptedResponses = Object.entries(encryptedResponses).map( ([ursula, encryptedResponse]) => encryptedResponse.decrypt(sessionSharedSecret[ursula]) ); - const variants = decryptedResponses.map((resp) => resp.ritualId); - if (variants.some((v) => v !== variant)) { - throw new Error('Decryption shares are not of the same variant'); + const ritualIds = decryptedResponses.map(({ ritualId }) => ritualId); + if (ritualIds.some((ritualId) => ritualId !== expectedRitualId)) { + throw new Error( + `Ritual id mismatch. Expected ${expectedRitualId}, got ${ritualIds}` + ); } const decryptionShares = decryptedResponses.map( - (resp) => resp.decryptionShare + ({ decryptionShare }) => decryptionShare ); - // TODO: Replace with a factory method - if (variant === FerveoVariant.Simple) { - return decryptionShares.map((share) => - DecryptionShareSimple.fromBytes(share) - ); - } else if (variant === FerveoVariant.Precomputed) { + + const DecryptionShareType = variantMap[variant]; + if (DecryptionShareType) { return decryptionShares.map((share) => - DecryptionSharePrecomputed.fromBytes(share) + DecryptionShareType.fromBytes(share) ); } else { throw new Error(`Unknown variant ${variant}`); @@ -161,22 +157,22 @@ export class CbdTDecDecrypter { conditionSet.toWASMConditions(), new Context(contextStr) ); - const secretFactory = SessionSecretFactory.random(); - const label = toBytes(`${ritualId}`); - const requesterSecretKey = secretFactory.makeKey(label); + const ephemeralSessionKey = this.makeSessionKey(); + + // Compute shared secrets for each participant const sharedSecrets: Record = Object.fromEntries( - dkgParticipants.map((participant) => { + dkgParticipants.map(({ provider, decryptionRequestStaticKey }) => { const decKey = SessionStaticKey.fromBytes( - fromHexString(participant.decryptionRequestStaticKey) + fromHexString(decryptionRequestStaticKey) ); - const sessionSharedSecret = - requesterSecretKey.deriveSharedSecret(decKey); - return [participant.provider, sessionSharedSecret]; + const sharedSecret = ephemeralSessionKey.deriveSharedSecret(decKey); + return [provider, sharedSecret]; }) ); + // Create encrypted requests for each participant const encryptedRequests: Record< string, EncryptedThresholdDecryptionRequest @@ -184,7 +180,7 @@ export class CbdTDecDecrypter { Object.entries(sharedSecrets).map(([provider, sessionSharedSecret]) => { const encryptedRequest = decryptionRequest.encrypt( sessionSharedSecret, - requesterSecretKey.publicKey() + ephemeralSessionKey.publicKey() ); return [provider, encryptedRequest]; }) @@ -193,6 +189,11 @@ export class CbdTDecDecrypter { return { sharedSecrets, encryptedRequests }; } + private makeSessionKey() { + // Moving to a separate function to make it easier to mock + return SessionStaticSecret.random(); + } + public toObj(): CbdTDecDecrypterJSON { return { porterUri: this.porter.porterUrl.toString(), diff --git a/src/dkg.ts b/src/dkg.ts index 5d2c9deb0..6d0314437 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -1,14 +1,42 @@ -import { DkgPublicKey, DkgPublicParameters } from '@nucypher/nucypher-core'; +import { + combineDecryptionSharesPrecomputed, + combineDecryptionSharesSimple, + DecryptionSharePrecomputed, + DecryptionShareSimple, + DkgPublicKey, + DkgPublicParameters, + SharedSecret, +} from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { bytesEquals } from './utils'; -// TOOD: Move to nucypher-core +// TODO: Expose from @nucypher/nucypher-core export enum FerveoVariant { Simple = 0, Precomputed = 1, } +// TODO: Replace with a factory method +export const variantMap: { + [key: number]: + | typeof DecryptionShareSimple + | typeof DecryptionSharePrecomputed; +} = { + [FerveoVariant.Simple]: DecryptionShareSimple, + [FerveoVariant.Precomputed]: DecryptionSharePrecomputed, +}; + +// TODO: Replace with a factory method +export const combineDecryptionSharesMap: { + [key: number]: ( + shares: DecryptionShareSimple[] | DecryptionSharePrecomputed[] + ) => SharedSecret; +} = { + [FerveoVariant.Simple]: combineDecryptionSharesSimple, + [FerveoVariant.Precomputed]: combineDecryptionSharesPrecomputed, +}; + export interface DkgRitualJSON { id: number; dkgPublicKey: Uint8Array; diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts index aeaa282f9..acb8337b6 100644 --- a/test/integration/dkg-client.test.ts +++ b/test/integration/dkg-client.test.ts @@ -4,14 +4,15 @@ import { DkgCoordinatorAgent } from '../../src/agents/coordinator'; import { fakeCoordinatorRitual, fakeDkgParticipants, + fakeRitualId, fakeWeb3Provider, + mockGetParticipants, } from '../utils'; -const ritualId = 1; jest.mock('../../src/agents/coordinator', () => ({ DkgCoordinatorAgent: { - getRitual: () => Promise.resolve(fakeCoordinatorRitual(ritualId)), - getParticipants: () => Promise.resolve(fakeDkgParticipants(ritualId)), + getRitual: () => Promise.resolve(fakeCoordinatorRitual(fakeRitualId)), + getParticipants: () => Promise.resolve(fakeDkgParticipants(fakeRitualId)), }, })); @@ -22,18 +23,21 @@ describe('DkgCoordinatorAgent', () => { it('fetches transcripts from the coordinator', async () => { const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - const ritual = await DkgCoordinatorAgent.getRitual(provider, ritualId); - + const ritual = await DkgCoordinatorAgent.getRitual(provider, fakeRitualId); expect(ritual).toBeDefined(); }); it('fetches participants from the coordinator', async () => { const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + const fakeParticipants = fakeDkgParticipants(fakeRitualId); + const getParticipantsSpy = mockGetParticipants( + fakeParticipants.participants + ); const participants = await DkgCoordinatorAgent.getParticipants( provider, - ritualId + fakeRitualId ); - + expect(getParticipantsSpy).toHaveBeenCalled(); expect(participants.length).toBeGreaterThan(0); }); }); @@ -44,7 +48,7 @@ describe('DkgCoordinatorAgent', () => { // const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); // // const dkgClient = new DkgClient(provider); -// const isValid = await dkgClient.verifyRitual(ritualId); +// const isValid = await dkgClient.verifyRitual(fakeRitualId); // expect(isValid).toBeTruthy(); // }); // }); diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 12ad90bfd..9774dcd43 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -1,4 +1,4 @@ -import { SecretKey } from '@nucypher/nucypher-core'; +import { SecretKey, SessionStaticSecret } from '@nucypher/nucypher-core'; import { conditions } from '../../src'; import { CbdTDecDecrypter } from '../../src/characters/cbd-recipient'; @@ -10,6 +10,7 @@ import { import { toBytes } from '../../src/utils'; import { fakeDkgFlow, + fakeDkgParticipants, fakeDkgRitual, fakeTDecFlow, fakeUrsulas, @@ -19,6 +20,7 @@ import { mockGetParticipants, mockGetUrsulas, mockInitializeRitual, + mockRandomSessionStaticSecret, } from '../utils'; import { aliceSecretKeyBytes } from './testVariables'; @@ -115,13 +117,20 @@ describe('CbdDeployedStrategy', () => { aad, ciphertext, }); - const getUrsulasSpy2 = mockGetUrsulas(ursulas); - const getParticipantsSpy = mockGetParticipants(mockedDkg.ritualId, variant); + const { participantSecrets, participants } = fakeDkgParticipants( + mockedDkg.ritualId, + variant + ); + const requesterSessionKey = SessionStaticSecret.random(); const decryptSpy = mockCbdDecrypt( mockedDkg.ritualId, decryptionShares, - ursulas.map((u) => u.checksumAddress) + participantSecrets, + requesterSessionKey.publicKey() ); + const getParticipantsSpy = mockGetParticipants(participants); + const getUrsulasSpy = mockGetUrsulas(ursulas); + const sessionKeySpy = mockRandomSessionStaticSecret(requesterSessionKey); const decryptedMessage = await deployedStrategy.decrypter.retrieveAndDecrypt( @@ -132,8 +141,9 @@ describe('CbdDeployedStrategy', () => { ciphertext, aad ); - expect(getUrsulasSpy2).toHaveBeenCalled(); + expect(getUrsulasSpy).toHaveBeenCalled(); expect(getParticipantsSpy).toHaveBeenCalled(); + expect(sessionKeySpy).toHaveBeenCalled(); expect(decryptSpy).toHaveBeenCalled(); expect(decryptedMessage[0]).toEqual(toBytes(message)); }); diff --git a/test/utils.ts b/test/utils.ts index 146584e72..0d78c2102 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -12,6 +12,8 @@ import { reencrypt, SecretKey, SessionSecretFactory, + SessionStaticKey, + SessionStaticSecret, ThresholdDecryptionResponse, VerifiedCapsuleFrag, VerifiedKeyFrag, @@ -37,6 +39,7 @@ import { keccak256 } from 'ethers/lib/utils'; import { Alice, Bob, Cohort, Configuration, RemoteBob } from '../src'; import { DkgCoordinatorAgent, DkgParticipant } from '../src/agents/coordinator'; +import { CbdTDecDecrypter } from '../src/characters/cbd-recipient'; import { CbdDecryptResult, GetUrsulasResult, @@ -422,29 +425,36 @@ export const fakeCoordinatorRitual = ( export const fakeDkgParticipants = ( ritualId: number, variant = FerveoVariant.Precomputed -): DkgParticipant[] => { +): { + participants: DkgParticipant[]; + participantSecrets: Record; +} => { const ritual = fakeDkgTDecFlowE2e(variant); - const secretFactory = SessionSecretFactory.random(); - - return zip(ritual.validators, ritual.transcripts).map(([v, t]) => { - const label = toBytes(`${ritualId}`); - const decryptionRequestStaticKey = secretFactory.makeKey(label).publicKey(); + const label = toBytes(`${ritualId}`); + + const participantSecrets: Record = + Object.fromEntries( + ritual.validators.map(({ address }) => { + const participantSecret = SessionSecretFactory.random().makeKey(label); + return [address.toString(), participantSecret]; + }) + ); + + const participants: DkgParticipant[] = zip( + Object.entries(participantSecrets), + ritual.transcripts + ).map(([[address, secret], transcript]) => { return { - provider: v.address.toString(), + provider: address, aggregated: true, // Assuming all validators already contributed to the aggregate - transcript: toHexString(t.toBytes()), - decryptionRequestStaticKey: toHexString( - decryptionRequestStaticKey.toBytes() - ), + transcript: toHexString(transcript.toBytes()), + decryptionRequestStaticKey: toHexString(secret.publicKey().toBytes()), } as DkgParticipant; }); + return { participantSecrets, participants }; }; -export const mockGetParticipants = ( - ritualId: number, - variant = FerveoVariant.Precomputed -) => { - const participants = fakeDkgParticipants(ritualId, variant); +export const mockGetParticipants = (participants: DkgParticipant[]) => { return jest .spyOn(DkgCoordinatorAgent, 'getParticipants') .mockImplementation(() => { @@ -455,26 +465,22 @@ export const mockGetParticipants = ( export const mockCbdDecrypt = ( ritualId: number, decryptionShares: (DecryptionSharePrecomputed | DecryptionShareSimple)[], - ursulas: ChecksumAddress[], + participantSecrets: Record, + requesterPk: SessionStaticKey, errors: Record = {} ) => { const encryptedResponses: Record< string, EncryptedThresholdDecryptionResponse > = Object.fromEntries( - zip(decryptionShares, ursulas).map(([share, ursula]) => { - const secretFactory = SessionSecretFactory.random(); - const label = toBytes(`${ritualId}`); - const ursulaPublicKey = secretFactory.makeKey(label).publicKey(); - const requesterSecretKey = secretFactory.makeKey(label); - const sessionSharedSecret = - requesterSecretKey.deriveSharedSecret(ursulaPublicKey); - - const resp = new ThresholdDecryptionResponse(ritualId, share.toBytes()); - const encryptedResp = resp.encrypt(sessionSharedSecret); - - return [ursula, encryptedResp]; - }) + zip(decryptionShares, Object.entries(participantSecrets)).map( + ([share, [address, secret]]) => { + const resp = new ThresholdDecryptionResponse(ritualId, share.toBytes()); + const sessionSecret = secret.deriveSharedSecret(requesterPk); + const encryptedResp = resp.encrypt(sessionSecret); + return [address, encryptedResp]; + } + ) ); const result: CbdDecryptResult = { @@ -486,8 +492,20 @@ export const mockCbdDecrypt = ( }); }; +export const mockRandomSessionStaticSecret = (secret: SessionStaticSecret) => { + return jest + .spyOn(CbdTDecDecrypter.prototype as any, 'makeSessionKey') + .mockImplementation(() => secret); +}; + +export const fakeRitualId = 0; + export const fakeDkgRitual = (ritual: { dkg: Dkg }) => { - return new DkgRitual(1, ritual.dkg.publicKey(), ritual.dkg.publicParams()); + return new DkgRitual( + fakeRitualId, + ritual.dkg.publicKey(), + ritual.dkg.publicParams() + ); }; export const mockInitializeRitual = (fakeRitual: unknown) => { From 03eb03efe5a37e15e55741bc12b88819e863db1b Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 9 Jun 2023 17:57:21 +0200 Subject: [PATCH 56/98] self review --- src/characters/cbd-recipient.ts | 33 ++++++++++------------- src/dkg.ts | 46 +++++++++++++++++++------------- src/sdk/strategy/cbd-strategy.ts | 12 ++++----- src/sdk/strategy/pre-strategy.ts | 4 +-- 4 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 7cafa258a..d3dda39a3 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -9,14 +9,17 @@ import { SessionSharedSecret, SessionStaticKey, SessionStaticSecret, - SharedSecret, ThresholdDecryptionRequest, } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator'; import { ConditionSet } from '../conditions'; -import { combineDecryptionSharesMap, DkgRitual, variantMap } from '../dkg'; +import { + DkgRitual, + getCombineDecryptionSharesFunction, + getVariantClass, +} from '../dkg'; import { fromHexString, fromJSON, toJSON } from '../utils'; import { Porter } from './porter'; @@ -29,12 +32,11 @@ export type CbdTDecDecrypterJSON = { export class CbdTDecDecrypter { private readonly porter: Porter; - // private readonly verifyingKey: Keyring; - constructor(porterUri: string, private readonly threshold: number) { this.porter = new Porter(porterUri); } + // Retrieve and decrypt ciphertext using provider and condition set public async retrieveAndDecrypt( provider: ethers.providers.Web3Provider, conditionSet: ConditionSet, @@ -51,13 +53,9 @@ export class CbdTDecDecrypter { ciphertext ); - let sharedSecret: SharedSecret; - const combineDecryptionSharesFn = combineDecryptionSharesMap[variant]; - if (combineDecryptionSharesFn) { - sharedSecret = combineDecryptionSharesFn(decryptionShares); - } else { - throw new Error(`Unknown variant ${variant}`); - } + const combineDecryptionSharesFn = + getCombineDecryptionSharesFunction(variant); + const sharedSecret = combineDecryptionSharesFn(decryptionShares); const plaintext = decryptWithSharedSecret( ciphertext, @@ -68,6 +66,7 @@ export class CbdTDecDecrypter { return [plaintext]; } + // Retrieve decryption shares public async retrieve( provider: ethers.providers.Web3Provider, conditionSet: ConditionSet, @@ -129,14 +128,10 @@ export class CbdTDecDecrypter { ({ decryptionShare }) => decryptionShare ); - const DecryptionShareType = variantMap[variant]; - if (DecryptionShareType) { - return decryptionShares.map((share) => - DecryptionShareType.fromBytes(share) - ); - } else { - throw new Error(`Unknown variant ${variant}`); - } + const DecryptionShareType = getVariantClass(variant); + return decryptionShares.map((share) => + DecryptionShareType.fromBytes(share) + ); } private makeDecryptionRequests( diff --git a/src/dkg.ts b/src/dkg.ts index 6d0314437..a2b805fca 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -17,25 +17,32 @@ export enum FerveoVariant { Precomputed = 1, } -// TODO: Replace with a factory method -export const variantMap: { - [key: number]: - | typeof DecryptionShareSimple - | typeof DecryptionSharePrecomputed; -} = { - [FerveoVariant.Simple]: DecryptionShareSimple, - [FerveoVariant.Precomputed]: DecryptionSharePrecomputed, -}; - -// TODO: Replace with a factory method -export const combineDecryptionSharesMap: { - [key: number]: ( - shares: DecryptionShareSimple[] | DecryptionSharePrecomputed[] - ) => SharedSecret; -} = { - [FerveoVariant.Simple]: combineDecryptionSharesSimple, - [FerveoVariant.Precomputed]: combineDecryptionSharesPrecomputed, -}; +export function getVariantClass( + variant: FerveoVariant +): typeof DecryptionShareSimple | typeof DecryptionSharePrecomputed { + switch (variant) { + case FerveoVariant.Simple: + return DecryptionShareSimple; + case FerveoVariant.Precomputed: + return DecryptionSharePrecomputed; + default: + throw new Error(`Invalid FerveoVariant: ${variant}`); + } +} +export function getCombineDecryptionSharesFunction( + variant: FerveoVariant +): ( + shares: DecryptionShareSimple[] | DecryptionSharePrecomputed[] +) => SharedSecret { + switch (variant) { + case FerveoVariant.Simple: + return combineDecryptionSharesSimple; + case FerveoVariant.Precomputed: + return combineDecryptionSharesPrecomputed; + default: + throw new Error(`Invalid FerveoVariant: ${variant}`); + } +} export interface DkgRitualJSON { id: number; @@ -95,6 +102,7 @@ export class DkgClient { // TODO: Create a new DKG ritual here throw new Error('Not implemented'); } + // TODO: Without Validator public key in Coordinator, we cannot verify the // transcript. We need to add it to the Coordinator. // public async verifyRitual(ritualId: number): Promise { diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 951d84d34..fa4f5f072 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -87,18 +87,18 @@ export class CbdStrategy { const conditionSetEquals = this.conditionSet && other.conditionSet ? this.conditionSet.equals(other.conditionSet) - : false; + : this.conditionSet === other.conditionSet; return this.cohort.equals(other.cohort) && conditionSetEquals; } } export class DeployedCbdStrategy { constructor( - public cohort: Cohort, - public dkgRitual: DkgRitual, - public encrypter: Enrico, - public decrypter: CbdTDecDecrypter, - public conditionSet?: ConditionSet + public readonly cohort: Cohort, + public readonly dkgRitual: DkgRitual, + public readonly encrypter: Enrico, + public readonly decrypter: CbdTDecDecrypter, + public readonly conditionSet?: ConditionSet ) {} public static fromJSON(json: string) { diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index 2c0dce71c..88c79b91a 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -160,7 +160,7 @@ export class PreStrategy { const conditionSetEquals = this.conditionSet && other.conditionSet ? this.conditionSet.equals(other.conditionSet) - : false; + : this.conditionSet === other.conditionSet; return ( this.cohort.equals(other.cohort) && // TODO: Add equality to WASM bindings @@ -274,7 +274,7 @@ export class DeployedPreStrategy { const conditionSetEquals = this.conditionSet && other.conditionSet ? this.conditionSet.equals(other.conditionSet) - : false; + : this.conditionSet === other.conditionSet; return ( this.label === other.label && this.cohort.equals(other.cohort) && From efcf6704aa47833a202e40e6081c4fd37a703d1e Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 12 Jun 2023 09:57:41 +0200 Subject: [PATCH 57/98] refactor: evm and time cond to inherit from rpc --- src/conditions/base/evm.ts | 32 +++++++++++++++----------------- src/conditions/base/rpc.ts | 22 ++++++++++++---------- src/conditions/base/time.ts | 26 ++++++++++---------------- 3 files changed, 37 insertions(+), 43 deletions(-) diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts index 604817948..86c269df7 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/evm.ts @@ -1,26 +1,24 @@ import Joi from 'joi'; -import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; +import { ETH_ADDRESS_REGEXP } from '../const'; -import { Condition } from './condition'; -import { returnValueTestSchema } from './schema'; +import { RpcCondition, rpcConditionSchema } from './rpc'; export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; -export class EvmCondition extends Condition { - public readonly schema = Joi.object({ - contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), - chain: Joi.number() - .valid(...SUPPORTED_CHAINS) - .required(), - standardContractType: Joi.string() - .valid(...STANDARD_CONTRACT_TYPES) - .optional(), - functionAbi: Joi.object().optional(), - method: Joi.string().required(), - parameters: Joi.array().required(), - returnValueTest: returnValueTestSchema, - }) +const evmMethodSchemas: Record = { + ...rpcConditionSchema, + contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), + standardContractType: Joi.string() + .valid(...STANDARD_CONTRACT_TYPES) + .optional(), + method: Joi.string().required(), + functionAbi: Joi.object().optional(), + parameters: Joi.array().required(), +}; + +export class EvmCondition extends RpcCondition { + public readonly schema = Joi.object(evmMethodSchemas) // At most one of these keys needs to be present .xor('standardContractType', 'functionAbi'); } diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 5144c251f..4a088817d 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -18,15 +18,17 @@ const makeParameters = () => })), }); +export const rpcConditionSchema = { + chain: Joi.number() + .valid(...SUPPORTED_CHAINS) + .required(), + method: Joi.string() + .valid(...Object.keys(rpcMethodSchemas)) + .required(), + parameters: makeParameters(), + returnValueTest: returnValueTestSchema.required(), +}; + export class RpcCondition extends Condition { - public readonly schema = Joi.object({ - chain: Joi.number() - .valid(...SUPPORTED_CHAINS) - .required(), - method: Joi.string() - .valid(...Object.keys(rpcMethodSchemas)) - .required(), - parameters: makeParameters(), - returnValueTest: returnValueTestSchema.required(), - }); + public readonly schema = Joi.object(rpcConditionSchema); } diff --git a/src/conditions/base/time.ts b/src/conditions/base/time.ts index 78fa28795..9bf3a7cf5 100644 --- a/src/conditions/base/time.ts +++ b/src/conditions/base/time.ts @@ -1,24 +1,18 @@ import Joi from 'joi'; -import { SUPPORTED_CHAINS } from '../const'; +import { RpcCondition, rpcConditionSchema } from './rpc'; -import { Condition } from './condition'; -import { returnValueTestSchema } from './schema'; +const BLOCKTIME_METHOD = 'blocktime'; -export class TimeCondition extends Condition { - // TODO: This is the only condition that uses defaults, and also the only condition that uses `method` in order - // to determine the schema. I.e. the only method that used `METHOD = 'blocktime'` in `nucypher/nucypher`. - // TODO: Consider introducing a different field for this, e.g. `conditionType` or `type`. Use this field in a - // condition factory. +const timeConditionSchema = { + ...rpcConditionSchema, + method: Joi.string().valid(BLOCKTIME_METHOD).required(), +}; + +export class TimeCondition extends RpcCondition { public readonly defaults: Record = { - method: 'blocktime', + method: BLOCKTIME_METHOD, }; - public readonly schema = Joi.object({ - method: Joi.string().valid(this.defaults.method).required(), - returnValueTest: returnValueTestSchema.required(), - chain: Joi.number() - .valid(...SUPPORTED_CHAINS) - .required(), - }); + public readonly schema = Joi.object(timeConditionSchema); } From e23b0cbad59acd22ef0060263e312d907a7db866 Mon Sep 17 00:00:00 2001 From: piotr-roslaniec <39299780+piotr-roslaniec@users.noreply.github.com> Date: Mon, 12 Jun 2023 21:55:56 +0200 Subject: [PATCH 58/98] Update comment Co-authored-by: Derek Pierre --- src/dkg.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dkg.ts b/src/dkg.ts index a2b805fca..c0bbd49e7 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -104,7 +104,7 @@ export class DkgClient { } // TODO: Without Validator public key in Coordinator, we cannot verify the - // transcript. We need to add it to the Coordinator. + // transcript. We need to add it to the Coordinator (nucypher-contracts #77). // public async verifyRitual(ritualId: number): Promise { // const ritual = await DkgCoordinatorAgent.getRitual(this.provider, ritualId); // const participants = await DkgCoordinatorAgent.getParticipants( From 863983d0af62ebdcd8d5244ef908ca30633a2fb4 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 12 Jun 2023 10:14:07 +0200 Subject: [PATCH 59/98] chore(ci): monitor bundle size --- .github/workflows/main.yml | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1ddfad165..9e2415b49 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,5 @@ name: CI -on: [push, pull_request] +on: [ push, pull_request ] jobs: build_project: name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }} @@ -7,8 +7,8 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node: ['16.x'] - os: [ubuntu-latest] + node: [ '16.x' ] + os: [ ubuntu-latest ] steps: - name: Checkout repo @@ -22,28 +22,37 @@ jobs: - name: Install deps and build (with cache) uses: bahmutov/npm-install@v1 - - name: Test - run: yarn test + - name: Check + run: yarn run package-check - name: Build run: yarn build - - name: Check - run: yarn run package-check + - name: Test + run: yarn test - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: fail_ci_if_error: true + - name: Check bundle size + uses: sarthak-saxena/JSBundleSize@3.0.0 + with: + # Skipping some commands with 'echo ok' + bootstrap: 'echo ok' + build_command: 'echo ok' + dist_path: 'build' + token: ${{ secrets.GITHUB_TOKEN }} + build_examples: name: Build project examples runs-on: ${{ matrix.os }} strategy: matrix: - node: ['16.x'] - os: [ubuntu-latest] + node: [ '16.x' ] + os: [ ubuntu-latest ] example: [ 'nodejs', From 77278d6cccacca87be176a0b807426a5a5b5c11f Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 12 Jun 2023 16:05:48 +0200 Subject: [PATCH 60/98] feat!: rename EvmCondition to ContractCondition --- src/conditions/base/{evm.ts => contract.ts} | 6 +-- src/conditions/base/index.ts | 2 +- src/conditions/predefined/erc721.ts | 6 +-- test/docs/cbd.test.ts | 4 +- test/unit/conditions/base/condition.test.ts | 20 ++++---- test/unit/conditions/base/evm.test.ts | 56 ++++++++++----------- test/unit/conditions/context.test.ts | 28 +++++------ test/unit/testVariables.ts | 2 +- 8 files changed, 62 insertions(+), 62 deletions(-) rename src/conditions/base/{evm.ts => contract.ts} (77%) diff --git a/src/conditions/base/evm.ts b/src/conditions/base/contract.ts similarity index 77% rename from src/conditions/base/evm.ts rename to src/conditions/base/contract.ts index 86c269df7..8485227bc 100644 --- a/src/conditions/base/evm.ts +++ b/src/conditions/base/contract.ts @@ -6,7 +6,7 @@ import { RpcCondition, rpcConditionSchema } from './rpc'; export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; -const evmMethodSchemas: Record = { +const contractMethodSchemas: Record = { ...rpcConditionSchema, contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), standardContractType: Joi.string() @@ -17,8 +17,8 @@ const evmMethodSchemas: Record = { parameters: Joi.array().required(), }; -export class EvmCondition extends RpcCondition { - public readonly schema = Joi.object(evmMethodSchemas) +export class ContractCondition extends RpcCondition { + public readonly schema = Joi.object(contractMethodSchemas) // At most one of these keys needs to be present .xor('standardContractType', 'functionAbi'); } diff --git a/src/conditions/base/index.ts b/src/conditions/base/index.ts index dc2fcf522..805717e01 100644 --- a/src/conditions/base/index.ts +++ b/src/conditions/base/index.ts @@ -1,4 +1,4 @@ export { Condition } from './condition'; -export { EvmCondition } from './evm'; +export { ContractCondition } from './contract'; export { RpcCondition } from './rpc'; export { TimeCondition } from './time'; diff --git a/src/conditions/predefined/erc721.ts b/src/conditions/predefined/erc721.ts index ef9f880f3..beed9a5ce 100644 --- a/src/conditions/predefined/erc721.ts +++ b/src/conditions/predefined/erc721.ts @@ -1,7 +1,7 @@ -import { EvmCondition } from '../base'; +import { ContractCondition } from '../base'; import { USER_ADDRESS_PARAM } from '../const'; -export class ERC721Ownership extends EvmCondition { +export class ERC721Ownership extends ContractCondition { public readonly defaults = { method: 'ownerOf', parameters: [], @@ -14,7 +14,7 @@ export class ERC721Ownership extends EvmCondition { }; } -export class ERC721Balance extends EvmCondition { +export class ERC721Balance extends ContractCondition { public readonly defaults = { method: 'balanceOf', parameters: [USER_ADDRESS_PARAM], diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 86be24fe6..5d4a4702a 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -18,7 +18,7 @@ import { const { predefined: { ERC721Ownership }, - base: { EvmCondition }, + base: { ContractCondition }, ConditionSet, } = conditions; @@ -100,7 +100,7 @@ describe('Get Started (CBD PoC)', () => { value: 3, }, }; - const NFTBalance = new EvmCondition(NFTBalanceConfig); + const NFTBalance = new ContractCondition(NFTBalanceConfig); const encrypter = newDeployed.encrypter; diff --git a/test/unit/conditions/base/condition.test.ts b/test/unit/conditions/base/condition.test.ts index a38798744..dc9b310ab 100644 --- a/test/unit/conditions/base/condition.test.ts +++ b/test/unit/conditions/base/condition.test.ts @@ -1,4 +1,4 @@ -import { EvmCondition } from '../../../../src/conditions/base'; +import { ContractCondition } from '../../../../src/conditions/base'; import { ERC721Balance, ERC721Ownership, @@ -7,7 +7,7 @@ import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR, TEST_CONTRACT_ADDR_2, - testEvmConditionObj, + testContractConditionObj, } from '../../testVariables'; describe('validation', () => { @@ -47,18 +47,18 @@ describe('validation', () => { describe('serialization', () => { it('serializes to a plain object', () => { - const evm = new EvmCondition(testEvmConditionObj); - expect(evm.toObj()).toEqual({ - ...testEvmConditionObj, - _class: 'EvmCondition', + const contract = new ContractCondition(testContractConditionObj); + expect(contract.toObj()).toEqual({ + ...testContractConditionObj, + _class: 'ContractCondition', }); }); it('serializes predefined conditions', () => { - const evm = new ERC721Ownership(testEvmConditionObj); - expect(evm.toObj()).toEqual({ - ...evm.defaults, - ...testEvmConditionObj, + const contract = new ERC721Ownership(testContractConditionObj); + expect(contract.toObj()).toEqual({ + ...contract.defaults, + ...testContractConditionObj, _class: 'ERC721Ownership', }); }); diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts index 5a35efa93..404d2d90b 100644 --- a/test/unit/conditions/base/evm.test.ts +++ b/test/unit/conditions/base/evm.test.ts @@ -1,27 +1,27 @@ -import { EvmCondition } from '../../../../src/conditions/base'; -import { testEvmConditionObj } from '../../testVariables'; +import { ContractCondition } from '../../../../src/conditions/base'; +import { testContractConditionObj } from '../../testVariables'; describe('validation', () => { it('accepts on a valid schema', () => { - const evm = new EvmCondition(testEvmConditionObj); - expect(evm.toObj()).toEqual({ - ...testEvmConditionObj, - _class: 'EvmCondition', + const contract = new ContractCondition(testContractConditionObj); + expect(contract.toObj()).toEqual({ + ...testContractConditionObj, + _class: 'ContractCondition', }); }); it('rejects an invalid schema', () => { - const badEvmCondition = { - ...testEvmConditionObj, + const badContractCondition = { + ...testContractConditionObj, // Intentionally removing `contractAddress` contractAddress: undefined, }; - const badEvm = new EvmCondition(badEvmCondition); + const badEvm = new ContractCondition(badContractCondition); expect(() => badEvm.toObj()).toThrow( 'Invalid condition: "contractAddress" is required' ); - const { error } = badEvm.validate(badEvmCondition); + const { error } = badEvm.validate(badContractCondition); expect(error?.message).toEqual('"contractAddress" is required'); }); }); @@ -32,50 +32,50 @@ describe('accepts either standardContractType or functionAbi but not both or non it('accepts standardContractType', () => { const conditionObj = { - ...testEvmConditionObj, + ...testContractConditionObj, standardContractType, functionAbi: undefined, }; - const evmCondition = new EvmCondition(conditionObj); - expect(evmCondition.toObj()).toEqual({ + const contractCondition = new ContractCondition(conditionObj); + expect(contractCondition.toObj()).toEqual({ ...conditionObj, - _class: 'EvmCondition', + _class: 'ContractCondition', }); }); it('accepts functionAbi', () => { const conditionObj = { - ...testEvmConditionObj, + ...testContractConditionObj, functionAbi, standardContractType: undefined, }; - const evmCondition = new EvmCondition(conditionObj); - expect(evmCondition.toObj()).toEqual({ + const contractCondition = new ContractCondition(conditionObj); + expect(contractCondition.toObj()).toEqual({ ...conditionObj, - _class: 'EvmCondition', + _class: 'ContractCondition', }); }); it('rejects both', () => { const conditionObj = { - ...testEvmConditionObj, + ...testContractConditionObj, standardContractType, functionAbi, }; - const evmCondition = new EvmCondition(conditionObj); - expect(() => evmCondition.toObj()).toThrow( + const contractCondition = new ContractCondition(conditionObj); + expect(() => contractCondition.toObj()).toThrow( '"value" contains a conflict between exclusive peers [standardContractType, functionAbi]' ); }); it('rejects none', () => { const conditionObj = { - ...testEvmConditionObj, + ...testContractConditionObj, standardContractType: undefined, functionAbi: undefined, }; - const evmCondition = new EvmCondition(conditionObj); - expect(() => evmCondition.toObj()).toThrow( + const contractCondition = new ContractCondition(conditionObj); + expect(() => contractCondition.toObj()).toThrow( '"value" must contain at least one of [standardContractType, functionAbi]' ); }); @@ -108,8 +108,8 @@ describe('accepts either standardContractType or functionAbi but not both or non // }, // ], // }; -// const evmConditionObj = { -// ...testEvmConditionObj, +// const contractConditionObj = { +// ...testContractConditionObj, // functionAbi: fakeFunctionAbi, // method: 'myFunction', // parameters: [USER_ADDRESS_PARAM, ':customParam'], @@ -119,9 +119,9 @@ describe('accepts either standardContractType or functionAbi but not both or non // value: USER_ADDRESS_PARAM, // }, // }; -// const evmCondition = new EvmCondition(evmConditionObj); +// const contractCondition = new ContractCondition(contractConditionObj); // const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); -// const conditionSet = new ConditionSet([evmCondition]); +// const conditionSet = new ConditionSet([contractCondition]); // const conditionContext = new ConditionContext( // conditionSet.toWASMConditions(), // web3Provider diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts index 9a1746919..79d048157 100644 --- a/test/unit/conditions/context.test.ts +++ b/test/unit/conditions/context.test.ts @@ -2,12 +2,12 @@ import { SecretKey } from '@nucypher/nucypher-core'; import { CustomContextParam } from '../../../src'; import { ConditionSet } from '../../../src/conditions'; -import { EvmCondition, RpcCondition } from '../../../src/conditions/base'; +import { ContractCondition, RpcCondition } from '../../../src/conditions/base'; import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; import { RESERVED_CONTEXT_PARAMS } from '../../../src/conditions/context/context'; import { fakeWeb3Provider } from '../../utils'; import { - testEvmConditionObj, + testContractConditionObj, testFunctionAbi, testReturnValueTest, testRpcConditionObj, @@ -40,15 +40,15 @@ describe('context parameters', () => { const customParams: Record = {}; customParams[customParamKey] = 1234; - const evmConditionObj = { - ...testEvmConditionObj, + const contractConditionObj = { + ...testContractConditionObj, returnValueTest: { ...testReturnValueTest, value: customParamKey, }, }; - const evmCondition = new EvmCondition(evmConditionObj); - const conditionSet = new ConditionSet([evmCondition]); + const contractCondition = new ContractCondition(contractConditionObj); + const conditionSet = new ConditionSet([contractCondition]); const conditionContext = conditionSet.buildContext(web3Provider); describe('return value test', () => { @@ -78,8 +78,8 @@ describe('context parameters', () => { }); describe('custom method parameters', () => { - const evmConditionObj = { - ...testEvmConditionObj, + const contractConditionObj = { + ...testContractConditionObj, standardContractType: undefined, // We're going to use a custom function ABI functionAbi: testFunctionAbi, parameters: [USER_ADDRESS_PARAM, customParamKey], // We're going to use a custom parameter @@ -89,12 +89,12 @@ describe('context parameters', () => { }; it('rejects on a missing parameter ', async () => { - const customEvmCondition = new EvmCondition({ - ...evmConditionObj, + const customContractCondition = new ContractCondition({ + ...contractConditionObj, parameters: [USER_ADDRESS_PARAM, customParamKey], }); const conditionContext = new ConditionSet([ - customEvmCondition, + customContractCondition, ]).buildContext(web3Provider); await expect(async () => conditionContext.toObj()).rejects.toThrow( @@ -103,12 +103,12 @@ describe('context parameters', () => { }); it('accepts on a hard-coded parameter', async () => { - const customEvmCondition = new EvmCondition({ - ...evmConditionObj, + const customContractCondition = new ContractCondition({ + ...contractConditionObj, parameters: [USER_ADDRESS_PARAM, 100], }); const conditionContext = new ConditionSet([ - customEvmCondition, + customContractCondition, ]).buildContext(web3Provider); const asObj = await conditionContext.toObj(); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index 99c219be0..6ece2f977 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -26,7 +26,7 @@ export const testRpcConditionObj = { returnValueTest: testReturnValueTest, }; -export const testEvmConditionObj = { +export const testContractConditionObj = { contractAddress: '0x0000000000000000000000000000000000000000', chain: 5, standardContractType: 'ERC20', From 662b5f538556bb85084ad03be6d9d410260f6435 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 12 Jun 2023 16:31:29 +0200 Subject: [PATCH 61/98] timelock doesn't take parameters --- src/conditions/base/time.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/conditions/base/time.ts b/src/conditions/base/time.ts index 9bf3a7cf5..ad4093d48 100644 --- a/src/conditions/base/time.ts +++ b/src/conditions/base/time.ts @@ -7,6 +7,7 @@ const BLOCKTIME_METHOD = 'blocktime'; const timeConditionSchema = { ...rpcConditionSchema, method: Joi.string().valid(BLOCKTIME_METHOD).required(), + parameters: undefined, // TimeCondition does not accept parameters }; export class TimeCondition extends RpcCondition { From 96a92d58294cb5cf327930df47f7bd2bb31abde3 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 13 Jun 2023 10:02:30 +0200 Subject: [PATCH 62/98] fix leaking rpc condition parmeters to time condition schema --- src/conditions/base/time.ts | 6 ++++-- src/utils.ts | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/conditions/base/time.ts b/src/conditions/base/time.ts index ad4093d48..8b136e4a7 100644 --- a/src/conditions/base/time.ts +++ b/src/conditions/base/time.ts @@ -1,13 +1,15 @@ import Joi from 'joi'; +import { omit } from '../../utils'; + import { RpcCondition, rpcConditionSchema } from './rpc'; const BLOCKTIME_METHOD = 'blocktime'; const timeConditionSchema = { - ...rpcConditionSchema, + // TimeCondition is an RpcCondition with the method set to 'blocktime' and no parameters + ...omit(rpcConditionSchema, ['parameters']), method: Joi.string().valid(BLOCKTIME_METHOD).required(), - parameters: undefined, // TimeCondition does not accept parameters }; export class TimeCondition extends RpcCondition { diff --git a/src/utils.ts b/src/utils.ts index 8b80d5463..722e98488 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -67,3 +67,9 @@ export const bytesEquals = (first: Uint8Array, second: Uint8Array): boolean => export const objectEquals = (a: unknown, b: unknown, strict = true): boolean => deepEqual(a, b, { strict }); + +export const omit = (obj: Record, keys: string[]) => { + const copy = { ...obj }; + keys.forEach((key) => delete copy[key]); + return copy; +}; From 81d23efb40f343cb84f22a99264b6836d2e482ac Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 12 Jun 2023 10:45:02 +0200 Subject: [PATCH 63/98] feat: add conveniance method making encrypters --- src/sdk/strategy/cbd-strategy.ts | 6 +++++- src/sdk/strategy/pre-strategy.ts | 20 ++++++++++++-------- test/docs/cbd.test.ts | 4 +--- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index fa4f5f072..57257d693 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -93,7 +93,7 @@ export class CbdStrategy { } export class DeployedCbdStrategy { - constructor( + public constructor( public readonly cohort: Cohort, public readonly dkgRitual: DkgRitual, public readonly encrypter: Enrico, @@ -101,6 +101,10 @@ export class DeployedCbdStrategy { public readonly conditionSet?: ConditionSet ) {} + public makeEncrypter(conditionSet: ConditionSet): Enrico { + return new Enrico(this.dkgRitual.dkgPublicKey, undefined, conditionSet); + } + public static fromJSON(json: string) { const config = fromJSON(json); return DeployedCbdStrategy.fromObj(config); diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index 88c79b91a..fbec1fbca 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -180,16 +180,20 @@ export class PreStrategy { } export class DeployedPreStrategy { - constructor( - public label: string, - public cohort: Cohort, - public policy: EnactedPolicy, - public encrypter: Enrico, - public decrypter: PreTDecDecrypter, - private bobSecretKey: SecretKey, - public conditionSet?: ConditionSet + public constructor( + public readonly label: string, + public readonly cohort: Cohort, + public readonly policy: EnactedPolicy, + public readonly encrypter: Enrico, + public readonly decrypter: PreTDecDecrypter, + private readonly bobSecretKey: SecretKey, + public readonly conditionSet?: ConditionSet ) {} + public makeEncrypter(conditionSet: ConditionSet): Enrico { + return new Enrico(this.policy.policyKey, undefined, conditionSet); + } + public static fromJSON(json: string) { const config = JSON.parse(json, base64ToU8Receiver); return DeployedPreStrategy.fromObj(config); diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 5d4a4702a..d8589ec76 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -102,10 +102,8 @@ describe('Get Started (CBD PoC)', () => { }; const NFTBalance = new ContractCondition(NFTBalanceConfig); - const encrypter = newDeployed.encrypter; - const plaintext = 'this is a secret'; - const encryptedMessageKit = encrypter.encryptMessagePre( + const encryptedMessageKit = newDeployed.encrypter.encryptMessagePre( plaintext, new ConditionSet([NFTBalance]) ); From 5912dcadb2440fa56159b1508b948db4200401e2 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 12 Jun 2023 17:15:16 +0200 Subject: [PATCH 64/98] avoid reconstructing encrypter in strategy constructor --- src/sdk/strategy/cbd-strategy.ts | 65 +++++--------------------------- src/sdk/strategy/pre-strategy.ts | 13 ------- test/docs/cbd.test.ts | 9 ++--- test/unit/cbd-strategy.test.ts | 7 ++-- test/unit/pre-strategy.test.ts | 5 ++- 5 files changed, 20 insertions(+), 79 deletions(-) diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 57257d693..f302ae15e 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -15,7 +15,6 @@ export type CbdStrategyJSON = { export type DeployedStrategyJSON = { dkgRitual: DkgRitualJSON; cohortConfig: CohortJSON; - conditionSet?: ConditionSetJSON | undefined; }; export class CbdStrategy { @@ -40,25 +39,7 @@ export class CbdStrategy { provider, dkgRitualParams ); - - const encrypter = new Enrico( - dkgRitual.dkgPublicKey, - undefined, - this.conditionSet - ); - - const decrypter = new CbdTDecDecrypter( - this.cohort.configuration.porterUri, - this.cohort.configuration.threshold - ); - - return new DeployedCbdStrategy( - this.cohort, - dkgRitual, - encrypter, - decrypter, - this.conditionSet - ); + return new DeployedCbdStrategy(this.cohort, dkgRitual); } public static fromJSON(json: string) { @@ -93,13 +74,14 @@ export class CbdStrategy { } export class DeployedCbdStrategy { + public readonly decrypter: CbdTDecDecrypter; + public constructor( public readonly cohort: Cohort, - public readonly dkgRitual: DkgRitual, - public readonly encrypter: Enrico, - public readonly decrypter: CbdTDecDecrypter, - public readonly conditionSet?: ConditionSet - ) {} + public readonly dkgRitual: DkgRitual + ) { + this.decrypter = new CbdTDecDecrypter(this.cohort.configuration.porterUri); + } public makeEncrypter(conditionSet: ConditionSet): Enrico { return new Enrico(this.dkgRitual.dkgPublicKey, undefined, conditionSet); @@ -114,50 +96,23 @@ export class DeployedCbdStrategy { return toJSON(this.toObj()); } - private static fromObj({ - dkgRitual, - cohortConfig, - conditionSet, - }: DeployedStrategyJSON) { + private static fromObj({ dkgRitual, cohortConfig }: DeployedStrategyJSON) { const ritual = DkgRitual.fromObj(dkgRitual); const cohort = Cohort.fromObj(cohortConfig); - const maybeConditionSet = conditionSet - ? ConditionSet.fromObj(conditionSet) - : undefined; - const encrypter = new Enrico( - ritual.dkgPublicKey, - undefined, - maybeConditionSet - ); - const decrypter = new CbdTDecDecrypter( - cohort.configuration.porterUri, - cohort.configuration.threshold - ); - return new DeployedCbdStrategy( - cohort, - ritual, - encrypter, - decrypter, - maybeConditionSet - ); + return new DeployedCbdStrategy(cohort, ritual); } public toObj(): DeployedStrategyJSON { return { dkgRitual: this.dkgRitual.toObj(), cohortConfig: this.cohort.toObj(), - conditionSet: this.conditionSet?.toObj(), }; } public equals(other: DeployedCbdStrategy) { - const conditionSetEquals = - this.conditionSet && other.conditionSet - ? this.conditionSet.equals(other.conditionSet) - : false; return ( + this.decrypter.equals(other.decrypter) && this.cohort.equals(other.cohort) && - conditionSetEquals && objectEquals(this.dkgRitual.toObj(), other.dkgRitual.toObj()) ); } diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index fbec1fbca..0997f3b93 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -92,11 +92,6 @@ export class PreStrategy { endDate: this.endDate, }; const policy = await alice.grant(policyParams, this.cohort.ursulaAddresses); - const encrypter = new Enrico( - policy.policyKey, - undefined, - this.conditionSet - ); const decrypter = new PreTDecDecrypter( this.cohort.configuration.porterUri, @@ -109,7 +104,6 @@ export class PreStrategy { label, this.cohort, policy, - encrypter, decrypter, this.bobSecretKey, this.conditionSet @@ -184,7 +178,6 @@ export class DeployedPreStrategy { public readonly label: string, public readonly cohort: Cohort, public readonly policy: EnactedPolicy, - public readonly encrypter: Enrico, public readonly decrypter: PreTDecDecrypter, private readonly bobSecretKey: SecretKey, public readonly conditionSet?: ConditionSet @@ -235,11 +228,6 @@ export class DeployedPreStrategy { const conditionSetOrUndefined = conditionSet ? ConditionSet.fromObj(conditionSet) : undefined; - const encrypter = new Enrico( - newPolicy.policyKey, - undefined, - conditionSetOrUndefined - ); const decrypter = new PreTDecDecrypter( cohort.configuration.porterUri, @@ -252,7 +240,6 @@ export class DeployedPreStrategy { label, cohort, newPolicy, - encrypter, decrypter, bobSecretKey, conditionSetOrUndefined diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index d8589ec76..9e79aef41 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -101,12 +101,11 @@ describe('Get Started (CBD PoC)', () => { }, }; const NFTBalance = new ContractCondition(NFTBalanceConfig); - + const newConditions = new ConditionSet([NFTBalance]); const plaintext = 'this is a secret'; - const encryptedMessageKit = newDeployed.encrypter.encryptMessagePre( - plaintext, - new ConditionSet([NFTBalance]) - ); + const encryptedMessageKit = newDeployed + .makeEncrypter(newConditions) + .encryptMessagePre(plaintext); // Mocking - Not a part of any code example const retrieveCFragsSpy = mockRetrieveAndDecrypt( diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 9774dcd43..6ca0664d2 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -104,10 +104,9 @@ describe('CbdDeployedStrategy', () => { const { mockedDkg, deployedStrategy } = await makeDeployedCbdStrategy(); const message = 'this is a secret'; - const { ciphertext, aad } = deployedStrategy.encrypter.encryptMessageCbd( - message, - conditionSet - ); + const { ciphertext, aad } = deployedStrategy + .makeEncrypter(conditionSet) + .encryptMessageCbd(message); // Setup mocks for `retrieveAndDecrypt` const { decryptionShares } = fakeTDecFlow({ diff --git a/test/unit/pre-strategy.test.ts b/test/unit/pre-strategy.test.ts index 8f5a401ef..18dde2a74 100644 --- a/test/unit/pre-strategy.test.ts +++ b/test/unit/pre-strategy.test.ts @@ -118,8 +118,9 @@ describe('PreDeployedStrategy', () => { await makeDeployedPreStrategy(); const plaintext = 'this is a secret'; - const encryptedMessageKit = - deployedStrategy.encrypter.encryptMessagePre(plaintext); + const encryptedMessageKit = deployedStrategy + .makeEncrypter(conditionSet) + .encryptMessagePre(plaintext); // Setup mocks for `retrieveAndDecrypt` const getUrsulasSpy = mockGetUrsulas(mockedUrsulas); From 0e23866c6d39d7bb7a9cb30f12abd5d72408b54d Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 13 Jun 2023 12:13:24 +0200 Subject: [PATCH 65/98] refactor: hide strategy fields and constructors --- src/characters/cbd-recipient.ts | 45 ++++++-- src/characters/pre-recipient.ts | 38 ++++--- src/policies/policy.ts | 10 +- src/sdk/strategy/cbd-strategy.ts | 74 ++++++------ src/sdk/strategy/pre-strategy.ts | 162 +++++++++------------------ test/acceptance/alice-grants.test.ts | 9 +- test/acceptance/delay-enact.test.ts | 10 +- test/docs/cbd.test.ts | 3 +- test/unit/cbd-strategy.test.ts | 3 +- test/unit/pre-strategy.test.ts | 8 +- 10 files changed, 166 insertions(+), 196 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index d3dda39a3..56dcb149e 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -4,6 +4,8 @@ import { DecryptionSharePrecomputed, DecryptionShareSimple, decryptWithSharedSecret, + DkgPublicParameters, + SharedSecret, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, SessionSharedSecret, @@ -26,21 +28,34 @@ import { Porter } from './porter'; export type CbdTDecDecrypterJSON = { porterUri: string; + ritualId: number; + dkgPublicParams: Uint8Array; threshold: number; }; export class CbdTDecDecrypter { - private readonly porter: Porter; - - constructor(porterUri: string, private readonly threshold: number) { - this.porter = new Porter(porterUri); + // private readonly verifyingKey: Keyring; + + private constructor( + private readonly porter: Porter, + private readonly ritualId: number, + private readonly dkgPublicParams: DkgPublicParameters, + private readonly threshold: number + ) {} + + public static create(porterUri: string, dkgRitual: DkgRitual, threshold: number) { + return new CbdTDecDecrypter( + new Porter(porterUri), + dkgRitual.id, + dkgRitual.dkgPublicParams, + threshold, + ); } // Retrieve and decrypt ciphertext using provider and condition set public async retrieveAndDecrypt( provider: ethers.providers.Web3Provider, conditionSet: ConditionSet, - dkgRitual: DkgRitual, variant: number, ciphertext: Ciphertext, aad: Uint8Array @@ -48,7 +63,7 @@ export class CbdTDecDecrypter { const decryptionShares = await this.retrieve( provider, conditionSet, - dkgRitual.id, + this.ritualId, variant, ciphertext ); @@ -61,7 +76,7 @@ export class CbdTDecDecrypter { ciphertext, aad, sharedSecret, - dkgRitual.dkgPublicParams + this.dkgPublicParams ); return [plaintext]; } @@ -192,6 +207,8 @@ export class CbdTDecDecrypter { public toObj(): CbdTDecDecrypterJSON { return { porterUri: this.porter.porterUrl.toString(), + ritualId: this.ritualId, + dkgPublicParams: this.dkgPublicParams.toBytes(), threshold: this.threshold, }; } @@ -200,8 +217,18 @@ export class CbdTDecDecrypter { return toJSON(this.toObj()); } - public static fromObj({ porterUri, threshold }: CbdTDecDecrypterJSON) { - return new CbdTDecDecrypter(porterUri, threshold); + public static fromObj({ + porterUri, + ritualId, + dkgPublicParams, + threshold, + }: CbdTDecDecrypterJSON) { + return new CbdTDecDecrypter( + new Porter(porterUri), + ritualId, + DkgPublicParameters.fromBytes(dkgPublicParams), + threshold + ); } public static fromJSON(json: string) { diff --git a/src/characters/pre-recipient.ts b/src/characters/pre-recipient.ts index a566b8fb3..a1b624169 100644 --- a/src/characters/pre-recipient.ts +++ b/src/characters/pre-recipient.ts @@ -16,7 +16,7 @@ import { base64ToU8Receiver, bytesEquals, toJSON, zip } from '../utils'; import { Porter } from './porter'; -type PreTDecDecrypterJSON = { +export type PreTDecDecrypterJSON = { porterUri: string; policyEncryptingKeyBytes: Uint8Array; encryptedTreasureMapBytes: Uint8Array; @@ -25,22 +25,30 @@ type PreTDecDecrypterJSON = { }; export class PreTDecDecrypter { - private readonly porter: Porter; - private readonly keyring: Keyring; - // private readonly verifyingKey: Keyring; constructor( - porterUri: string, + private readonly porter: Porter, + private readonly keyring: Keyring, private readonly policyEncryptingKey: PublicKey, - readonly encryptedTreasureMap: EncryptedTreasureMap, private readonly publisherVerifyingKey: PublicKey, - secretKey: SecretKey - // verifyingKey: SecretKey - ) { - this.porter = new Porter(porterUri); - this.keyring = new Keyring(secretKey); - // this.verifyingKey = new Keyring(verifyingKey); + private readonly encryptedTreasureMap: EncryptedTreasureMap + ) {} + + public static create( + porterUri: string, + secretKey: SecretKey, + policyEncryptingKey: PublicKey, + publisherVerifyingKey: PublicKey, + encryptedTreasureMap: EncryptedTreasureMap + ): PreTDecDecrypter { + return new PreTDecDecrypter( + new Porter(porterUri), + new Keyring(secretKey), + policyEncryptingKey, + publisherVerifyingKey, + encryptedTreasureMap + ); } public get decryptingKey(): PublicKey { @@ -161,11 +169,11 @@ export class PreTDecDecrypter { bobSecretKeyBytes, }: PreTDecDecrypterJSON) { return new PreTDecDecrypter( - porterUri, + new Porter(porterUri), + new Keyring(SecretKey.fromBEBytes(bobSecretKeyBytes)), PublicKey.fromCompressedBytes(policyEncryptingKeyBytes), - EncryptedTreasureMap.fromBytes(encryptedTreasureMapBytes), PublicKey.fromCompressedBytes(publisherVerifyingKeyBytes), - SecretKey.fromBEBytes(bobSecretKeyBytes) + EncryptedTreasureMap.fromBytes(encryptedTreasureMapBytes) ); } diff --git a/src/policies/policy.ts b/src/policies/policy.ts index 0e01a6926..1ccca6a90 100644 --- a/src/policies/policy.ts +++ b/src/policies/policy.ts @@ -11,7 +11,6 @@ import { PreSubscriptionManagerAgent } from '../agents/subscription-manager'; import { Alice } from '../characters/alice'; import { RemoteBob } from '../characters/bob'; import { Ursula } from '../characters/porter'; -// import { RevocationKit } from '../kits/revocation'; import { toBytes, toEpoch, zip } from '../utils'; import { toCanonicalAddress } from '../web3'; @@ -20,8 +19,7 @@ export type EnactedPolicy = { readonly label: string; readonly policyKey: PublicKey; readonly encryptedTreasureMap: EncryptedTreasureMap; - // readonly revocationKit: RevocationKit; - readonly aliceVerifyingKey: Uint8Array; + readonly aliceVerifyingKey: PublicKey; readonly size: number; readonly startTimestamp: Date; readonly endTimestamp: Date; @@ -45,8 +43,7 @@ export class PreEnactedPolicy implements IPreEnactedPolicy { public readonly label: string, public readonly policyKey: PublicKey, public readonly encryptedTreasureMap: EncryptedTreasureMap, - // public readonly revocationKit: RevocationKit, - public readonly aliceVerifyingKey: Uint8Array, + public readonly aliceVerifyingKey: PublicKey, public readonly size: number, public readonly startTimestamp: Date, public readonly endTimestamp: Date @@ -130,8 +127,7 @@ export class BlockchainPolicy { this.label, this.delegatingKey, encryptedTreasureMap, - // revocationKit, - this.publisher.verifyingKey.toCompressedBytes(), + this.publisher.verifyingKey, this.shares, this.startDate, this.endDate diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index f302ae15e..4b1ae65b9 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -1,10 +1,15 @@ +import { DkgPublicKey } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { CbdTDecDecrypter } from '../../characters/cbd-recipient'; +import { bytesEqual } from '../../../test/utils'; +import { + CbdTDecDecrypter, + CbdTDecDecrypterJSON, +} from '../../characters/cbd-recipient'; import { Enrico } from '../../characters/enrico'; import { ConditionSet, ConditionSetJSON } from '../../conditions'; -import { DkgClient, DkgRitual, DkgRitualJSON } from '../../dkg'; -import { fromJSON, objectEquals, toJSON } from '../../utils'; +import { DkgClient, DkgRitual } from '../../dkg'; +import { fromJSON, toJSON } from '../../utils'; import { Cohort, CohortJSON } from '../cohort'; export type CbdStrategyJSON = { @@ -13,18 +18,15 @@ export type CbdStrategyJSON = { }; export type DeployedStrategyJSON = { - dkgRitual: DkgRitualJSON; - cohortConfig: CohortJSON; + decrypter: CbdTDecDecrypterJSON; + dkgPublicKey: Uint8Array; }; export class CbdStrategy { - private constructor( - public readonly cohort: Cohort, - private readonly conditionSet?: ConditionSet - ) {} + private constructor(public readonly cohort: Cohort) {} - public static create(cohort: Cohort, conditionSet?: ConditionSet) { - return new CbdStrategy(cohort, conditionSet); + public static create(cohort: Cohort) { + return new CbdStrategy(cohort); } public async deploy( @@ -39,7 +41,7 @@ export class CbdStrategy { provider, dkgRitualParams ); - return new DeployedCbdStrategy(this.cohort, dkgRitual); + return DeployedCbdStrategy.create(this.cohort, dkgRitual); } public static fromJSON(json: string) { @@ -50,41 +52,37 @@ export class CbdStrategy { return toJSON(this.toObj()); } - public static fromObj({ cohort, conditionSet }: CbdStrategyJSON) { - const maybeConditionSet = conditionSet - ? ConditionSet.fromObj(conditionSet) - : undefined; - return new CbdStrategy(Cohort.fromObj(cohort), maybeConditionSet); + public static fromObj({ cohort }: CbdStrategyJSON) { + return new CbdStrategy(Cohort.fromObj(cohort)); } public toObj(): CbdStrategyJSON { return { cohort: this.cohort.toObj(), - conditionSet: this.conditionSet?.toObj(), }; } public equals(other: CbdStrategy) { - const conditionSetEquals = - this.conditionSet && other.conditionSet - ? this.conditionSet.equals(other.conditionSet) - : this.conditionSet === other.conditionSet; - return this.cohort.equals(other.cohort) && conditionSetEquals; + return this.cohort.equals(other.cohort); } } export class DeployedCbdStrategy { - public readonly decrypter: CbdTDecDecrypter; + private constructor( + public readonly decrypter: CbdTDecDecrypter, + public readonly dkgPublicKey: DkgPublicKey + ) {} - public constructor( - public readonly cohort: Cohort, - public readonly dkgRitual: DkgRitual - ) { - this.decrypter = new CbdTDecDecrypter(this.cohort.configuration.porterUri); + public static create(cohort: Cohort, dkgRitual: DkgRitual) { + const decrypter = CbdTDecDecrypter.create( + cohort.configuration.porterUri, + dkgRitual + ); + return new DeployedCbdStrategy(decrypter, dkgRitual.dkgPublicKey); } public makeEncrypter(conditionSet: ConditionSet): Enrico { - return new Enrico(this.dkgRitual.dkgPublicKey, undefined, conditionSet); + return new Enrico(this.dkgPublicKey, undefined, conditionSet); } public static fromJSON(json: string) { @@ -96,24 +94,24 @@ export class DeployedCbdStrategy { return toJSON(this.toObj()); } - private static fromObj({ dkgRitual, cohortConfig }: DeployedStrategyJSON) { - const ritual = DkgRitual.fromObj(dkgRitual); - const cohort = Cohort.fromObj(cohortConfig); - return new DeployedCbdStrategy(cohort, ritual); + private static fromObj({ decrypter, dkgPublicKey }: DeployedStrategyJSON) { + return new DeployedCbdStrategy( + CbdTDecDecrypter.fromObj(decrypter), + DkgPublicKey.fromBytes(dkgPublicKey) + ); } public toObj(): DeployedStrategyJSON { return { - dkgRitual: this.dkgRitual.toObj(), - cohortConfig: this.cohort.toObj(), + decrypter: this.decrypter.toObj(), + dkgPublicKey: this.dkgPublicKey.toBytes(), }; } public equals(other: DeployedCbdStrategy) { return ( this.decrypter.equals(other.decrypter) && - this.cohort.equals(other.cohort) && - objectEquals(this.dkgRitual.toObj(), other.dkgRitual.toObj()) + bytesEqual(this.dkgPublicKey.toBytes(), other.dkgPublicKey.toBytes()) ); } } diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index 0997f3b93..6b6654199 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -1,17 +1,15 @@ -import { - EncryptedTreasureMap, - HRAC, - PublicKey, - SecretKey, -} from '@nucypher/nucypher-core'; +import { PublicKey, SecretKey } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { Alice } from '../../characters/alice'; import { Bob } from '../../characters/bob'; import { Enrico } from '../../characters/enrico'; -import { PreTDecDecrypter } from '../../characters/pre-recipient'; -import { ConditionSet, ConditionSetJSON } from '../../conditions'; -import { EnactedPolicy, EnactedPolicyJSON } from '../../policies/policy'; +import { + PreTDecDecrypter, + PreTDecDecrypterJSON, +} from '../../characters/pre-recipient'; +import { ConditionSet } from '../../conditions'; +import { EnactedPolicy } from '../../policies/policy'; import { base64ToU8Receiver, bytesEquals, toJSON } from '../../utils'; import { Cohort, CohortJSON } from '../cohort'; @@ -19,16 +17,15 @@ export type PreStrategyJSON = { cohort: CohortJSON; aliceSecretKeyBytes: Uint8Array; bobSecretKeyBytes: Uint8Array; - conditionSet?: ConditionSetJSON; startDate: Date; endDate: Date; }; export type DeployedPreStrategyJSON = { - policy: EnactedPolicyJSON; cohortConfig: CohortJSON; bobSecretKeyBytes: Uint8Array; - conditionSet?: ConditionSetJSON; + decrypterJSON: PreTDecDecrypterJSON; + policyKeyBytes: Uint8Array; }; export class PreStrategy { @@ -37,13 +34,11 @@ export class PreStrategy { private readonly aliceSecretKey: SecretKey, private readonly bobSecretKey: SecretKey, private readonly startDate: Date, - private readonly endDate: Date, - private readonly conditionSet?: ConditionSet + private readonly endDate: Date ) {} public static create( cohort: Cohort, - conditionSet?: ConditionSet, aliceSecretKey?: SecretKey, bobSecretKey?: SecretKey, startDate?: Date, @@ -66,8 +61,7 @@ export class PreStrategy { aliceSecretKey, bobSecretKey, startDate, - endDate, - conditionSet + endDate ); } @@ -92,22 +86,7 @@ export class PreStrategy { endDate: this.endDate, }; const policy = await alice.grant(policyParams, this.cohort.ursulaAddresses); - - const decrypter = new PreTDecDecrypter( - this.cohort.configuration.porterUri, - policy.policyKey, - policy.encryptedTreasureMap, - alice.verifyingKey, - this.bobSecretKey - ); - return new DeployedPreStrategy( - label, - this.cohort, - policy, - decrypter, - this.bobSecretKey, - this.conditionSet - ); + return DeployedPreStrategy.create(this.cohort, policy, this.bobSecretKey); } public static fromJSON(json: string) { @@ -125,7 +104,6 @@ export class PreStrategy { cohort, aliceSecretKeyBytes, bobSecretKeyBytes, - conditionSet, startDate, endDate, }: PreStrategyJSON) { @@ -134,8 +112,7 @@ export class PreStrategy { SecretKey.fromBEBytes(aliceSecretKeyBytes), SecretKey.fromBEBytes(bobSecretKeyBytes), new Date(startDate), - new Date(endDate), - conditionSet ? ConditionSet.fromObj(conditionSet) : undefined + new Date(endDate) ); } @@ -144,17 +121,12 @@ export class PreStrategy { cohort: this.cohort.toObj(), aliceSecretKeyBytes: this.aliceSecretKey.toBEBytes(), bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), - conditionSet: this.conditionSet ? this.conditionSet.toObj() : undefined, startDate: this.startDate, endDate: this.endDate, }; } public equals(other: PreStrategy) { - const conditionSetEquals = - this.conditionSet && other.conditionSet - ? this.conditionSet.equals(other.conditionSet) - : this.conditionSet === other.conditionSet; return ( this.cohort.equals(other.cohort) && // TODO: Add equality to WASM bindings @@ -166,7 +138,6 @@ export class PreStrategy { this.bobSecretKey.toBEBytes(), other.bobSecretKey.toBEBytes() ) && - conditionSetEquals && this.startDate.toString() === other.startDate.toString() && this.endDate.toString() === other.endDate.toString() ); @@ -174,17 +145,35 @@ export class PreStrategy { } export class DeployedPreStrategy { - public constructor( - public readonly label: string, + private constructor( public readonly cohort: Cohort, - public readonly policy: EnactedPolicy, - public readonly decrypter: PreTDecDecrypter, private readonly bobSecretKey: SecretKey, - public readonly conditionSet?: ConditionSet + public readonly decrypter: PreTDecDecrypter, + public readonly policyKey: PublicKey ) {} + public static create( + cohort: Cohort, + policy: EnactedPolicy, + bobSecretKey: SecretKey + ) { + const decrypter = PreTDecDecrypter.create( + cohort.configuration.porterUri, + bobSecretKey, + policy.policyKey, + policy.aliceVerifyingKey, + policy.encryptedTreasureMap + ); + return new DeployedPreStrategy( + cohort, + bobSecretKey, + decrypter, + policy.policyKey + ); + } + public makeEncrypter(conditionSet: ConditionSet): Enrico { - return new Enrico(this.policy.policyKey, undefined, conditionSet); + return new Enrico(this.policyKey, undefined, conditionSet); } public static fromJSON(json: string) { @@ -197,86 +186,39 @@ export class DeployedPreStrategy { } public static fromObj({ - policy, cohortConfig, bobSecretKeyBytes, - conditionSet, + decrypterJSON, + policyKeyBytes, }: DeployedPreStrategyJSON) { - const id = HRAC.fromBytes(policy.id); - const policyKey = PublicKey.fromCompressedBytes(policy.policyKey); - const encryptedTreasureMap = EncryptedTreasureMap.fromBytes( - policy.encryptedTreasureMap - ); - const aliceVerifyingKey = PublicKey.fromCompressedBytes( - policy.aliceVerifyingKey - ); - const newPolicy = { - id, - label: policy.label, - policyKey, - encryptedTreasureMap, - aliceVerifyingKey: aliceVerifyingKey.toCompressedBytes(), - size: policy.size, - startTimestamp: policy.startTimestamp, - endTimestamp: policy.endTimestamp, - txHash: policy.txHash, - }; - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); - const label = newPolicy.label; const cohort = Cohort.fromObj(cohortConfig); - - const conditionSetOrUndefined = conditionSet - ? ConditionSet.fromObj(conditionSet) - : undefined; - - const decrypter = new PreTDecDecrypter( - cohort.configuration.porterUri, - policyKey, - encryptedTreasureMap, - aliceVerifyingKey, - bobSecretKey - ); - return new DeployedPreStrategy( - label, - cohort, - newPolicy, - decrypter, - bobSecretKey, - conditionSetOrUndefined - ); + const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); + const decrypter = PreTDecDecrypter.fromObj(decrypterJSON); + const policyKey = PublicKey.fromCompressedBytes(policyKeyBytes); + return new DeployedPreStrategy(cohort, bobSecretKey, decrypter, policyKey); } public toObj(): DeployedPreStrategyJSON { - const policy = { - ...this.policy, - id: this.policy.id.toBytes(), - policyKey: this.policy.policyKey.toCompressedBytes(), - encryptedTreasureMap: this.policy.encryptedTreasureMap.toBytes(), - }; return { - policy, cohortConfig: this.cohort.toObj(), bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), - conditionSet: this.conditionSet?.toObj(), + decrypterJSON: this.decrypter.toObj(), + policyKeyBytes: this.policyKey.toCompressedBytes(), }; } public equals(other: DeployedPreStrategy) { - const conditionSetEquals = - this.conditionSet && other.conditionSet - ? this.conditionSet.equals(other.conditionSet) - : this.conditionSet === other.conditionSet; return ( - this.label === other.label && this.cohort.equals(other.cohort) && - bytesEquals(this.policy.id.toBytes(), other.policy.id.toBytes()) && - this.policy.label === other.policy.label && - this.policy.policyKey.equals(other.policy.policyKey) && bytesEquals( - this.policy.encryptedTreasureMap.toBytes(), - other.policy.encryptedTreasureMap.toBytes() + this.bobSecretKey.toBEBytes(), + other.bobSecretKey.toBEBytes() ) && - conditionSetEquals + this.decrypter.equals(other.decrypter) && + bytesEquals( + this.policyKey.toCompressedBytes(), + other.policyKey.toCompressedBytes() + ) ); } } diff --git a/test/acceptance/alice-grants.test.ts b/test/acceptance/alice-grants.test.ts index 741877a04..a5fce2365 100644 --- a/test/acceptance/alice-grants.test.ts +++ b/test/acceptance/alice-grants.test.ts @@ -64,9 +64,12 @@ describe('story: alice shares message with bob through policy', () => { }; policy = await alice.grant(policyParams); - expect(policy.aliceVerifyingKey).toEqual( - alice.verifyingKey.toCompressedBytes() - ); + expect( + bytesEqual( + policy.aliceVerifyingKey.toCompressedBytes(), + alice.verifyingKey.toCompressedBytes() + ) + ).toBeTruthy(); expect(policy.label).toBe(label); expect(getUrsulasSpy).toHaveBeenCalled(); expect(generateKFragsSpy).toHaveBeenCalled(); diff --git a/test/acceptance/delay-enact.test.ts b/test/acceptance/delay-enact.test.ts index ddd99d1b7..61574df95 100644 --- a/test/acceptance/delay-enact.test.ts +++ b/test/acceptance/delay-enact.test.ts @@ -1,4 +1,5 @@ import { + bytesEqual, fakeAlice, fakeRemoteBob, fakeUrsulas, @@ -37,9 +38,12 @@ describe('story: alice1 creates a policy but alice2 enacts it', () => { const preEnactedPolicy = await alice1.generatePreEnactedPolicy( policyParams ); - expect(preEnactedPolicy.aliceVerifyingKey).toEqual( - alice1.verifyingKey.toCompressedBytes() - ); + expect( + bytesEqual( + preEnactedPolicy.aliceVerifyingKey.toCompressedBytes(), + alice1.verifyingKey.toCompressedBytes() + ) + ).toBeTruthy(); expect(preEnactedPolicy.label).toBe(label); const enacted = await preEnactedPolicy.enact(alice2); diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 9e79aef41..483d52f1e 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -80,7 +80,7 @@ describe('Get Started (CBD PoC)', () => { ]); // 4. Build a Strategy - const newStrategy = PreStrategy.create(newCohort, conditions); + const newStrategy = PreStrategy.create(newCohort); const MMprovider = await detectEthereumProvider(); const mumbai = providers.getNetwork(80001); @@ -133,7 +133,6 @@ describe('Get Started (CBD PoC)', () => { ); expect(conditions.validate()).toEqual(true); expect(publishToBlockchainSpy).toHaveBeenCalled(); - expect(newDeployed.label).toEqual('test'); expect(getUrsulasSpy).toHaveBeenCalledTimes(2); expect(generateKFragsSpy).toHaveBeenCalled(); expect(encryptTreasureMapSpy).toHaveBeenCalled(); diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 6ca0664d2..eb08b341b 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -44,7 +44,7 @@ const variant = FerveoVariant.Precomputed; const makeCbdStrategy = async () => { const cohort = await makeCohort(ursulas); - const strategy = CbdStrategy.create(cohort, conditionSet); + const strategy = CbdStrategy.create(cohort); expect(strategy.cohort).toEqual(cohort); return strategy; }; @@ -135,7 +135,6 @@ describe('CbdDeployedStrategy', () => { await deployedStrategy.decrypter.retrieveAndDecrypt( aliceProvider, conditionSet, - deployedStrategy.dkgRitual, variant, ciphertext, aad diff --git a/test/unit/pre-strategy.test.ts b/test/unit/pre-strategy.test.ts index 18dde2a74..0879ce9b4 100644 --- a/test/unit/pre-strategy.test.ts +++ b/test/unit/pre-strategy.test.ts @@ -42,12 +42,7 @@ const mockedUrsulas = fakeUrsulas().slice(0, 3); const makePreStrategy = async () => { const cohort = await makeCohort(mockedUrsulas); - const strategy = PreStrategy.create( - cohort, - conditionSet, - aliceSecretKey, - bobSecretKey - ); + const strategy = PreStrategy.create(cohort, aliceSecretKey, bobSecretKey); expect(strategy.cohort).toEqual(cohort); return strategy; }; @@ -66,7 +61,6 @@ const makeDeployedPreStrategy = async () => { expect(makeTreasureMapSpy).toHaveBeenCalled(); expect(encryptTreasureMapSpy).toHaveBeenCalled(); - expect(deployedStrategy.conditionSet).toEqual(conditionSet); expect(deployedStrategy.cohort).toEqual(strategy.cohort); const ursulaAddresses = ( From b1a3332fe1a91b7bc6e51ae17363b17b87ee6de8 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 14 Jun 2023 11:29:39 +0200 Subject: [PATCH 66/98] remove redundant parameter --- src/sdk/strategy/pre-strategy.ts | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index 6b6654199..40f0cb12d 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -23,7 +23,6 @@ export type PreStrategyJSON = { export type DeployedPreStrategyJSON = { cohortConfig: CohortJSON; - bobSecretKeyBytes: Uint8Array; decrypterJSON: PreTDecDecrypterJSON; policyKeyBytes: Uint8Array; }; @@ -147,7 +146,6 @@ export class PreStrategy { export class DeployedPreStrategy { private constructor( public readonly cohort: Cohort, - private readonly bobSecretKey: SecretKey, public readonly decrypter: PreTDecDecrypter, public readonly policyKey: PublicKey ) {} @@ -164,12 +162,7 @@ export class DeployedPreStrategy { policy.aliceVerifyingKey, policy.encryptedTreasureMap ); - return new DeployedPreStrategy( - cohort, - bobSecretKey, - decrypter, - policy.policyKey - ); + return new DeployedPreStrategy(cohort, decrypter, policy.policyKey); } public makeEncrypter(conditionSet: ConditionSet): Enrico { @@ -187,21 +180,18 @@ export class DeployedPreStrategy { public static fromObj({ cohortConfig, - bobSecretKeyBytes, decrypterJSON, policyKeyBytes, }: DeployedPreStrategyJSON) { const cohort = Cohort.fromObj(cohortConfig); - const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes); const decrypter = PreTDecDecrypter.fromObj(decrypterJSON); const policyKey = PublicKey.fromCompressedBytes(policyKeyBytes); - return new DeployedPreStrategy(cohort, bobSecretKey, decrypter, policyKey); + return new DeployedPreStrategy(cohort, decrypter, policyKey); } public toObj(): DeployedPreStrategyJSON { return { cohortConfig: this.cohort.toObj(), - bobSecretKeyBytes: this.bobSecretKey.toBEBytes(), decrypterJSON: this.decrypter.toObj(), policyKeyBytes: this.policyKey.toCompressedBytes(), }; @@ -210,10 +200,6 @@ export class DeployedPreStrategy { public equals(other: DeployedPreStrategy) { return ( this.cohort.equals(other.cohort) && - bytesEquals( - this.bobSecretKey.toBEBytes(), - other.bobSecretKey.toBEBytes() - ) && this.decrypter.equals(other.decrypter) && bytesEquals( this.policyKey.toCompressedBytes(), From edd85ac0052bf5a718a91c6a33db5074a1a7afab Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 14 Jun 2023 11:42:02 +0200 Subject: [PATCH 67/98] fix after rebase --- src/characters/cbd-recipient.ts | 15 ++++++--------- src/dkg.ts | 20 +++++++++++++++----- test/unit/cbd-strategy.test.ts | 2 +- test/utils.ts | 5 +++-- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 56dcb149e..c461dc102 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -5,7 +5,6 @@ import { DecryptionShareSimple, decryptWithSharedSecret, DkgPublicParameters, - SharedSecret, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, SessionSharedSecret, @@ -40,15 +39,15 @@ export class CbdTDecDecrypter { private readonly porter: Porter, private readonly ritualId: number, private readonly dkgPublicParams: DkgPublicParameters, - private readonly threshold: number + private readonly threshold: number ) {} - public static create(porterUri: string, dkgRitual: DkgRitual, threshold: number) { + public static create(porterUri: string, dkgRitual: DkgRitual) { return new CbdTDecDecrypter( new Porter(porterUri), dkgRitual.id, dkgRitual.dkgPublicParams, - threshold, + dkgRitual.threshold ); } @@ -63,7 +62,6 @@ export class CbdTDecDecrypter { const decryptionShares = await this.retrieve( provider, conditionSet, - this.ritualId, variant, ciphertext ); @@ -85,17 +83,16 @@ export class CbdTDecDecrypter { public async retrieve( provider: ethers.providers.Web3Provider, conditionSet: ConditionSet, - ritualId: number, variant: number, ciphertext: Ciphertext ): Promise { const dkgParticipants = await DkgCoordinatorAgent.getParticipants( provider, - ritualId + this.ritualId ); const contextStr = await conditionSet.buildContext(provider).toJson(); const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests( - ritualId, + this.ritualId, variant, ciphertext, conditionSet, @@ -117,7 +114,7 @@ export class CbdTDecDecrypter { encryptedResponses, sharedSecrets, variant, - ritualId + this.ritualId ); } diff --git a/src/dkg.ts b/src/dkg.ts index c0bbd49e7..50798ce19 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -29,6 +29,7 @@ export function getVariantClass( throw new Error(`Invalid FerveoVariant: ${variant}`); } } + export function getCombineDecryptionSharesFunction( variant: FerveoVariant ): ( @@ -48,13 +49,15 @@ export interface DkgRitualJSON { id: number; dkgPublicKey: Uint8Array; dkgPublicParams: Uint8Array; + threshold: number; } export class DkgRitual { constructor( public readonly id: number, public readonly dkgPublicKey: DkgPublicKey, - public readonly dkgPublicParams: DkgPublicParameters + public readonly dkgPublicParams: DkgPublicParameters, + public readonly threshold: number ) {} public toObj(): DkgRitualJSON { @@ -62,14 +65,21 @@ export class DkgRitual { id: this.id, dkgPublicKey: this.dkgPublicKey.toBytes(), dkgPublicParams: this.dkgPublicParams.toBytes(), + threshold: this.threshold, }; } - public static fromObj(json: DkgRitualJSON): DkgRitual { + public static fromObj({ + id, + dkgPublicKey, + dkgPublicParams, + threshold, + }: DkgRitualJSON): DkgRitual { return new DkgRitual( - json.id, - DkgPublicKey.fromBytes(json.dkgPublicKey), - DkgPublicParameters.fromBytes(json.dkgPublicParams) + id, + DkgPublicKey.fromBytes(dkgPublicKey), + DkgPublicParameters.fromBytes(dkgPublicParams), + threshold ); } diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index eb08b341b..44e2adc49 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -53,7 +53,7 @@ async function makeDeployedCbdStrategy() { const strategy = await makeCbdStrategy(); const mockedDkg = fakeDkgFlow(variant, 0); - const mockedDkgRitual = fakeDkgRitual(mockedDkg); + const mockedDkgRitual = fakeDkgRitual(mockedDkg, mockedDkg.threshold); const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); const getUrsulasSpy = mockGetUrsulas(ursulas); const initializeRitualSpy = mockInitializeRitual(mockedDkgRitual); diff --git a/test/utils.ts b/test/utils.ts index 0d78c2102..cf2311d2e 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -500,11 +500,12 @@ export const mockRandomSessionStaticSecret = (secret: SessionStaticSecret) => { export const fakeRitualId = 0; -export const fakeDkgRitual = (ritual: { dkg: Dkg }) => { +export const fakeDkgRitual = (ritual: { dkg: Dkg }, thresold: number) => { return new DkgRitual( fakeRitualId, ritual.dkg.publicKey(), - ritual.dkg.publicParams() + ritual.dkg.publicParams(), + thresold ); }; From d7db27a90f8a27448568430a0654e6721813416e Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 12 Jun 2023 12:34:26 +0200 Subject: [PATCH 68/98] feat: validate function abi params and method --- src/conditions/base/evm.ts | 59 +++++++++++ test/unit/conditions/base/evm.test.ts | 139 +++++++++++++++----------- test/unit/conditions/context.test.ts | 1 + test/unit/testVariables.ts | 3 + 4 files changed, 141 insertions(+), 61 deletions(-) create mode 100644 src/conditions/base/evm.ts diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts new file mode 100644 index 000000000..bfbd44200 --- /dev/null +++ b/src/conditions/base/evm.ts @@ -0,0 +1,59 @@ +import Joi from 'joi'; + +import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; + +import { Condition } from './condition'; +import { returnValueTestSchema } from './schema'; + +export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; + +const functionAbiVariable = Joi.object({ + internalType: Joi.string().required(), + name: Joi.string().required(), + type: Joi.string().required(), +}); + +const functionAbiSchema = Joi.object({ + name: Joi.string().required(), + type: Joi.string().valid('function').required(), + inputs: Joi.array().items(functionAbiVariable), + outputs: Joi.array().items(functionAbiVariable), + // TODO: Should we restrict this to 'view'? + // stateMutability: Joi.string().valid('view').required(), +}).custom((functionAbi, helper) => { + // Validate method name + const method = helper.state.ancestors[0].method; + if (functionAbi.name !== method) { + return helper.message({ + custom: '"method" must be the same as "functionAbi.name"', + }); + } + + // Validate nr of parameters + const parameters = helper.state.ancestors[0].parameters; + if (functionAbi.inputs?.length !== parameters.length) { + return helper.message({ + custom: '"parameters" must have the same length as "functionAbi.inputs"', + }); + } + + return functionAbi; +}); + +export class EvmCondition extends Condition { + public readonly schema = Joi.object({ + contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), + chain: Joi.number() + .valid(...SUPPORTED_CHAINS) + .required(), + standardContractType: Joi.string() + .valid(...STANDARD_CONTRACT_TYPES) + .optional(), + functionAbi: functionAbiSchema.optional(), + method: Joi.string().required(), + parameters: Joi.array().required(), + returnValueTest: returnValueTestSchema, + }) + // At most one of these keys needs to be present + .xor('standardContractType', 'functionAbi'); +} diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts index 404d2d90b..c696f4925 100644 --- a/test/unit/conditions/base/evm.test.ts +++ b/test/unit/conditions/base/evm.test.ts @@ -1,5 +1,11 @@ +import { SecretKey } from '@nucypher/nucypher-core'; + +import { CustomContextParam } from '../../../../build/main/src'; +import { ConditionContext, ConditionSet } from '../../../../src/conditions'; import { ContractCondition } from '../../../../src/conditions/base'; -import { testContractConditionObj } from '../../testVariables'; +import { USER_ADDRESS_PARAM } from '../../../../src/conditions/const'; +import { fakeWeb3Provider } from '../../../utils'; +import { testContractConditionObj, testFunctionAbi } from '../../testVariables'; describe('validation', () => { it('accepts on a valid schema', () => { @@ -28,7 +34,6 @@ describe('validation', () => { describe('accepts either standardContractType or functionAbi but not both or none', () => { const standardContractType = 'ERC20'; - const functionAbi = { fake_function_abi: true }; it('accepts standardContractType', () => { const conditionObj = { @@ -46,7 +51,9 @@ describe('accepts either standardContractType or functionAbi but not both or non it('accepts functionAbi', () => { const conditionObj = { ...testContractConditionObj, - functionAbi, + functionAbi: testFunctionAbi, + method: testFunctionAbi.name, + parameters: ['0x1234', 1234], standardContractType: undefined, }; const contractCondition = new ContractCondition(conditionObj); @@ -60,7 +67,9 @@ describe('accepts either standardContractType or functionAbi but not both or non const conditionObj = { ...testContractConditionObj, standardContractType, - functionAbi, + parameters: ['0x1234', 1234], + functionAbi: testFunctionAbi, + method: testFunctionAbi.name, }; const contractCondition = new ContractCondition(conditionObj); expect(() => contractCondition.toObj()).toThrow( @@ -81,61 +90,69 @@ describe('accepts either standardContractType or functionAbi but not both or non }); }); -// TODO(#124) -// it('accepts custom parameters in function abi methods', async () => { -// throw new Error('Not implemented'); -// }); +describe('supports custom function abi', () => { + const evmConditionObj = { + ...testEvmConditionObj, + standardContractType: undefined, + functionAbi: testFunctionAbi, + method: 'myFunction', + parameters: [USER_ADDRESS_PARAM, ':customParam'], + returnValueTest: { + index: 0, + comparator: '==', + value: USER_ADDRESS_PARAM, + }, + }; + const evmCondition = new EvmCondition(evmConditionObj); + const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + const conditionSet = new ConditionSet([evmCondition]); + const conditionContext = new ConditionContext( + conditionSet.toWASMConditions(), + web3Provider + ); + const myCustomParam = ':customParam'; + const customParams: Record = {}; + customParams[myCustomParam] = 1234; + + it('accepts a custom param in function abi method params', async () => { + const asJson = await conditionContext + .withCustomParams(customParams) + .toJson(); + expect(asJson).toBeDefined(); + expect(asJson).toContain(USER_ADDRESS_PARAM); + expect(asJson).toContain(myCustomParam); + }); + + it('rejects on functionAbi mismatch', async () => { + const badEvmConditionObj = { + ...evmConditionObj, + functionAbi: { bad_function_abi: 'bad_function_abi' }, + }; + const evmCondition = new EvmCondition(badEvmConditionObj); + expect(() => evmCondition.toObj()).toThrow( + 'Invalid condition: "functionAbi.name" is required' + ); + }); -// TODO(#124) -// describe('supports custom function abi', () => { -// const fakeFunctionAbi = { -// name: 'myFunction', -// type: 'function', -// inputs: [ -// { -// name: 'account', -// type: 'address', -// }, -// { -// name: 'myCustomParam', -// type: 'uint256', -// }, -// ], -// outputs: [ -// { -// name: 'someValue', -// type: 'uint256', -// }, -// ], -// }; -// const contractConditionObj = { -// ...testContractConditionObj, -// functionAbi: fakeFunctionAbi, -// method: 'myFunction', -// parameters: [USER_ADDRESS_PARAM, ':customParam'], -// returnValueTest: { -// index: 0, -// comparator: '==', -// value: USER_ADDRESS_PARAM, -// }, -// }; -// const contractCondition = new ContractCondition(contractConditionObj); -// const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); -// const conditionSet = new ConditionSet([contractCondition]); -// const conditionContext = new ConditionContext( -// conditionSet.toWASMConditions(), -// web3Provider -// ); -// const myCustomParam = ':customParam'; -// const customParams: Record = {}; -// customParams[myCustomParam] = 1234; -// -// it('accepts custom function abi', async () => { -// const asJson = await conditionContext -// .withCustomParams(customParams) -// .toJson(); -// expect(asJson).toBeDefined(); -// expect(asJson).toContain(USER_ADDRESS_PARAM); -// expect(asJson).toContain(myCustomParam); -// }); -// }); + it('rejects on functionAbi mismatch', async () => { + const badEvmConditionObj = { + ...evmConditionObj, + method: 'badMethod', + }; + const evmCondition = new EvmCondition(badEvmConditionObj); + expect(() => evmCondition.toObj()).toThrow( + 'Invalid condition: "method" must be the same as "functionAbi.name"' + ); + }); + + it('rejects on functionAbi mismatch', async () => { + const badEvmConditionObj = { + ...evmConditionObj, + parameters: [], + }; + const evmCondition = new EvmCondition(badEvmConditionObj); + expect(() => evmCondition.toObj()).toThrow( + 'Invalid condition: "parameters" must have the same length as "functionAbi.inputs"' + ); + }); +}); diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts index 79d048157..b86fb0d0c 100644 --- a/test/unit/conditions/context.test.ts +++ b/test/unit/conditions/context.test.ts @@ -82,6 +82,7 @@ describe('context parameters', () => { ...testContractConditionObj, standardContractType: undefined, // We're going to use a custom function ABI functionAbi: testFunctionAbi, + method: testFunctionAbi.name, parameters: [USER_ADDRESS_PARAM, customParamKey], // We're going to use a custom parameter returnValueTest: { ...testReturnValueTest, diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index 6ece2f977..c199a3043 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -40,16 +40,19 @@ export const testFunctionAbi = { type: 'function', inputs: [ { + internalType: 'address', name: 'account', type: 'address', }, { + internalType: 'uint256', name: 'myCustomParam', type: 'uint256', }, ], outputs: [ { + internalType: 'uint256', name: 'someValue', type: 'uint256', }, From 4f324113fcddc18698d87b3e45d9acbddfc9fa26 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 14 Jun 2023 15:26:48 +0200 Subject: [PATCH 69/98] fix after rebase --- src/conditions/base/contract.ts | 35 +++++- src/conditions/base/evm.ts | 59 ---------- test/unit/conditions/base/evm.test.ts | 158 -------------------------- 3 files changed, 34 insertions(+), 218 deletions(-) delete mode 100644 src/conditions/base/evm.ts delete mode 100644 test/unit/conditions/base/evm.test.ts diff --git a/src/conditions/base/contract.ts b/src/conditions/base/contract.ts index 8485227bc..4f4b5dd3c 100644 --- a/src/conditions/base/contract.ts +++ b/src/conditions/base/contract.ts @@ -6,6 +6,39 @@ import { RpcCondition, rpcConditionSchema } from './rpc'; export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; +const functionAbiVariable = Joi.object({ + internalType: Joi.string().required(), + name: Joi.string().required(), + type: Joi.string().required(), +}); + +const functionAbiSchema = Joi.object({ + name: Joi.string().required(), + type: Joi.string().valid('function').required(), + inputs: Joi.array().items(functionAbiVariable), + outputs: Joi.array().items(functionAbiVariable), + // TODO: Should we restrict this to 'view'? + // stateMutability: Joi.string().valid('view').required(), +}).custom((functionAbi, helper) => { + // Validate method name + const method = helper.state.ancestors[0].method; + if (functionAbi.name !== method) { + return helper.message({ + custom: '"method" must be the same as "functionAbi.name"', + }); + } + + // Validate nr of parameters + const parameters = helper.state.ancestors[0].parameters; + if (functionAbi.inputs?.length !== parameters.length) { + return helper.message({ + custom: '"parameters" must have the same length as "functionAbi.inputs"', + }); + } + + return functionAbi; +}); + const contractMethodSchemas: Record = { ...rpcConditionSchema, contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), @@ -13,7 +46,7 @@ const contractMethodSchemas: Record = { .valid(...STANDARD_CONTRACT_TYPES) .optional(), method: Joi.string().required(), - functionAbi: Joi.object().optional(), + functionAbi: functionAbiSchema.optional(), parameters: Joi.array().required(), }; diff --git a/src/conditions/base/evm.ts b/src/conditions/base/evm.ts deleted file mode 100644 index bfbd44200..000000000 --- a/src/conditions/base/evm.ts +++ /dev/null @@ -1,59 +0,0 @@ -import Joi from 'joi'; - -import { ETH_ADDRESS_REGEXP, SUPPORTED_CHAINS } from '../const'; - -import { Condition } from './condition'; -import { returnValueTestSchema } from './schema'; - -export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; - -const functionAbiVariable = Joi.object({ - internalType: Joi.string().required(), - name: Joi.string().required(), - type: Joi.string().required(), -}); - -const functionAbiSchema = Joi.object({ - name: Joi.string().required(), - type: Joi.string().valid('function').required(), - inputs: Joi.array().items(functionAbiVariable), - outputs: Joi.array().items(functionAbiVariable), - // TODO: Should we restrict this to 'view'? - // stateMutability: Joi.string().valid('view').required(), -}).custom((functionAbi, helper) => { - // Validate method name - const method = helper.state.ancestors[0].method; - if (functionAbi.name !== method) { - return helper.message({ - custom: '"method" must be the same as "functionAbi.name"', - }); - } - - // Validate nr of parameters - const parameters = helper.state.ancestors[0].parameters; - if (functionAbi.inputs?.length !== parameters.length) { - return helper.message({ - custom: '"parameters" must have the same length as "functionAbi.inputs"', - }); - } - - return functionAbi; -}); - -export class EvmCondition extends Condition { - public readonly schema = Joi.object({ - contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), - chain: Joi.number() - .valid(...SUPPORTED_CHAINS) - .required(), - standardContractType: Joi.string() - .valid(...STANDARD_CONTRACT_TYPES) - .optional(), - functionAbi: functionAbiSchema.optional(), - method: Joi.string().required(), - parameters: Joi.array().required(), - returnValueTest: returnValueTestSchema, - }) - // At most one of these keys needs to be present - .xor('standardContractType', 'functionAbi'); -} diff --git a/test/unit/conditions/base/evm.test.ts b/test/unit/conditions/base/evm.test.ts deleted file mode 100644 index c696f4925..000000000 --- a/test/unit/conditions/base/evm.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { SecretKey } from '@nucypher/nucypher-core'; - -import { CustomContextParam } from '../../../../build/main/src'; -import { ConditionContext, ConditionSet } from '../../../../src/conditions'; -import { ContractCondition } from '../../../../src/conditions/base'; -import { USER_ADDRESS_PARAM } from '../../../../src/conditions/const'; -import { fakeWeb3Provider } from '../../../utils'; -import { testContractConditionObj, testFunctionAbi } from '../../testVariables'; - -describe('validation', () => { - it('accepts on a valid schema', () => { - const contract = new ContractCondition(testContractConditionObj); - expect(contract.toObj()).toEqual({ - ...testContractConditionObj, - _class: 'ContractCondition', - }); - }); - - it('rejects an invalid schema', () => { - const badContractCondition = { - ...testContractConditionObj, - // Intentionally removing `contractAddress` - contractAddress: undefined, - }; - const badEvm = new ContractCondition(badContractCondition); - expect(() => badEvm.toObj()).toThrow( - 'Invalid condition: "contractAddress" is required' - ); - - const { error } = badEvm.validate(badContractCondition); - expect(error?.message).toEqual('"contractAddress" is required'); - }); -}); - -describe('accepts either standardContractType or functionAbi but not both or none', () => { - const standardContractType = 'ERC20'; - - it('accepts standardContractType', () => { - const conditionObj = { - ...testContractConditionObj, - standardContractType, - functionAbi: undefined, - }; - const contractCondition = new ContractCondition(conditionObj); - expect(contractCondition.toObj()).toEqual({ - ...conditionObj, - _class: 'ContractCondition', - }); - }); - - it('accepts functionAbi', () => { - const conditionObj = { - ...testContractConditionObj, - functionAbi: testFunctionAbi, - method: testFunctionAbi.name, - parameters: ['0x1234', 1234], - standardContractType: undefined, - }; - const contractCondition = new ContractCondition(conditionObj); - expect(contractCondition.toObj()).toEqual({ - ...conditionObj, - _class: 'ContractCondition', - }); - }); - - it('rejects both', () => { - const conditionObj = { - ...testContractConditionObj, - standardContractType, - parameters: ['0x1234', 1234], - functionAbi: testFunctionAbi, - method: testFunctionAbi.name, - }; - const contractCondition = new ContractCondition(conditionObj); - expect(() => contractCondition.toObj()).toThrow( - '"value" contains a conflict between exclusive peers [standardContractType, functionAbi]' - ); - }); - - it('rejects none', () => { - const conditionObj = { - ...testContractConditionObj, - standardContractType: undefined, - functionAbi: undefined, - }; - const contractCondition = new ContractCondition(conditionObj); - expect(() => contractCondition.toObj()).toThrow( - '"value" must contain at least one of [standardContractType, functionAbi]' - ); - }); -}); - -describe('supports custom function abi', () => { - const evmConditionObj = { - ...testEvmConditionObj, - standardContractType: undefined, - functionAbi: testFunctionAbi, - method: 'myFunction', - parameters: [USER_ADDRESS_PARAM, ':customParam'], - returnValueTest: { - index: 0, - comparator: '==', - value: USER_ADDRESS_PARAM, - }, - }; - const evmCondition = new EvmCondition(evmConditionObj); - const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - const conditionSet = new ConditionSet([evmCondition]); - const conditionContext = new ConditionContext( - conditionSet.toWASMConditions(), - web3Provider - ); - const myCustomParam = ':customParam'; - const customParams: Record = {}; - customParams[myCustomParam] = 1234; - - it('accepts a custom param in function abi method params', async () => { - const asJson = await conditionContext - .withCustomParams(customParams) - .toJson(); - expect(asJson).toBeDefined(); - expect(asJson).toContain(USER_ADDRESS_PARAM); - expect(asJson).toContain(myCustomParam); - }); - - it('rejects on functionAbi mismatch', async () => { - const badEvmConditionObj = { - ...evmConditionObj, - functionAbi: { bad_function_abi: 'bad_function_abi' }, - }; - const evmCondition = new EvmCondition(badEvmConditionObj); - expect(() => evmCondition.toObj()).toThrow( - 'Invalid condition: "functionAbi.name" is required' - ); - }); - - it('rejects on functionAbi mismatch', async () => { - const badEvmConditionObj = { - ...evmConditionObj, - method: 'badMethod', - }; - const evmCondition = new EvmCondition(badEvmConditionObj); - expect(() => evmCondition.toObj()).toThrow( - 'Invalid condition: "method" must be the same as "functionAbi.name"' - ); - }); - - it('rejects on functionAbi mismatch', async () => { - const badEvmConditionObj = { - ...evmConditionObj, - parameters: [], - }; - const evmCondition = new EvmCondition(badEvmConditionObj); - expect(() => evmCondition.toObj()).toThrow( - 'Invalid condition: "parameters" must have the same length as "functionAbi.inputs"' - ); - }); -}); From 6c5368326f84bbab41232df539ffbeeb44818d91 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 15 Jun 2023 15:01:36 +0200 Subject: [PATCH 70/98] apply pr suggestions --- test/docs/cbd.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 483d52f1e..5d4be4a84 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -103,9 +103,8 @@ describe('Get Started (CBD PoC)', () => { const NFTBalance = new ContractCondition(NFTBalanceConfig); const newConditions = new ConditionSet([NFTBalance]); const plaintext = 'this is a secret'; - const encryptedMessageKit = newDeployed - .makeEncrypter(newConditions) - .encryptMessagePre(plaintext); + const encrypter = newDeployed.makeEncrypter(newConditions); + const encryptedMessageKit = encrypter.encryptMessagePre(plaintext); // Mocking - Not a part of any code example const retrieveCFragsSpy = mockRetrieveAndDecrypt( From 5b5ba52fd7f1fa571f2906f94aa1f1c4d661d7e3 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Wed, 14 Jun 2023 20:26:24 -0400 Subject: [PATCH 71/98] Initial work for implementing prefixed conditions in typescript. --- src/characters/pre-recipient.ts | 9 ++- src/conditions/base/contract.ts | 4 +- src/conditions/base/rpc.ts | 2 +- src/conditions/base/time.ts | 2 +- src/conditions/compound-condition.ts | 34 ++++++++ src/conditions/condition-set.ts | 71 +++-------------- src/conditions/context/context.ts | 10 ++- src/conditions/index.ts | 2 +- src/conditions/operator.ts | 26 ------- test/docs/cbd.test.ts | 11 ++- test/integration/enrico.test.ts | 6 +- test/integration/pre.test.ts | 13 ++-- test/unit/cbd-strategy.test.ts | 2 +- .../conditions/compound-condition.test.ts | 78 +++++++++++++++++++ test/unit/conditions/condition-set.test.ts | 61 ++------------- test/unit/conditions/context.test.ts | 16 ++-- test/unit/conditions/operator.test.ts | 14 ---- test/unit/pre-strategy.test.ts | 2 +- test/unit/testVariables.ts | 10 +++ 19 files changed, 180 insertions(+), 193 deletions(-) create mode 100644 src/conditions/compound-condition.ts delete mode 100644 src/conditions/operator.ts create mode 100644 test/unit/conditions/compound-condition.test.ts delete mode 100644 test/unit/conditions/operator.test.ts diff --git a/src/characters/pre-recipient.ts b/src/characters/pre-recipient.ts index a1b624169..1fb3066a5 100644 --- a/src/characters/pre-recipient.ts +++ b/src/characters/pre-recipient.ts @@ -8,7 +8,7 @@ import { } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { ConditionSet } from '../conditions'; +import { Condition, ConditionContext } from '../conditions'; import { Keyring } from '../keyring'; import { PolicyMessageKit } from '../kits/message'; import { RetrievalResult } from '../kits/retrieval'; @@ -106,8 +106,11 @@ export class PreTDecDecrypter { .map((condition) => JSON.parse(condition.toString())) .reduce((acc: Record[], val) => acc.concat(val), []); - const conditionContext = - ConditionSet.fromConditionList(conditions).buildContext(provider); + const conditionsList = conditions.map((ele: Record) => { + return Condition.fromObj(ele); + }); + + const conditionContext = new ConditionContext(conditionsList, provider); const policyMessageKits = messageKits.map((mk) => PolicyMessageKit.fromMessageKit( diff --git a/src/conditions/base/contract.ts b/src/conditions/base/contract.ts index 4f4b5dd3c..f1bea2584 100644 --- a/src/conditions/base/contract.ts +++ b/src/conditions/base/contract.ts @@ -39,7 +39,7 @@ const functionAbiSchema = Joi.object({ return functionAbi; }); -const contractMethodSchemas: Record = { +export const contractMethodSchema: Record = { ...rpcConditionSchema, contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), standardContractType: Joi.string() @@ -51,7 +51,7 @@ const contractMethodSchemas: Record = { }; export class ContractCondition extends RpcCondition { - public readonly schema = Joi.object(contractMethodSchemas) + public readonly schema = Joi.object(contractMethodSchema) // At most one of these keys needs to be present .xor('standardContractType', 'functionAbi'); } diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 4a088817d..fe73c20af 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -18,7 +18,7 @@ const makeParameters = () => })), }); -export const rpcConditionSchema = { +export const rpcConditionSchema: Record = { chain: Joi.number() .valid(...SUPPORTED_CHAINS) .required(), diff --git a/src/conditions/base/time.ts b/src/conditions/base/time.ts index 8b136e4a7..535c6b9a9 100644 --- a/src/conditions/base/time.ts +++ b/src/conditions/base/time.ts @@ -6,7 +6,7 @@ import { RpcCondition, rpcConditionSchema } from './rpc'; const BLOCKTIME_METHOD = 'blocktime'; -const timeConditionSchema = { +export const timeConditionSchema: Record = { // TimeCondition is an RpcCondition with the method set to 'blocktime' and no parameters ...omit(rpcConditionSchema, ['parameters']), method: Joi.string().valid(BLOCKTIME_METHOD).required(), diff --git a/src/conditions/compound-condition.ts b/src/conditions/compound-condition.ts new file mode 100644 index 000000000..ce050c04f --- /dev/null +++ b/src/conditions/compound-condition.ts @@ -0,0 +1,34 @@ +import Joi from 'joi'; + +import { Condition } from './base/condition'; +import { contractMethodSchema } from './base/contract'; +import { rpcConditionSchema } from './base/rpc'; +import { timeConditionSchema } from './base/time'; + +const OR_OPERATOR = 'or'; +const AND_OPERATOR = 'and'; + +const LOGICAL_OPERATORS = [AND_OPERATOR, OR_OPERATOR]; + +export const compoundCondition = Joi.object({ + operator: Joi.string() + .valid(...LOGICAL_OPERATORS) + .required(), + operands: Joi.array() + .min(2) + .items( + Joi.object(rpcConditionSchema), + Joi.object(timeConditionSchema), + Joi.object(contractMethodSchema).xor( + 'standardContractType', + 'functionAbi' + ), + Joi.link('#compoundCondition') + ) + .required() + .valid(), +}).id('compoundCondition'); + +export class CompoundCondition extends Condition { + public readonly schema = compoundCondition; +} diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index 29417b068..de61e2022 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -1,68 +1,27 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; -import deepEqual from 'deep-equal'; import { ethers } from 'ethers'; import { toJSON } from '../utils'; import { Condition } from './base'; import { ConditionContext } from './context'; -import { Operator } from './operator'; - -type ConditionOrOperator = Condition | Operator; export type ConditionSetJSON = { - conditions: ({ operator: string } | Record)[]; + condition: Record; }; export class ConditionSet { - constructor(public readonly conditions: ReadonlyArray) {} - - public validate() { - // Expects [Condition, Operator, Condition, Operator, ...], where the last element is a Condition - - if (this.conditions.length % 2 === 0) { - throw new Error( - 'conditions must be odd length, every other element being an operator' - ); - } - - this.conditions.forEach((cndOrOp: ConditionOrOperator, index) => { - if (index % 2 && !(cndOrOp instanceof Operator)) { - throw new Error( - `index ${index} must be an Operator, got ${cndOrOp.constructor.name} instead` - ); - } - if (!(index % 2) && cndOrOp instanceof Operator) { - throw new Error( - `index ${index} must be a Condition, got ${cndOrOp.constructor.name} instead` - ); - } - }); - return true; - } + constructor(public readonly condition: Condition) {} public toObj(): ConditionSetJSON { - const conditions = this.conditions.map((cnd) => cnd.toObj()); - return { conditions }; + // TODO add version here + const condition_json = this.condition.toObj(); + return { condition: condition_json }; } public static fromObj(obj: ConditionSetJSON): ConditionSet { - const conditions = obj.conditions.map((cnd) => { - if ('operator' in cnd) { - return Operator.fromObj(cnd as Record); - } - return Condition.fromObj(cnd); - }); - return new ConditionSet(conditions); - } - - public static fromConditionList(list: ReadonlyArray>) { - return new ConditionSet( - list.map((ele: Record) => { - if ('operator' in ele) return Operator.fromObj(ele); - return Condition.fromObj(ele); - }) - ); + const condition = Condition.fromObj(obj.condition); + return new ConditionSet(condition); } public toJson(): string { @@ -80,22 +39,10 @@ export class ConditionSet { public buildContext( provider: ethers.providers.Web3Provider ): ConditionContext { - return new ConditionContext(this.toWASMConditions(), provider); + return new ConditionContext([this.condition], provider); } public equals(other: ConditionSet): boolean { - // TODO: This is a hack to make the equals method work for Condition - // TODO: Implement proper casting from Conditon to _class type - const thisConditions = this.conditions.map((cnd) => { - const asObj = cnd.toObj(); - delete asObj._class; - return asObj; - }); - const otherConditions = other.conditions.map((cnd) => { - const asObj = cnd.toObj(); - delete asObj._class; - return asObj; - }); - return deepEqual(thisConditions, otherConditions); + return this.condition.equals(other.condition); } } diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index f966dfc5c..dc385245b 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -2,6 +2,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { fromJSON, toJSON } from '../../utils'; +import { Condition } from '../base/condition'; import { USER_ADDRESS_PARAM } from '../const'; import { TypedSignature, WalletAuthenticationProvider } from './providers'; @@ -16,7 +17,7 @@ export class ConditionContext { private readonly walletAuthProvider: WalletAuthenticationProvider; constructor( - private readonly conditions: WASMConditions, + private readonly conditions: ReadonlyArray, // TODO: We don't always need a web3 provider, only in cases where some specific context parameters are used // TODO: Consider making this optional or introducing a different pattern to handle that private readonly web3Provider: ethers.providers.Web3Provider, @@ -42,8 +43,11 @@ export class ConditionContext { const requestedParameters = new Set(); // Search conditions for parameters - const { conditions } = fromJSON(this.conditions.toString()); - for (const cond of conditions) { + const conditions = this.conditions.map((cnd) => cnd.toObj()); + const conditionsToCheck = fromJSON( + new WASMConditions(toJSON(conditions)).toString() + ); + for (const cond of conditionsToCheck) { // Check return value test const rvt = cond.returnValueTest.value; if (typeof rvt === 'string' && rvt.startsWith(CONTEXT_PARAM_PREFIX)) { diff --git a/src/conditions/index.ts b/src/conditions/index.ts index 8047d8129..7e335a0e0 100644 --- a/src/conditions/index.ts +++ b/src/conditions/index.ts @@ -5,6 +5,6 @@ export { predefined, base }; export { Condition } from './base/condition'; export type { ConditionSetJSON } from './condition-set'; export { ConditionSet } from './condition-set'; -export { Operator } from './operator'; +export { CompoundCondition } from './compound-condition'; export type { CustomContextParam } from './context'; export { ConditionContext } from './context'; diff --git a/src/conditions/operator.ts b/src/conditions/operator.ts deleted file mode 100644 index 51a6cdb2e..000000000 --- a/src/conditions/operator.ts +++ /dev/null @@ -1,26 +0,0 @@ -export class Operator { - static readonly LOGICAL_OPERATORS: ReadonlyArray = ['and', 'or']; - static readonly AND = new Operator('and'); - static readonly OR = new Operator('or'); - - public constructor(public readonly operator: string) { - if (!Operator.LOGICAL_OPERATORS.includes(operator)) { - throw `"${operator}" must be one of [${Operator.LOGICAL_OPERATORS.join( - ', ' - )}]`; - } - this.operator = operator; - } - - public toObj() { - return { operator: this.operator }; - } - - public static fromObj(obj: Record) { - return new Operator(obj.operator); - } - - public equals(other: Operator): boolean { - return this.operator === other.operator; - } -} diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index 5d4be4a84..b5de452be 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -74,10 +74,10 @@ describe('Get Started (CBD PoC)', () => { parameters: [5954], }); - const conditions = new ConditionSet([ - NFTOwnership, + const conditions = new ConditionSet( + NFTOwnership // Other conditions can be added here - ]); + ); // 4. Build a Strategy const newStrategy = PreStrategy.create(newCohort); @@ -101,7 +101,7 @@ describe('Get Started (CBD PoC)', () => { }, }; const NFTBalance = new ContractCondition(NFTBalanceConfig); - const newConditions = new ConditionSet([NFTBalance]); + const newConditions = new ConditionSet(NFTBalance); const plaintext = 'this is a secret'; const encrypter = newDeployed.makeEncrypter(newConditions); const encryptedMessageKit = encrypter.encryptMessagePre(plaintext); @@ -123,14 +123,13 @@ describe('Get Started (CBD PoC)', () => { // const expectedAddresses = fakeUrsulas().map((u) => u.checksumAddress); - const condObj = conditions.conditions[0].toObj(); + const condObj = conditions.condition.toObj(); expect(newCohort.ursulaAddresses).toEqual(expectedAddresses); expect(condObj.parameters).toEqual([5954]); expect(condObj.chain).toEqual(5); expect(condObj.contractAddress).toEqual( '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' ); - expect(conditions.validate()).toEqual(true); expect(publishToBlockchainSpy).toHaveBeenCalled(); expect(getUrsulasSpy).toHaveBeenCalledTimes(2); expect(generateKFragsSpy).toHaveBeenCalled(); diff --git a/test/integration/enrico.test.ts b/test/integration/enrico.test.ts index bd90e5d5a..86eb4f18e 100644 --- a/test/integration/enrico.test.ts +++ b/test/integration/enrico.test.ts @@ -105,7 +105,7 @@ describe('enrico', () => { chain: 5, }); - const conditions = new ConditionSet([ownsBufficornNFT]); + const conditions = new ConditionSet(ownsBufficornNFT); const enrico = new Enrico(policyKey, undefined, conditions); const encrypted = enrico.encryptMessagePre(toBytes(message)); @@ -135,8 +135,8 @@ describe('enrico', () => { parameters: [6969], }); - const conditions = new ConditionSet([ownsBufficornNFT]); - const updatedConditions = new ConditionSet([ownsNonsenseNFT]); + const conditions = new ConditionSet(ownsBufficornNFT); + const updatedConditions = new ConditionSet(ownsNonsenseNFT); const enrico = new Enrico(policyKey, undefined, conditions); const encrypted = enrico.encryptMessagePre( diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index 1a9fb5600..1db2b8df2 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -1,13 +1,13 @@ import { CapsuleFrag, reencrypt } from '@nucypher/nucypher-core'; import { conditions, Enrico, MessageKit, PolicyMessageKit } from '../../src'; +import { CompoundCondition } from '../../src/conditions'; import { RetrievalResult } from '../../src/kits/retrieval'; import { toBytes, zip } from '../../src/utils'; import { fakeAlice, fakeBob, fakeUrsulas, reencryptKFrags } from '../utils'; const { predefined: { ERC721Ownership }, - Operator, ConditionSet, } = conditions; @@ -94,11 +94,12 @@ describe('proxy reencryption', () => { chain: 1, parameters: [1], }); - const conditionsSet = new ConditionSet([ - genuineUndead, - Operator.OR, - gnomePals, - ]); + const conditionsSet = new ConditionSet( + new CompoundCondition({ + operator: 'or', + operands: [genuineUndead.toObj(), gnomePals.toObj()], + }) + ); const enrico = new Enrico(policyEncryptingKey, undefined, conditionsSet); const encryptedMessage = enrico.encryptMessagePre(plaintext); diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 44e2adc49..743c65d0b 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -38,7 +38,7 @@ const ownsNFT = new ERC721Ownership({ parameters: [3591], chain: 5, }); -const conditionSet = new ConditionSet([ownsNFT]); +const conditionSet = new ConditionSet(ownsNFT); const ursulas = fakeUrsulas().slice(0, 3); const variant = FerveoVariant.Precomputed; diff --git a/test/unit/conditions/compound-condition.test.ts b/test/unit/conditions/compound-condition.test.ts new file mode 100644 index 000000000..433ed7304 --- /dev/null +++ b/test/unit/conditions/compound-condition.test.ts @@ -0,0 +1,78 @@ +import { CompoundCondition } from '../../../src/conditions'; +import { + testContractConditionObj, + testRpcConditionObj, + testTimeConditionObj, +} from '../testVariables'; + +describe('validate', () => { + it('accepts or operator', () => { + const orCondition = new CompoundCondition({ + operator: 'or', + operands: [testRpcConditionObj, testTimeConditionObj], + }).toObj(); + + expect(orCondition.operator).toEqual('or'); + expect(orCondition.operands).toEqual([ + testRpcConditionObj, + testTimeConditionObj, + ]); + }); + + it('accepts and operator', () => { + const orCondition = new CompoundCondition({ + operator: 'and', + operands: [testContractConditionObj, testTimeConditionObj], + }).toObj(); + + expect(orCondition.operator).toEqual('and'); + expect(orCondition.operands).toEqual([ + testContractConditionObj, + testTimeConditionObj, + ]); + }); + + it('rejects an invalid operator', () => { + expect(() => + new CompoundCondition({ + operator: 'not-an-operator', + operands: [testRpcConditionObj, testTimeConditionObj], + }).toObj() + ).toThrow('"operator" must be one of [and, or]'); + }); + + it('rejects invalid number of operands = 0', () => { + expect(() => + new CompoundCondition({ + operator: 'or', + operands: [], + }).toObj() + ).toThrow('"operands" must contain at least 2 items'); + }); + + it('rejects invalid number of operands = 1', () => { + expect(() => + new CompoundCondition({ + operator: 'or', + operands: [testRpcConditionObj], + }).toObj() + ).toThrow('"operands" must contain at least 2 items'); + }); + + it('it allows recursive compound conditions', () => { + const compoundCondition = new CompoundCondition({ + operator: 'and', + operands: [ + testContractConditionObj, + testTimeConditionObj, + testRpcConditionObj, + { + operator: 'or', + operands: [testRpcConditionObj, testTimeConditionObj], + }, + ], + }).toObj(); + expect(compoundCondition.operator).toEqual('and'); + expect(compoundCondition.operands).toHaveLength(4); + }); +}); diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts index 8a9ec9be7..c56dff621 100644 --- a/test/unit/conditions/condition-set.test.ts +++ b/test/unit/conditions/condition-set.test.ts @@ -1,64 +1,16 @@ -import { Condition, ConditionSet, Operator } from '../../../src/conditions'; +import { Condition, ConditionSet } from '../../../src/conditions'; import { ERC721Balance } from '../../../src/conditions/predefined'; -import { - TEST_CHAIN_ID, - TEST_CONTRACT_ADDR, - TEST_CONTRACT_ADDR_2, -} from '../testVariables'; +import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from '../testVariables'; describe('condition set', () => { - describe('validation', () => { - const cond1 = new ERC721Balance({ - contractAddress: TEST_CONTRACT_ADDR, - }); - const cond2 = new ERC721Balance({ - contractAddress: TEST_CONTRACT_ADDR_2, - }); - - it('validates on a correct set', async () => { - const validSets = [[cond1, Operator.AND, cond2], [cond1]].map( - (set) => new ConditionSet(set) - ); - - validSets.forEach((set) => { - expect(set.validate()).toBeTruthy(); - }); - }); - - it('throws on an invalid set', async () => { - const setWithInvalidLength = new ConditionSet([cond1, cond2]); - expect(() => setWithInvalidLength.validate()).toThrow( - 'conditions must be odd length, every other element being an operator' - ); - - const setWithOperatorInsteadOfComparator = new ConditionSet([ - cond1, - Operator.AND, - Operator.AND, - ]); - expect(() => setWithOperatorInsteadOfComparator.validate()).toThrow( - 'index 2 must be a Condition, got Operator instead' - ); - - const setWithConditionInsteadOfOperator = new ConditionSet([ - cond1, - cond2, - cond1, - ]); - expect(() => setWithConditionInsteadOfOperator.validate()).toThrow( - 'index 1 must be an Operator, got ERC721Balance instead' - ); - }); - }); - describe('serialization', () => { it('serializes to and from json', async () => { - const set = new ConditionSet([ + const set = new ConditionSet( new ERC721Balance({ chain: TEST_CHAIN_ID, contractAddress: TEST_CONTRACT_ADDR, - }), - ]); + }) + ); const setJson = set.toJson(); expect(setJson).toBeDefined(); expect(setJson).toContain('chain'); @@ -68,8 +20,7 @@ describe('condition set', () => { const setFromJson = ConditionSet.fromJSON(setJson); expect(setFromJson).toBeDefined(); - expect(setFromJson.conditions.length).toEqual(1); - expect(setFromJson.conditions[0]).toBeInstanceOf(Condition); // TODO: This should arguably be an ERC721Balance + expect(setFromJson.condition).toBeInstanceOf(Condition); // TODO: This should arguably be an ERC721Balance }); }); }); diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts index b86fb0d0c..958454cd1 100644 --- a/test/unit/conditions/context.test.ts +++ b/test/unit/conditions/context.test.ts @@ -26,7 +26,7 @@ describe('serialization', () => { value: USER_ADDRESS_PARAM, }, }); - const conditionContext = new ConditionSet([rpcCondition]).buildContext( + const conditionContext = new ConditionSet(rpcCondition).buildContext( web3Provider ); const asJson = await conditionContext.toJson(); @@ -48,7 +48,7 @@ describe('context parameters', () => { }, }; const contractCondition = new ContractCondition(contractConditionObj); - const conditionSet = new ConditionSet([contractCondition]); + const conditionSet = new ConditionSet(contractCondition); const conditionContext = conditionSet.buildContext(web3Provider); describe('return value test', () => { @@ -94,9 +94,9 @@ describe('context parameters', () => { ...contractConditionObj, parameters: [USER_ADDRESS_PARAM, customParamKey], }); - const conditionContext = new ConditionSet([ - customContractCondition, - ]).buildContext(web3Provider); + const conditionContext = new ConditionSet( + customContractCondition + ).buildContext(web3Provider); await expect(async () => conditionContext.toObj()).rejects.toThrow( `Missing custom context parameter(s): ${customParamKey}` @@ -108,9 +108,9 @@ describe('context parameters', () => { ...contractConditionObj, parameters: [USER_ADDRESS_PARAM, 100], }); - const conditionContext = new ConditionSet([ - customContractCondition, - ]).buildContext(web3Provider); + const conditionContext = new ConditionSet( + customContractCondition + ).buildContext(web3Provider); const asObj = await conditionContext.toObj(); expect(asObj).toBeDefined(); diff --git a/test/unit/conditions/operator.test.ts b/test/unit/conditions/operator.test.ts deleted file mode 100644 index c6c4d3ed6..000000000 --- a/test/unit/conditions/operator.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Operator } from '../../../src/conditions'; - -describe('validate', () => { - it('accepts a valid operator', () => { - const op = new Operator('or'); - expect(op.operator).toEqual('or'); - }); - - it('rejects an invalid operator', () => { - expect(() => new Operator('not-an-operator')).toThrow( - '"not-an-operator" must be one of [and, or]' - ); - }); -}); diff --git a/test/unit/pre-strategy.test.ts b/test/unit/pre-strategy.test.ts index 0879ce9b4..3b15ba0eb 100644 --- a/test/unit/pre-strategy.test.ts +++ b/test/unit/pre-strategy.test.ts @@ -37,7 +37,7 @@ const ownsNFT = new ERC721Ownership({ parameters: [3591], chain: 5, }); -const conditionSet = new ConditionSet([ownsNFT]); +const conditionSet = new ConditionSet(ownsNFT); const mockedUrsulas = fakeUrsulas().slice(0, 3); const makePreStrategy = async () => { diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index c199a3043..a95d99be0 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -19,6 +19,16 @@ export const testReturnValueTest = { value: '100', }; +export const testTimeConditionObj = { + returnValueTest: { + index: 0, + comparator: '>', + value: '100', + }, + method: 'blocktime', + chain: 5, +}; + export const testRpcConditionObj = { chain: TEST_CHAIN_ID, method: 'eth_getBalance', From 8052d3d715b2cfc21229734389970cea291c5ac6 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 15 Jun 2023 12:39:59 -0400 Subject: [PATCH 72/98] Remove class metadata entry from serialized conditions since predefined classes are only convenience objects, but underlying they are still contract conditions etc. --- src/conditions/base/condition.ts | 2 - src/conditions/base/time.ts | 2 +- src/conditions/condition-set.ts | 34 ++++- test/unit/conditions/base/condition.test.ts | 2 - test/unit/conditions/base/contract.test.ts | 138 ++++++++++++++++++ test/unit/conditions/base/rpc.test.ts | 1 - test/unit/conditions/base/time.test.ts | 1 - .../conditions/compound-condition.test.ts | 13 +- 8 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 test/unit/conditions/base/contract.test.ts diff --git a/src/conditions/base/condition.ts b/src/conditions/base/condition.ts index 16c8c95ec..f4caa12c2 100644 --- a/src/conditions/base/condition.ts +++ b/src/conditions/base/condition.ts @@ -26,7 +26,6 @@ export class Condition { } return { ...value, - _class: this.constructor.name, }; } @@ -36,7 +35,6 @@ export class Condition { this: new (...args: any[]) => T, obj: Map ): T { - delete obj._class; return new this(obj); } diff --git a/src/conditions/base/time.ts b/src/conditions/base/time.ts index 535c6b9a9..57ee66f69 100644 --- a/src/conditions/base/time.ts +++ b/src/conditions/base/time.ts @@ -4,7 +4,7 @@ import { omit } from '../../utils'; import { RpcCondition, rpcConditionSchema } from './rpc'; -const BLOCKTIME_METHOD = 'blocktime'; +export const BLOCKTIME_METHOD = 'blocktime'; export const timeConditionSchema: Record = { // TimeCondition is an RpcCondition with the method set to 'blocktime' and no parameters diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index de61e2022..12a1e90d6 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -3,7 +3,14 @@ import { ethers } from 'ethers'; import { toJSON } from '../utils'; -import { Condition } from './base'; +import { + Condition, + ContractCondition, + RpcCondition, + TimeCondition, +} from './base'; +import { BLOCKTIME_METHOD } from './base/time'; +import { CompoundCondition } from './compound-condition'; import { ConditionContext } from './context'; export type ConditionSetJSON = { @@ -15,12 +22,31 @@ export class ConditionSet { public toObj(): ConditionSetJSON { // TODO add version here - const condition_json = this.condition.toObj(); - return { condition: condition_json }; + const conditionData = this.condition.toObj(); + return { condition: conditionData }; } public static fromObj(obj: ConditionSetJSON): ConditionSet { - const condition = Condition.fromObj(obj.condition); + // version specific logic can go here + const underlyingConditionData = obj.condition; + let condition = undefined; + if (underlyingConditionData.operator) { + condition = new CompoundCondition(underlyingConditionData); + } else if (underlyingConditionData.method) { + if (underlyingConditionData.method == BLOCKTIME_METHOD) { + condition = new TimeCondition(underlyingConditionData); + } else if (underlyingConditionData.contractAddress) { + condition = new ContractCondition(underlyingConditionData); + } else if ( + (underlyingConditionData.method as string).startsWith('eth_') + ) { + condition = new RpcCondition(underlyingConditionData); + } + } + if (condition == undefined) { + throw `Invalid condition: unrecognized condition data`; + } + return new ConditionSet(condition); } diff --git a/test/unit/conditions/base/condition.test.ts b/test/unit/conditions/base/condition.test.ts index dc9b310ab..2a567434c 100644 --- a/test/unit/conditions/base/condition.test.ts +++ b/test/unit/conditions/base/condition.test.ts @@ -50,7 +50,6 @@ describe('serialization', () => { const contract = new ContractCondition(testContractConditionObj); expect(contract.toObj()).toEqual({ ...testContractConditionObj, - _class: 'ContractCondition', }); }); @@ -59,7 +58,6 @@ describe('serialization', () => { expect(contract.toObj()).toEqual({ ...contract.defaults, ...testContractConditionObj, - _class: 'ERC721Ownership', }); }); }); diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts new file mode 100644 index 000000000..66f03d172 --- /dev/null +++ b/test/unit/conditions/base/contract.test.ts @@ -0,0 +1,138 @@ +import { ContractCondition } from '../../../../src/conditions/base'; +import { testContractConditionObj } from '../../testVariables'; + +describe('validation', () => { + it('accepts on a valid schema', () => { + const contract = new ContractCondition(testContractConditionObj); + expect(contract.toObj()).toEqual({ + ...testContractConditionObj, + }); + }); + + it('rejects an invalid schema', () => { + const badContractCondition = { + ...testContractConditionObj, + // Intentionally removing `contractAddress` + contractAddress: undefined, + }; + const badEvm = new ContractCondition(badContractCondition); + expect(() => badEvm.toObj()).toThrow( + 'Invalid condition: "contractAddress" is required' + ); + + const { error } = badEvm.validate(badContractCondition); + expect(error?.message).toEqual('"contractAddress" is required'); + }); +}); + +describe('accepts either standardContractType or functionAbi but not both or none', () => { + const standardContractType = 'ERC20'; + const functionAbi = { fake_function_abi: true }; + + it('accepts standardContractType', () => { + const conditionObj = { + ...testContractConditionObj, + standardContractType, + functionAbi: undefined, + }; + const contractCondition = new ContractCondition(conditionObj); + expect(contractCondition.toObj()).toEqual({ + ...conditionObj, + }); + }); + + it('accepts functionAbi', () => { + const conditionObj = { + ...testContractConditionObj, + functionAbi, + standardContractType: undefined, + }; + const contractCondition = new ContractCondition(conditionObj); + expect(contractCondition.toObj()).toEqual({ + ...conditionObj, + }); + }); + + it('rejects both', () => { + const conditionObj = { + ...testContractConditionObj, + standardContractType, + functionAbi, + }; + const contractCondition = new ContractCondition(conditionObj); + expect(() => contractCondition.toObj()).toThrow( + '"value" contains a conflict between exclusive peers [standardContractType, functionAbi]' + ); + }); + + it('rejects none', () => { + const conditionObj = { + ...testContractConditionObj, + standardContractType: undefined, + functionAbi: undefined, + }; + const contractCondition = new ContractCondition(conditionObj); + expect(() => contractCondition.toObj()).toThrow( + '"value" must contain at least one of [standardContractType, functionAbi]' + ); + }); +}); + +// TODO(#124) +// it('accepts custom parameters in function abi methods', async () => { +// throw new Error('Not implemented'); +// }); + +// TODO(#124) +// describe('supports custom function abi', () => { +// const fakeFunctionAbi = { +// name: 'myFunction', +// type: 'function', +// inputs: [ +// { +// name: 'account', +// type: 'address', +// }, +// { +// name: 'myCustomParam', +// type: 'uint256', +// }, +// ], +// outputs: [ +// { +// name: 'someValue', +// type: 'uint256', +// }, +// ], +// }; +// const contractConditionObj = { +// ...testContractConditionObj, +// functionAbi: fakeFunctionAbi, +// method: 'myFunction', +// parameters: [USER_ADDRESS_PARAM, ':customParam'], +// returnValueTest: { +// index: 0, +// comparator: '==', +// value: USER_ADDRESS_PARAM, +// }, +// }; +// const contractCondition = new ContractCondition(contractConditionObj); +// const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); +// const conditionSet = new ConditionSet([contractCondition]); +// const conditionContext = new ConditionContext( +// conditionSet.toWASMConditions(), +// web3Provider +// ); +// const myCustomParam = ':customParam'; +// const customParams: Record = {}; +// customParams[myCustomParam] = 1234; +// +// it('accepts custom function abi', async () => { +// const asJson = await conditionContext +// .withCustomParams(customParams) +// .toJson(); +// expect(asJson).toBeDefined(); +// expect(asJson).toContain(USER_ADDRESS_PARAM); +// expect(asJson).toContain(myCustomParam); +// }); +// }); diff --git a/test/unit/conditions/base/rpc.test.ts b/test/unit/conditions/base/rpc.test.ts index 8803854cf..ccd65f63a 100644 --- a/test/unit/conditions/base/rpc.test.ts +++ b/test/unit/conditions/base/rpc.test.ts @@ -6,7 +6,6 @@ describe('validation', () => { const rpc = new RpcCondition(testRpcConditionObj); expect(rpc.toObj()).toEqual({ ...testRpcConditionObj, - _class: 'RpcCondition', }); }); diff --git a/test/unit/conditions/base/time.test.ts b/test/unit/conditions/base/time.test.ts index 05b459dfc..a043c8eba 100644 --- a/test/unit/conditions/base/time.test.ts +++ b/test/unit/conditions/base/time.test.ts @@ -16,7 +16,6 @@ describe('validation', () => { returnValueTest, chain: 5, method: 'blocktime', - _class: 'TimeCondition', }); }); diff --git a/test/unit/conditions/compound-condition.test.ts b/test/unit/conditions/compound-condition.test.ts index 433ed7304..396b6fddf 100644 --- a/test/unit/conditions/compound-condition.test.ts +++ b/test/unit/conditions/compound-condition.test.ts @@ -1,4 +1,5 @@ import { CompoundCondition } from '../../../src/conditions'; +import { ERC721Ownership } from '../../../src/conditions/predefined/erc721'; import { testContractConditionObj, testRpcConditionObj, @@ -6,15 +7,21 @@ import { } from '../testVariables'; describe('validate', () => { + const ownsBufficornNFT = ERC721Ownership.fromObj({ + contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', + parameters: [3591], + chain: 5, + }).toObj(); + it('accepts or operator', () => { const orCondition = new CompoundCondition({ operator: 'or', - operands: [testRpcConditionObj, testTimeConditionObj], + operands: [ownsBufficornNFT, testTimeConditionObj], }).toObj(); expect(orCondition.operator).toEqual('or'); expect(orCondition.operands).toEqual([ - testRpcConditionObj, + ownsBufficornNFT, testTimeConditionObj, ]); }); @@ -68,7 +75,7 @@ describe('validate', () => { testRpcConditionObj, { operator: 'or', - operands: [testRpcConditionObj, testTimeConditionObj], + operands: [ownsBufficornNFT, testContractConditionObj], }, ], }).toObj(); From 90f87ba104c2e78579bb5017956ded4420aaa490 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 15 Jun 2023 13:35:11 -0400 Subject: [PATCH 73/98] Separate schema record from schema itself so that the schema objects can be used for compound conditions. Update some tests. --- src/conditions/base/contract.ts | 18 +++++----- src/conditions/base/rpc.ts | 6 ++-- src/conditions/base/time.ts | 10 +++--- src/conditions/compound-condition.ts | 15 ++++----- test/unit/conditions/base/contract.test.ts | 18 +++++++++- test/unit/conditions/condition-set.test.ts | 38 +++++++++++++--------- test/unit/testVariables.ts | 1 + 7 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/conditions/base/contract.ts b/src/conditions/base/contract.ts index f1bea2584..83a13b6ae 100644 --- a/src/conditions/base/contract.ts +++ b/src/conditions/base/contract.ts @@ -2,12 +2,12 @@ import Joi from 'joi'; import { ETH_ADDRESS_REGEXP } from '../const'; -import { RpcCondition, rpcConditionSchema } from './rpc'; +import { RpcCondition, rpcConditionRecord } from './rpc'; export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; const functionAbiVariable = Joi.object({ - internalType: Joi.string().required(), + internalType: Joi.string(), // TODO is this needed? name: Joi.string().required(), type: Joi.string().required(), }); @@ -18,7 +18,7 @@ const functionAbiSchema = Joi.object({ inputs: Joi.array().items(functionAbiVariable), outputs: Joi.array().items(functionAbiVariable), // TODO: Should we restrict this to 'view'? - // stateMutability: Joi.string().valid('view').required(), + stateMutability: Joi.string(), }).custom((functionAbi, helper) => { // Validate method name const method = helper.state.ancestors[0].method; @@ -39,8 +39,8 @@ const functionAbiSchema = Joi.object({ return functionAbi; }); -export const contractMethodSchema: Record = { - ...rpcConditionSchema, +export const contractConditionRecord: Record = { + ...rpcConditionRecord, contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), standardContractType: Joi.string() .valid(...STANDARD_CONTRACT_TYPES) @@ -50,8 +50,10 @@ export const contractMethodSchema: Record = { parameters: Joi.array().required(), }; +export const contractConditionSchema = Joi.object(contractConditionRecord) + // At most one of these keys needs to be present + .xor('standardContractType', 'functionAbi'); + export class ContractCondition extends RpcCondition { - public readonly schema = Joi.object(contractMethodSchema) - // At most one of these keys needs to be present - .xor('standardContractType', 'functionAbi'); + public readonly schema = contractConditionSchema; } diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index fe73c20af..2eb7d2202 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -18,7 +18,7 @@ const makeParameters = () => })), }); -export const rpcConditionSchema: Record = { +export const rpcConditionRecord = { chain: Joi.number() .valid(...SUPPORTED_CHAINS) .required(), @@ -29,6 +29,8 @@ export const rpcConditionSchema: Record = { returnValueTest: returnValueTestSchema.required(), }; +export const rpcConditionSchema = Joi.object(rpcConditionRecord); + export class RpcCondition extends Condition { - public readonly schema = Joi.object(rpcConditionSchema); + public readonly schema = rpcConditionSchema; } diff --git a/src/conditions/base/time.ts b/src/conditions/base/time.ts index 57ee66f69..4f4671dd0 100644 --- a/src/conditions/base/time.ts +++ b/src/conditions/base/time.ts @@ -2,20 +2,22 @@ import Joi from 'joi'; import { omit } from '../../utils'; -import { RpcCondition, rpcConditionSchema } from './rpc'; +import { RpcCondition, rpcConditionRecord } from './rpc'; export const BLOCKTIME_METHOD = 'blocktime'; -export const timeConditionSchema: Record = { +export const timeConditionRecord: Record = { // TimeCondition is an RpcCondition with the method set to 'blocktime' and no parameters - ...omit(rpcConditionSchema, ['parameters']), + ...omit(rpcConditionRecord, ['parameters']), method: Joi.string().valid(BLOCKTIME_METHOD).required(), }; +export const timeConditionSchema = Joi.object(timeConditionRecord); + export class TimeCondition extends RpcCondition { public readonly defaults: Record = { method: BLOCKTIME_METHOD, }; - public readonly schema = Joi.object(timeConditionSchema); + public readonly schema = timeConditionSchema; } diff --git a/src/conditions/compound-condition.ts b/src/conditions/compound-condition.ts index ce050c04f..fe0249677 100644 --- a/src/conditions/compound-condition.ts +++ b/src/conditions/compound-condition.ts @@ -1,7 +1,7 @@ import Joi from 'joi'; import { Condition } from './base/condition'; -import { contractMethodSchema } from './base/contract'; +import { contractConditionSchema } from './base/contract'; import { rpcConditionSchema } from './base/rpc'; import { timeConditionSchema } from './base/time'; @@ -10,19 +10,16 @@ const AND_OPERATOR = 'and'; const LOGICAL_OPERATORS = [AND_OPERATOR, OR_OPERATOR]; -export const compoundCondition = Joi.object({ +export const compoundConditionSchema = Joi.object({ operator: Joi.string() .valid(...LOGICAL_OPERATORS) .required(), operands: Joi.array() .min(2) .items( - Joi.object(rpcConditionSchema), - Joi.object(timeConditionSchema), - Joi.object(contractMethodSchema).xor( - 'standardContractType', - 'functionAbi' - ), + rpcConditionSchema, + timeConditionSchema, + contractConditionSchema, Joi.link('#compoundCondition') ) .required() @@ -30,5 +27,5 @@ export const compoundCondition = Joi.object({ }).id('compoundCondition'); export class CompoundCondition extends Condition { - public readonly schema = compoundCondition; + public readonly schema = compoundConditionSchema; } diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts index 66f03d172..26b25f4c5 100644 --- a/test/unit/conditions/base/contract.test.ts +++ b/test/unit/conditions/base/contract.test.ts @@ -27,7 +27,23 @@ describe('validation', () => { describe('accepts either standardContractType or functionAbi but not both or none', () => { const standardContractType = 'ERC20'; - const functionAbi = { fake_function_abi: true }; + const functionAbi = { + inputs: [ + { + name: '_owner', + type: 'address', + }, + ], + name: 'balanceOf', + outputs: [ + { + name: 'balance', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }; it('accepts standardContractType', () => { const conditionObj = { diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts index c56dff621..2d6d85fca 100644 --- a/test/unit/conditions/condition-set.test.ts +++ b/test/unit/conditions/condition-set.test.ts @@ -1,26 +1,32 @@ -import { Condition, ConditionSet } from '../../../src/conditions'; +import { ConditionSet } from '../../../src/conditions'; +import { ContractCondition } from '../../../src/conditions/base'; import { ERC721Balance } from '../../../src/conditions/predefined'; import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from '../testVariables'; describe('condition set', () => { describe('serialization', () => { + const condition_set = new ConditionSet( + new ERC721Balance({ + chain: TEST_CHAIN_ID, + contractAddress: TEST_CONTRACT_ADDR, + }) + ); + it('serializes to and from json', async () => { - const set = new ConditionSet( - new ERC721Balance({ - chain: TEST_CHAIN_ID, - contractAddress: TEST_CONTRACT_ADDR, - }) - ); - const setJson = set.toJson(); - expect(setJson).toBeDefined(); - expect(setJson).toContain('chain'); - expect(setJson).toContain(TEST_CHAIN_ID.toString()); - expect(setJson).toContain('contractAddress'); - expect(setJson).toContain(TEST_CONTRACT_ADDR.toString()); + const conditionSetJson = condition_set.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('contractAddress'); + expect(conditionSetJson).toContain(TEST_CONTRACT_ADDR.toString()); + expect(conditionSetJson).toContain('standardContractType'); + expect(conditionSetJson).toContain('ERC721'); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain('balanceOf'); - const setFromJson = ConditionSet.fromJSON(setJson); - expect(setFromJson).toBeDefined(); - expect(setFromJson.condition).toBeInstanceOf(Condition); // TODO: This should arguably be an ERC721Balance + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); }); }); }); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index a95d99be0..ef943513a 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -67,4 +67,5 @@ export const testFunctionAbi = { type: 'uint256', }, ], + stateMutability: 'view', }; From 63c22f640eabee11daefcc835621c625b6139536 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 15 Jun 2023 14:28:04 -0400 Subject: [PATCH 74/98] Compare ConditionSet objects correctly using objectEquals(). --- src/conditions/condition-set.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index 12a1e90d6..ac3ed6bbc 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -1,7 +1,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { toJSON } from '../utils'; +import { objectEquals, toJSON } from '../utils'; import { Condition, @@ -69,6 +69,6 @@ export class ConditionSet { } public equals(other: ConditionSet): boolean { - return this.condition.equals(other.condition); + return objectEquals(this.condition.toObj(), other.condition.toObj()); } } From 68d533d669c7e07e67792bd3e9c0cb3aca8889a9 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 16 Jun 2023 13:03:36 -0400 Subject: [PATCH 75/98] Improve condition deserialization code. Additional tests for ConditionSet. --- src/conditions/condition-set.ts | 31 ++-- test/unit/conditions/condition-set.test.ts | 182 ++++++++++++++++++--- 2 files changed, 177 insertions(+), 36 deletions(-) diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index ac3ed6bbc..674a9d5b7 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -29,25 +29,26 @@ export class ConditionSet { public static fromObj(obj: ConditionSetJSON): ConditionSet { // version specific logic can go here const underlyingConditionData = obj.condition; - let condition = undefined; + if (underlyingConditionData.operator) { - condition = new CompoundCondition(underlyingConditionData); - } else if (underlyingConditionData.method) { - if (underlyingConditionData.method == BLOCKTIME_METHOD) { - condition = new TimeCondition(underlyingConditionData); - } else if (underlyingConditionData.contractAddress) { - condition = new ContractCondition(underlyingConditionData); - } else if ( - (underlyingConditionData.method as string).startsWith('eth_') - ) { - condition = new RpcCondition(underlyingConditionData); - } + return new ConditionSet(new CompoundCondition(underlyingConditionData)); } - if (condition == undefined) { - throw `Invalid condition: unrecognized condition data`; + + if (underlyingConditionData.method) { + if (underlyingConditionData.method === BLOCKTIME_METHOD) { + return new ConditionSet(new TimeCondition(underlyingConditionData)); + } + + if (underlyingConditionData.contractAddress) { + return new ConditionSet(new ContractCondition(underlyingConditionData)); + } + + if ((underlyingConditionData.method as string).startsWith('eth_')) { + return new ConditionSet(new RpcCondition(underlyingConditionData)); + } } - return new ConditionSet(condition); + throw new Error('Invalid condition: unrecognized condition data'); } public toJson(): string { diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts index 2d6d85fca..fef69ab9c 100644 --- a/test/unit/conditions/condition-set.test.ts +++ b/test/unit/conditions/condition-set.test.ts @@ -1,32 +1,172 @@ -import { ConditionSet } from '../../../src/conditions'; -import { ContractCondition } from '../../../src/conditions/base'; +import { CompoundCondition, ConditionSet } from '../../../src/conditions'; +import { + ContractCondition, + RpcCondition, + TimeCondition, +} from '../../../src/conditions/base'; import { ERC721Balance } from '../../../src/conditions/predefined'; import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from '../testVariables'; +import { + testContractConditionObj, + testRpcConditionObj, + testTimeConditionObj, +} from '../testVariables'; describe('condition set', () => { - describe('serialization', () => { - const condition_set = new ConditionSet( - new ERC721Balance({ - chain: TEST_CHAIN_ID, - contractAddress: TEST_CONTRACT_ADDR, - }) - ); + const erc721BalanceCondition = new ERC721Balance({ + chain: TEST_CHAIN_ID, + contractAddress: TEST_CONTRACT_ADDR, + }); + const contractCondition = new ContractCondition(testContractConditionObj); + const rpcCondition = new RpcCondition(testRpcConditionObj); + const timeCondition = new TimeCondition(testTimeConditionObj); + const compoundCondition = new CompoundCondition({ + operator: 'and', + operands: [ + testContractConditionObj, + testTimeConditionObj, + testRpcConditionObj, + { + operator: 'or', + operands: [testTimeConditionObj, testContractConditionObj], + }, + ], + }); - it('serializes to and from json', async () => { - const conditionSetJson = condition_set.toJson(); + describe('serialization', () => { + it.each([ + erc721BalanceCondition, + contractCondition, + rpcCondition, + timeCondition, + compoundCondition, + ])('serializes to and from json', async (condition) => { + const conditionSet = new ConditionSet(condition); + const conditionSetJson = conditionSet.toJson(); expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('contractAddress'); - expect(conditionSetJson).toContain(TEST_CONTRACT_ADDR.toString()); - expect(conditionSetJson).toContain('standardContractType'); - expect(conditionSetJson).toContain('ERC721'); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain('balanceOf'); - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); + expect(conditionSetFromJson.equals(conditionSetFromJson)).toBeTruthy(); }); }); + + it('erc721 condition serialization', async () => { + const conditionSet = new ConditionSet(erc721BalanceCondition); + + const erc721BalanceConditionObj = erc721BalanceCondition.toObj(); + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('contractAddress'); + expect(conditionSetJson).toContain( + erc721BalanceConditionObj.contractAddress + ); + expect(conditionSetJson).toContain('standardContractType'); + expect(conditionSetJson).toContain('ERC721'); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(erc721BalanceConditionObj.method); + expect(conditionSetJson).toContain('returnValueTest'); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); + }); + + it('contract condition serialization', async () => { + const conditionSet = new ConditionSet(contractCondition); + + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('contractAddress'); + expect(conditionSetJson).toContain( + testContractConditionObj.contractAddress + ); + expect(conditionSetJson).toContain('standardContractType'); + expect(conditionSetJson).toContain( + testContractConditionObj.standardContractType + ); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(testContractConditionObj.method); + expect(conditionSetJson).toContain('parameters'); + expect(conditionSetJson).toContain(testContractConditionObj.parameters[0]); + expect(conditionSetJson).toContain('returnValueTest'); + + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); + }); + + it('time condition serialization', async () => { + const conditionSet = new ConditionSet(timeCondition); + + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(testTimeConditionObj.method); + expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).not.toContain('parameters'); + expect(conditionSetJson).not.toContain('contractAddress'); + expect(conditionSetJson).not.toContain('standardContractType'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(TimeCondition); + }); + + it('rpc condition serialization', async () => { + const conditionSet = new ConditionSet(rpcCondition); + + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(testRpcConditionObj.method); + expect(conditionSetJson).toContain('parameters'); + expect(conditionSetJson).toContain(testRpcConditionObj.parameters[0]); + expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).not.toContain('contractAddress'); + expect(conditionSetJson).not.toContain('standardContractType'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(RpcCondition); + }); + + it('compound condition serialization', async () => { + const conditionSet = new ConditionSet(compoundCondition); + const compoundConditionObj = compoundCondition.toObj(); + + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toContain('operator'); + expect(conditionSetJson).toContain(compoundConditionObj.operator); + expect(conditionSetJson).toContain('operands'); + + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(testRpcConditionObj.method); + expect(conditionSetJson).toContain(testTimeConditionObj.method); + expect(conditionSetJson).toContain(testContractConditionObj.method); + expect(conditionSetJson).toContain('parameters'); + expect(conditionSetJson).toContain(testRpcConditionObj.parameters[0]); + expect(conditionSetJson).toContain(testContractConditionObj.parameters[0]); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(CompoundCondition); + }); }); From 86a69f67f669cdfb839c66288eeea59a843f8611 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 16 Jun 2023 13:41:08 -0400 Subject: [PATCH 76/98] Additional testing for invalid conditions whose type cannot be determined. --- test/unit/conditions/condition-set.test.ts | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts index fef69ab9c..54b363ce6 100644 --- a/test/unit/conditions/condition-set.test.ts +++ b/test/unit/conditions/condition-set.test.ts @@ -50,6 +50,40 @@ describe('condition set', () => { }); }); + it.each([ + // no "operator" nor "method" value + { + randoKey: 'randoValue', + otherKey: 'otherValue', + }, + // invalid "method" and no "contractAddress" + { + method: 'doWhatIWant', + returnValueTest: { + index: 0, + comparator: '>', + value: '100', + }, + chain: 5, + }, + // condition with wrong method "method" and no contract address + { + ...testTimeConditionObj, + method: 'doWhatIWant', + }, + // rpc condition (no contract address) with disallowed method + { + ...testRpcConditionObj, + method: 'isPolicyActive', + }, + ])("can't determine condition type", async (invalidCondition) => { + expect(() => { + ConditionSet.fromObj({ + condition: invalidCondition, + }); + }).toThrow('unrecognized condition data'); + }); + it('erc721 condition serialization', async () => { const conditionSet = new ConditionSet(erc721BalanceCondition); From e810f422e4872c9009dbf5a03a03ae84b9947c81 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 16 Jun 2023 13:52:15 -0400 Subject: [PATCH 77/98] Add tests for contract condition with function abi defined. --- test/unit/conditions/condition-set.test.ts | 74 ++++++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts index 54b363ce6..bc3104008 100644 --- a/test/unit/conditions/condition-set.test.ts +++ b/test/unit/conditions/condition-set.test.ts @@ -4,8 +4,14 @@ import { RpcCondition, TimeCondition, } from '../../../src/conditions/base'; +import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; import { ERC721Balance } from '../../../src/conditions/predefined'; -import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR } from '../testVariables'; +import { + TEST_CHAIN_ID, + TEST_CONTRACT_ADDR, + testFunctionAbi, + testReturnValueTest, +} from '../testVariables'; import { testContractConditionObj, testRpcConditionObj, @@ -17,7 +23,26 @@ describe('condition set', () => { chain: TEST_CHAIN_ID, contractAddress: TEST_CONTRACT_ADDR, }); - const contractCondition = new ContractCondition(testContractConditionObj); + + const contractConditionNoAbi = new ContractCondition( + testContractConditionObj + ); + + const customParamKey = ':customParam'; + const contractConditionWithAbiObj = { + ...testContractConditionObj, + standardContractType: undefined, + functionAbi: testFunctionAbi, + method: testFunctionAbi.name, + parameters: [USER_ADDRESS_PARAM, customParamKey], + returnValueTest: { + ...testReturnValueTest, + }, + }; + const contractConditionWithAbi = new ContractCondition( + contractConditionWithAbiObj + ); + const rpcCondition = new RpcCondition(testRpcConditionObj); const timeCondition = new TimeCondition(testTimeConditionObj); const compoundCondition = new CompoundCondition({ @@ -36,7 +61,8 @@ describe('condition set', () => { describe('serialization', () => { it.each([ erc721BalanceCondition, - contractCondition, + contractConditionNoAbi, + contractConditionWithAbi, rpcCondition, timeCondition, compoundCondition, @@ -102,13 +128,17 @@ describe('condition set', () => { expect(conditionSetJson).toContain(erc721BalanceConditionObj.method); expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).not.toContain('functionAbi'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); expect(conditionSetFromJson).toBeDefined(); expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); }); - it('contract condition serialization', async () => { - const conditionSet = new ConditionSet(contractCondition); + it('contract condition no abi serialization', async () => { + const conditionSet = new ConditionSet(contractConditionNoAbi); const conditionSetJson = conditionSet.toJson(); expect(conditionSetJson).toBeDefined(); @@ -127,7 +157,39 @@ describe('condition set', () => { expect(conditionSetJson).toContain('parameters'); expect(conditionSetJson).toContain(testContractConditionObj.parameters[0]); expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).not.toContain('functionAbi'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); + }); + + it('contract condition with abi serialization', async () => { + const conditionSet = new ConditionSet(contractConditionWithAbi); + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('contractAddress'); + expect(conditionSetJson).toContain( + contractConditionWithAbiObj.contractAddress + ); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(contractConditionWithAbiObj.method); + expect(conditionSetJson).toContain('parameters'); + expect(conditionSetJson).toContain( + contractConditionWithAbiObj.parameters[0] + ); + expect(conditionSetJson).toContain( + contractConditionWithAbiObj.parameters[1] + ); + expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).toContain('functionAbi'); + + expect(conditionSetJson).not.toContain('standardContractType'); expect(conditionSetJson).not.toContain('operator'); expect(conditionSetJson).not.toContain('operands'); @@ -149,6 +211,7 @@ describe('condition set', () => { expect(conditionSetJson).not.toContain('parameters'); expect(conditionSetJson).not.toContain('contractAddress'); expect(conditionSetJson).not.toContain('standardContractType'); + expect(conditionSetJson).not.toContain('functionAbi'); expect(conditionSetJson).not.toContain('operator'); expect(conditionSetJson).not.toContain('operands'); @@ -171,6 +234,7 @@ describe('condition set', () => { expect(conditionSetJson).toContain('returnValueTest'); expect(conditionSetJson).not.toContain('contractAddress'); expect(conditionSetJson).not.toContain('standardContractType'); + expect(conditionSetJson).not.toContain('functionAbi'); expect(conditionSetJson).not.toContain('operator'); expect(conditionSetJson).not.toContain('operands'); From ca14ed0d7b832f6f5cdd59b4169096c265cb71e2 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 16 Jun 2023 15:06:53 -0400 Subject: [PATCH 78/98] Uncomment previously commented tests regarding the use of functionAbi for contract condition. --- test/unit/conditions/base/contract.test.ts | 111 ++++++++++----------- 1 file changed, 54 insertions(+), 57 deletions(-) diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts index 26b25f4c5..3be6bdcd6 100644 --- a/test/unit/conditions/base/contract.test.ts +++ b/test/unit/conditions/base/contract.test.ts @@ -1,4 +1,9 @@ +import { SecretKey } from '@nucypher/nucypher-core'; + +import { ConditionSet, CustomContextParam } from '../../../../src/conditions'; import { ContractCondition } from '../../../../src/conditions/base'; +import { USER_ADDRESS_PARAM } from '../../../../src/conditions/const'; +import { fakeWeb3Provider } from '../../../utils'; import { testContractConditionObj } from '../../testVariables'; describe('validation', () => { @@ -94,61 +99,53 @@ describe('accepts either standardContractType or functionAbi but not both or non }); }); -// TODO(#124) -// it('accepts custom parameters in function abi methods', async () => { -// throw new Error('Not implemented'); -// }); +describe('supports custom function abi', () => { + const fakeFunctionAbi = { + name: 'myFunction', + type: 'function', + inputs: [ + { + name: 'account', + type: 'address', + }, + { + name: 'myCustomParam', + type: 'uint256', + }, + ], + outputs: [ + { + name: 'someValue', + type: 'uint256', + }, + ], + }; + const contractConditionObj = { + ...testContractConditionObj, + standardContractType: undefined, + functionAbi: fakeFunctionAbi, + method: 'myFunction', + parameters: [USER_ADDRESS_PARAM, ':customParam'], + returnValueTest: { + index: 0, + comparator: '==', + value: USER_ADDRESS_PARAM, + }, + }; + const contractCondition = new ContractCondition(contractConditionObj); + const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); + const conditionSet = new ConditionSet(contractCondition); + const conditionContext = conditionSet.buildContext(web3Provider); + const myCustomParam = ':customParam'; + const customParams: Record = {}; + customParams[myCustomParam] = 1234; -// TODO(#124) -// describe('supports custom function abi', () => { -// const fakeFunctionAbi = { -// name: 'myFunction', -// type: 'function', -// inputs: [ -// { -// name: 'account', -// type: 'address', -// }, -// { -// name: 'myCustomParam', -// type: 'uint256', -// }, -// ], -// outputs: [ -// { -// name: 'someValue', -// type: 'uint256', -// }, -// ], -// }; -// const contractConditionObj = { -// ...testContractConditionObj, -// functionAbi: fakeFunctionAbi, -// method: 'myFunction', -// parameters: [USER_ADDRESS_PARAM, ':customParam'], -// returnValueTest: { -// index: 0, -// comparator: '==', -// value: USER_ADDRESS_PARAM, -// }, -// }; -// const contractCondition = new ContractCondition(contractConditionObj); -// const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); -// const conditionSet = new ConditionSet([contractCondition]); -// const conditionContext = new ConditionContext( -// conditionSet.toWASMConditions(), -// web3Provider -// ); -// const myCustomParam = ':customParam'; -// const customParams: Record = {}; -// customParams[myCustomParam] = 1234; -// -// it('accepts custom function abi', async () => { -// const asJson = await conditionContext -// .withCustomParams(customParams) -// .toJson(); -// expect(asJson).toBeDefined(); -// expect(asJson).toContain(USER_ADDRESS_PARAM); -// expect(asJson).toContain(myCustomParam); -// }); -// }); + it('accepts custom function abi', async () => { + const asJson = await conditionContext + .withCustomParams(customParams) + .toJson(); + expect(asJson).toBeDefined(); + expect(asJson).toContain(USER_ADDRESS_PARAM); + expect(asJson).toContain(myCustomParam); + }); +}); From 5ea1745850274f2cf22b3e15ef740d594c7b2d21 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 16 Jun 2023 14:47:15 -0400 Subject: [PATCH 79/98] Add version to condition data. Rename schema package to return-value package. --- .../base/{schema.ts => return-value.ts} | 0 src/conditions/base/rpc.ts | 5 +- src/conditions/condition-set.ts | 42 ++++++++++++--- test/unit/conditions/condition-set.test.ts | 52 +++++++++++++------ 4 files changed, 77 insertions(+), 22 deletions(-) rename src/conditions/base/{schema.ts => return-value.ts} (100%) diff --git a/src/conditions/base/schema.ts b/src/conditions/base/return-value.ts similarity index 100% rename from src/conditions/base/schema.ts rename to src/conditions/base/return-value.ts diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 2eb7d2202..27ed5f0b7 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -3,7 +3,10 @@ import Joi from 'joi'; import { SUPPORTED_CHAINS } from '../const'; import { Condition } from './condition'; -import { ethAddressOrUserAddressSchema, returnValueTestSchema } from './schema'; +import { + ethAddressOrUserAddressSchema, + returnValueTestSchema, +} from './return-value'; const rpcMethodSchemas: Record = { eth_getBalance: Joi.array().items(ethAddressOrUserAddressSchema).required(), diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index 674a9d5b7..1bc406338 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -14,37 +14,67 @@ import { CompoundCondition } from './compound-condition'; import { ConditionContext } from './context'; export type ConditionSetJSON = { + version: string; condition: Record; }; export class ConditionSet { - constructor(public readonly condition: Condition) {} + static VERSION = '1.0.0'; + + constructor( + public readonly condition: Condition, + public readonly version: string = ConditionSet.VERSION + ) {} public toObj(): ConditionSetJSON { // TODO add version here const conditionData = this.condition.toObj(); - return { condition: conditionData }; + return { + version: this.version, + condition: conditionData, + }; } public static fromObj(obj: ConditionSetJSON): ConditionSet { + const version = obj.version; // version specific logic can go here + const receivedMajorVersion = version.split('.')[0]; + const currentMajorVersion = ConditionSet.VERSION.split('.')[0]; + if (receivedMajorVersion > currentMajorVersion) { + throw new Error( + `Version provided, ${version}, is incompatible with current version, ${ConditionSet.VERSION}` + ); + } + const underlyingConditionData = obj.condition; if (underlyingConditionData.operator) { - return new ConditionSet(new CompoundCondition(underlyingConditionData)); + return new ConditionSet( + new CompoundCondition(underlyingConditionData), + version + ); } if (underlyingConditionData.method) { if (underlyingConditionData.method === BLOCKTIME_METHOD) { - return new ConditionSet(new TimeCondition(underlyingConditionData)); + return new ConditionSet( + new TimeCondition(underlyingConditionData), + version + ); } if (underlyingConditionData.contractAddress) { - return new ConditionSet(new ContractCondition(underlyingConditionData)); + return new ConditionSet( + new ContractCondition(underlyingConditionData), + version + ); } if ((underlyingConditionData.method as string).startsWith('eth_')) { - return new ConditionSet(new RpcCondition(underlyingConditionData)); + return new ConditionSet( + new RpcCondition(underlyingConditionData), + version + ); } } diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts index bc3104008..4a5de8848 100644 --- a/test/unit/conditions/condition-set.test.ts +++ b/test/unit/conditions/condition-set.test.ts @@ -76,37 +76,59 @@ describe('condition set', () => { }); }); + it('incompatible version', async () => { + const invalidVersion = '100.0.0'; + expect(() => { + ConditionSet.fromObj({ + version: invalidVersion, + condition: testTimeConditionObj, + }); + }).toThrow( + `Version provided, ${invalidVersion}, is incompatible with current version, ${ConditionSet.VERSION}` + ); + }); + it.each([ // no "operator" nor "method" value { - randoKey: 'randoValue', - otherKey: 'otherValue', + version: ConditionSet.VERSION, + condition: { + randoKey: 'randoValue', + otherKey: 'otherValue', + }, }, // invalid "method" and no "contractAddress" { - method: 'doWhatIWant', - returnValueTest: { - index: 0, - comparator: '>', - value: '100', + version: ConditionSet.VERSION, + condition: { + method: 'doWhatIWant', + returnValueTest: { + index: 0, + comparator: '>', + value: '100', + }, + chain: 5, }, - chain: 5, }, // condition with wrong method "method" and no contract address { - ...testTimeConditionObj, - method: 'doWhatIWant', + version: ConditionSet.VERSION, + condition: { + ...testTimeConditionObj, + method: 'doWhatIWant', + }, }, // rpc condition (no contract address) with disallowed method { - ...testRpcConditionObj, - method: 'isPolicyActive', + version: ConditionSet.VERSION, + condition: { + ...testRpcConditionObj, + method: 'isPolicyActive', + }, }, ])("can't determine condition type", async (invalidCondition) => { expect(() => { - ConditionSet.fromObj({ - condition: invalidCondition, - }); + ConditionSet.fromObj(invalidCondition); }).toThrow('unrecognized condition data'); }); From b3d0c396d5f71b5538aa1b1fa00afd58543699dc Mon Sep 17 00:00:00 2001 From: derekpierre Date: Mon, 19 Jun 2023 11:49:38 -0400 Subject: [PATCH 80/98] Update ConditionSet.equals() method and add tests. --- src/conditions/condition-set.ts | 5 +- test/unit/conditions/condition-set.test.ts | 447 ++++++++++++--------- 2 files changed, 253 insertions(+), 199 deletions(-) diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-set.ts index 1bc406338..ea514de22 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-set.ts @@ -100,6 +100,9 @@ export class ConditionSet { } public equals(other: ConditionSet): boolean { - return objectEquals(this.condition.toObj(), other.condition.toObj()); + return ( + this.version === other.version && + objectEquals(this.condition.toObj(), other.condition.toObj()) + ); } } diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts index 4a5de8848..6a2465f02 100644 --- a/test/unit/conditions/condition-set.test.ts +++ b/test/unit/conditions/condition-set.test.ts @@ -6,6 +6,7 @@ import { } from '../../../src/conditions/base'; import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; import { ERC721Balance } from '../../../src/conditions/predefined'; +import { toJSON } from '../../../src/utils'; import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR, @@ -58,7 +59,48 @@ describe('condition set', () => { ], }); - describe('serialization', () => { + it('equality', async () => { + const conditionSetCurrentVersion = new ConditionSet(rpcCondition); + const conditionSetSameCurrentVerstion = new ConditionSet( + rpcCondition, + ConditionSet.VERSION + ); + // same version and condition + expect( + conditionSetCurrentVersion.equals(conditionSetSameCurrentVerstion) + ).toBeTruthy(); + + // same version but different condition + const conditionSetSameVersionDifferentCondition = new ConditionSet( + timeCondition + ); + expect( + conditionSetCurrentVersion.equals( + conditionSetSameVersionDifferentCondition + ) + ).not.toBeTruthy(); + + // different minor/patch version but same condition + const conditionSetOlderMinorVersion = new ConditionSet( + rpcCondition, + '0.1.0' + ); + const conditionSetOlderPatchVersion = new ConditionSet( + rpcCondition, + '0.0.1' + ); + expect( + conditionSetCurrentVersion.equals(conditionSetOlderMinorVersion) + ).not.toBeTruthy(); + expect( + conditionSetCurrentVersion.equals(conditionSetOlderPatchVersion) + ).not.toBeTruthy(); + expect( + conditionSetOlderMinorVersion.equals(conditionSetOlderPatchVersion) + ).not.toBeTruthy(); + }); + + describe('serialization / deserialization', () => { it.each([ erc721BalanceCondition, contractConditionNoAbi, @@ -70,223 +112,232 @@ describe('condition set', () => { const conditionSet = new ConditionSet(condition); const conditionSetJson = conditionSet.toJson(); expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('version'); + expect(conditionSetJson).toContain(ConditionSet.VERSION); + expect(conditionSetJson).toContain('condition'); + expect(conditionSetJson).toContain(toJSON(condition.toObj())); + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); expect(conditionSetFromJson).toBeDefined(); expect(conditionSetFromJson.equals(conditionSetFromJson)).toBeTruthy(); }); - }); - it('incompatible version', async () => { - const invalidVersion = '100.0.0'; - expect(() => { - ConditionSet.fromObj({ - version: invalidVersion, - condition: testTimeConditionObj, - }); - }).toThrow( - `Version provided, ${invalidVersion}, is incompatible with current version, ${ConditionSet.VERSION}` - ); - }); + it('incompatible version', async () => { + const invalidVersion = '100.0.0'; + expect(() => { + ConditionSet.fromObj({ + version: invalidVersion, + condition: testTimeConditionObj, + }); + }).toThrow( + `Version provided, ${invalidVersion}, is incompatible with current version, ${ConditionSet.VERSION}` + ); + }); - it.each([ - // no "operator" nor "method" value - { - version: ConditionSet.VERSION, - condition: { - randoKey: 'randoValue', - otherKey: 'otherValue', + it.each([ + // no "operator" nor "method" value + { + version: ConditionSet.VERSION, + condition: { + randoKey: 'randoValue', + otherKey: 'otherValue', + }, }, - }, - // invalid "method" and no "contractAddress" - { - version: ConditionSet.VERSION, - condition: { - method: 'doWhatIWant', - returnValueTest: { - index: 0, - comparator: '>', - value: '100', + // invalid "method" and no "contractAddress" + { + version: ConditionSet.VERSION, + condition: { + method: 'doWhatIWant', + returnValueTest: { + index: 0, + comparator: '>', + value: '100', + }, + chain: 5, }, - chain: 5, }, - }, - // condition with wrong method "method" and no contract address - { - version: ConditionSet.VERSION, - condition: { - ...testTimeConditionObj, - method: 'doWhatIWant', + // condition with wrong method "method" and no contract address + { + version: ConditionSet.VERSION, + condition: { + ...testTimeConditionObj, + method: 'doWhatIWant', + }, }, - }, - // rpc condition (no contract address) with disallowed method - { - version: ConditionSet.VERSION, - condition: { - ...testRpcConditionObj, - method: 'isPolicyActive', + // rpc condition (no contract address) with disallowed method + { + version: ConditionSet.VERSION, + condition: { + ...testRpcConditionObj, + method: 'isPolicyActive', + }, }, - }, - ])("can't determine condition type", async (invalidCondition) => { - expect(() => { - ConditionSet.fromObj(invalidCondition); - }).toThrow('unrecognized condition data'); - }); + ])("can't determine condition type", async (invalidCondition) => { + expect(() => { + ConditionSet.fromObj(invalidCondition); + }).toThrow('unrecognized condition data'); + }); - it('erc721 condition serialization', async () => { - const conditionSet = new ConditionSet(erc721BalanceCondition); - - const erc721BalanceConditionObj = erc721BalanceCondition.toObj(); - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('contractAddress'); - expect(conditionSetJson).toContain( - erc721BalanceConditionObj.contractAddress - ); - expect(conditionSetJson).toContain('standardContractType'); - expect(conditionSetJson).toContain('ERC721'); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(erc721BalanceConditionObj.method); - expect(conditionSetJson).toContain('returnValueTest'); - - expect(conditionSetJson).not.toContain('functionAbi'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); - }); + it('erc721 condition serialization', async () => { + const conditionSet = new ConditionSet(erc721BalanceCondition); - it('contract condition no abi serialization', async () => { - const conditionSet = new ConditionSet(contractConditionNoAbi); + const erc721BalanceConditionObj = erc721BalanceCondition.toObj(); + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('contractAddress'); + expect(conditionSetJson).toContain( + erc721BalanceConditionObj.contractAddress + ); + expect(conditionSetJson).toContain('standardContractType'); + expect(conditionSetJson).toContain('ERC721'); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(erc721BalanceConditionObj.method); + expect(conditionSetJson).toContain('returnValueTest'); - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('contractAddress'); - expect(conditionSetJson).toContain( - testContractConditionObj.contractAddress - ); - expect(conditionSetJson).toContain('standardContractType'); - expect(conditionSetJson).toContain( - testContractConditionObj.standardContractType - ); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(testContractConditionObj.method); - expect(conditionSetJson).toContain('parameters'); - expect(conditionSetJson).toContain(testContractConditionObj.parameters[0]); - expect(conditionSetJson).toContain('returnValueTest'); - expect(conditionSetJson).not.toContain('functionAbi'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); - }); + expect(conditionSetJson).not.toContain('functionAbi'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); - it('contract condition with abi serialization', async () => { - const conditionSet = new ConditionSet(contractConditionWithAbi); + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); + }); - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('contractAddress'); - expect(conditionSetJson).toContain( - contractConditionWithAbiObj.contractAddress - ); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(contractConditionWithAbiObj.method); - expect(conditionSetJson).toContain('parameters'); - expect(conditionSetJson).toContain( - contractConditionWithAbiObj.parameters[0] - ); - expect(conditionSetJson).toContain( - contractConditionWithAbiObj.parameters[1] - ); - expect(conditionSetJson).toContain('returnValueTest'); - expect(conditionSetJson).toContain('functionAbi'); + it('contract condition no abi serialization', async () => { + const conditionSet = new ConditionSet(contractConditionNoAbi); - expect(conditionSetJson).not.toContain('standardContractType'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('contractAddress'); + expect(conditionSetJson).toContain( + testContractConditionObj.contractAddress + ); + expect(conditionSetJson).toContain('standardContractType'); + expect(conditionSetJson).toContain( + testContractConditionObj.standardContractType + ); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(testContractConditionObj.method); + expect(conditionSetJson).toContain('parameters'); + expect(conditionSetJson).toContain( + testContractConditionObj.parameters[0] + ); + expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).not.toContain('functionAbi'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); - }); + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); + }); - it('time condition serialization', async () => { - const conditionSet = new ConditionSet(timeCondition); - - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(testTimeConditionObj.method); - expect(conditionSetJson).toContain('returnValueTest'); - expect(conditionSetJson).not.toContain('parameters'); - expect(conditionSetJson).not.toContain('contractAddress'); - expect(conditionSetJson).not.toContain('standardContractType'); - expect(conditionSetJson).not.toContain('functionAbi'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(TimeCondition); - }); + it('contract condition with abi serialization', async () => { + const conditionSet = new ConditionSet(contractConditionWithAbi); - it('rpc condition serialization', async () => { - const conditionSet = new ConditionSet(rpcCondition); - - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(testRpcConditionObj.method); - expect(conditionSetJson).toContain('parameters'); - expect(conditionSetJson).toContain(testRpcConditionObj.parameters[0]); - expect(conditionSetJson).toContain('returnValueTest'); - expect(conditionSetJson).not.toContain('contractAddress'); - expect(conditionSetJson).not.toContain('standardContractType'); - expect(conditionSetJson).not.toContain('functionAbi'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(RpcCondition); - }); + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('contractAddress'); + expect(conditionSetJson).toContain( + contractConditionWithAbiObj.contractAddress + ); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(contractConditionWithAbiObj.method); + expect(conditionSetJson).toContain('parameters'); + expect(conditionSetJson).toContain( + contractConditionWithAbiObj.parameters[0] + ); + expect(conditionSetJson).toContain( + contractConditionWithAbiObj.parameters[1] + ); + expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).toContain('functionAbi'); + + expect(conditionSetJson).not.toContain('standardContractType'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); + }); + + it('time condition serialization', async () => { + const conditionSet = new ConditionSet(timeCondition); + + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(testTimeConditionObj.method); + expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).not.toContain('parameters'); + expect(conditionSetJson).not.toContain('contractAddress'); + expect(conditionSetJson).not.toContain('standardContractType'); + expect(conditionSetJson).not.toContain('functionAbi'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); - it('compound condition serialization', async () => { - const conditionSet = new ConditionSet(compoundCondition); - const compoundConditionObj = compoundCondition.toObj(); - - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toContain('operator'); - expect(conditionSetJson).toContain(compoundConditionObj.operator); - expect(conditionSetJson).toContain('operands'); - - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(testRpcConditionObj.method); - expect(conditionSetJson).toContain(testTimeConditionObj.method); - expect(conditionSetJson).toContain(testContractConditionObj.method); - expect(conditionSetJson).toContain('parameters'); - expect(conditionSetJson).toContain(testRpcConditionObj.parameters[0]); - expect(conditionSetJson).toContain(testContractConditionObj.parameters[0]); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(CompoundCondition); + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(TimeCondition); + }); + + it('rpc condition serialization', async () => { + const conditionSet = new ConditionSet(rpcCondition); + + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(testRpcConditionObj.method); + expect(conditionSetJson).toContain('parameters'); + expect(conditionSetJson).toContain(testRpcConditionObj.parameters[0]); + expect(conditionSetJson).toContain('returnValueTest'); + expect(conditionSetJson).not.toContain('contractAddress'); + expect(conditionSetJson).not.toContain('standardContractType'); + expect(conditionSetJson).not.toContain('functionAbi'); + expect(conditionSetJson).not.toContain('operator'); + expect(conditionSetJson).not.toContain('operands'); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(RpcCondition); + }); + + it('compound condition serialization', async () => { + const conditionSet = new ConditionSet(compoundCondition); + const compoundConditionObj = compoundCondition.toObj(); + + const conditionSetJson = conditionSet.toJson(); + expect(conditionSetJson).toContain('operator'); + expect(conditionSetJson).toContain(compoundConditionObj.operator); + expect(conditionSetJson).toContain('operands'); + + expect(conditionSetJson).toBeDefined(); + expect(conditionSetJson).toContain('chain'); + expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionSetJson).toContain('method'); + expect(conditionSetJson).toContain(testRpcConditionObj.method); + expect(conditionSetJson).toContain(testTimeConditionObj.method); + expect(conditionSetJson).toContain(testContractConditionObj.method); + expect(conditionSetJson).toContain('parameters'); + expect(conditionSetJson).toContain(testRpcConditionObj.parameters[0]); + expect(conditionSetJson).toContain( + testContractConditionObj.parameters[0] + ); + + const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); + expect(conditionSetFromJson).toBeDefined(); + expect(conditionSetFromJson.condition).toBeInstanceOf(CompoundCondition); + }); }); }); From 80d43472766b82c249bc1020799d24e037f9e860 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Mon, 19 Jun 2023 13:26:51 -0400 Subject: [PATCH 81/98] Rename `ConditionSet` to `ConditionLingo`. --- src/characters/cbd-recipient.ts | 16 +- src/characters/enrico.ts | 10 +- .../{condition-set.ts => condition-expr.ts} | 28 +- src/conditions/index.ts | 4 +- src/sdk/strategy/cbd-strategy.ts | 8 +- src/sdk/strategy/pre-strategy.ts | 6 +- test/docs/cbd.test.ts | 6 +- test/integration/enrico.test.ts | 8 +- test/integration/pre.test.ts | 4 +- test/unit/cbd-strategy.test.ts | 8 +- test/unit/conditions/base/contract.test.ts | 9 +- test/unit/conditions/condition-expr.test.ts | 353 ++++++++++++++++++ test/unit/conditions/condition-set.test.ts | 343 ----------------- test/unit/conditions/context.test.ts | 12 +- test/unit/pre-strategy.test.ts | 6 +- 15 files changed, 417 insertions(+), 404 deletions(-) rename src/conditions/{condition-set.ts => condition-expr.ts} (75%) create mode 100644 test/unit/conditions/condition-expr.test.ts delete mode 100644 test/unit/conditions/condition-set.test.ts diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index c461dc102..1a350755f 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -15,7 +15,7 @@ import { import { ethers } from 'ethers'; import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator'; -import { ConditionSet } from '../conditions'; +import { ConditionExpression } from '../conditions'; import { DkgRitual, getCombineDecryptionSharesFunction, @@ -54,14 +54,14 @@ export class CbdTDecDecrypter { // Retrieve and decrypt ciphertext using provider and condition set public async retrieveAndDecrypt( provider: ethers.providers.Web3Provider, - conditionSet: ConditionSet, + conditionExpr: ConditionExpression, variant: number, ciphertext: Ciphertext, aad: Uint8Array ): Promise { const decryptionShares = await this.retrieve( provider, - conditionSet, + conditionExpr, variant, ciphertext ); @@ -82,7 +82,7 @@ export class CbdTDecDecrypter { // Retrieve decryption shares public async retrieve( provider: ethers.providers.Web3Provider, - conditionSet: ConditionSet, + conditionExpr: ConditionExpression, variant: number, ciphertext: Ciphertext ): Promise { @@ -90,12 +90,12 @@ export class CbdTDecDecrypter { provider, this.ritualId ); - const contextStr = await conditionSet.buildContext(provider).toJson(); + const contextStr = await conditionExpr.buildContext(provider).toJson(); const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests( this.ritualId, variant, ciphertext, - conditionSet, + conditionExpr, contextStr, dkgParticipants ); @@ -150,7 +150,7 @@ export class CbdTDecDecrypter { ritualId: number, variant: number, ciphertext: Ciphertext, - conditionSet: ConditionSet, + conditionExpr: ConditionExpression, contextStr: string, dkgParticipants: Array ): { @@ -161,7 +161,7 @@ export class CbdTDecDecrypter { ritualId, variant, ciphertext, - conditionSet.toWASMConditions(), + conditionExpr.toWASMConditions(), new Context(contextStr) ); diff --git a/src/characters/enrico.ts b/src/characters/enrico.ts index a089455d9..2f8115937 100644 --- a/src/characters/enrico.ts +++ b/src/characters/enrico.ts @@ -7,19 +7,19 @@ import { SecretKey, } from '@nucypher/nucypher-core'; -import { ConditionSet } from '../conditions'; +import { ConditionExpression } from '../conditions'; import { Keyring } from '../keyring'; import { toBytes } from '../utils'; export class Enrico { public readonly encryptingKey: PublicKey | DkgPublicKey; private readonly keyring: Keyring; - public conditions?: ConditionSet; + public conditions?: ConditionExpression; constructor( encryptingKey: PublicKey | DkgPublicKey, verifyingKey?: SecretKey, - conditions?: ConditionSet + conditions?: ConditionExpression ) { this.encryptingKey = encryptingKey; this.keyring = new Keyring(verifyingKey ?? SecretKey.random()); @@ -32,7 +32,7 @@ export class Enrico { public encryptMessagePre( plaintext: Uint8Array | string, - withConditions?: ConditionSet + withConditions?: ConditionExpression ): MessageKit { if (!withConditions) { withConditions = this.conditions; @@ -51,7 +51,7 @@ export class Enrico { public encryptMessageCbd( plaintext: Uint8Array | string, - withConditions?: ConditionSet + withConditions?: ConditionExpression ): { ciphertext: Ciphertext; aad: Uint8Array } { if (!withConditions) { withConditions = this.conditions; diff --git a/src/conditions/condition-set.ts b/src/conditions/condition-expr.ts similarity index 75% rename from src/conditions/condition-set.ts rename to src/conditions/condition-expr.ts index ea514de22..d584a26fd 100644 --- a/src/conditions/condition-set.ts +++ b/src/conditions/condition-expr.ts @@ -13,20 +13,20 @@ import { BLOCKTIME_METHOD } from './base/time'; import { CompoundCondition } from './compound-condition'; import { ConditionContext } from './context'; -export type ConditionSetJSON = { +export type ConditionExpressionJSON = { version: string; condition: Record; }; -export class ConditionSet { +export class ConditionExpression { static VERSION = '1.0.0'; constructor( public readonly condition: Condition, - public readonly version: string = ConditionSet.VERSION + public readonly version: string = ConditionExpression.VERSION ) {} - public toObj(): ConditionSetJSON { + public toObj(): ConditionExpressionJSON { // TODO add version here const conditionData = this.condition.toObj(); return { @@ -35,21 +35,21 @@ export class ConditionSet { }; } - public static fromObj(obj: ConditionSetJSON): ConditionSet { + public static fromObj(obj: ConditionExpressionJSON): ConditionExpression { const version = obj.version; // version specific logic can go here const receivedMajorVersion = version.split('.')[0]; - const currentMajorVersion = ConditionSet.VERSION.split('.')[0]; + const currentMajorVersion = ConditionExpression.VERSION.split('.')[0]; if (receivedMajorVersion > currentMajorVersion) { throw new Error( - `Version provided, ${version}, is incompatible with current version, ${ConditionSet.VERSION}` + `Version provided, ${version}, is incompatible with current version, ${ConditionExpression.VERSION}` ); } const underlyingConditionData = obj.condition; if (underlyingConditionData.operator) { - return new ConditionSet( + return new ConditionExpression( new CompoundCondition(underlyingConditionData), version ); @@ -57,21 +57,21 @@ export class ConditionSet { if (underlyingConditionData.method) { if (underlyingConditionData.method === BLOCKTIME_METHOD) { - return new ConditionSet( + return new ConditionExpression( new TimeCondition(underlyingConditionData), version ); } if (underlyingConditionData.contractAddress) { - return new ConditionSet( + return new ConditionExpression( new ContractCondition(underlyingConditionData), version ); } if ((underlyingConditionData.method as string).startsWith('eth_')) { - return new ConditionSet( + return new ConditionExpression( new RpcCondition(underlyingConditionData), version ); @@ -85,8 +85,8 @@ export class ConditionSet { return toJSON(this.toObj()); } - public static fromJSON(json: string): ConditionSet { - return ConditionSet.fromObj(JSON.parse(json)); + public static fromJSON(json: string): ConditionExpression { + return ConditionExpression.fromObj(JSON.parse(json)); } public toWASMConditions(): WASMConditions { @@ -99,7 +99,7 @@ export class ConditionSet { return new ConditionContext([this.condition], provider); } - public equals(other: ConditionSet): boolean { + public equals(other: ConditionExpression): boolean { return ( this.version === other.version && objectEquals(this.condition.toObj(), other.condition.toObj()) diff --git a/src/conditions/index.ts b/src/conditions/index.ts index 7e335a0e0..2d5661fdb 100644 --- a/src/conditions/index.ts +++ b/src/conditions/index.ts @@ -3,8 +3,8 @@ import * as predefined from './predefined'; export { predefined, base }; export { Condition } from './base/condition'; -export type { ConditionSetJSON } from './condition-set'; -export { ConditionSet } from './condition-set'; +export type { ConditionExpressionJSON } from './condition-expr'; +export { ConditionExpression } from './condition-expr'; export { CompoundCondition } from './compound-condition'; export type { CustomContextParam } from './context'; export { ConditionContext } from './context'; diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index 4b1ae65b9..a56f93e72 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -7,14 +7,14 @@ import { CbdTDecDecrypterJSON, } from '../../characters/cbd-recipient'; import { Enrico } from '../../characters/enrico'; -import { ConditionSet, ConditionSetJSON } from '../../conditions'; +import { ConditionExpression, ConditionExpressionJSON } from '../../conditions'; import { DkgClient, DkgRitual } from '../../dkg'; import { fromJSON, toJSON } from '../../utils'; import { Cohort, CohortJSON } from '../cohort'; export type CbdStrategyJSON = { cohort: CohortJSON; - conditionSet?: ConditionSetJSON | undefined; + conditionExpr?: ConditionExpressionJSON | undefined; }; export type DeployedStrategyJSON = { @@ -81,8 +81,8 @@ export class DeployedCbdStrategy { return new DeployedCbdStrategy(decrypter, dkgRitual.dkgPublicKey); } - public makeEncrypter(conditionSet: ConditionSet): Enrico { - return new Enrico(this.dkgPublicKey, undefined, conditionSet); + public makeEncrypter(conditionExpr: ConditionExpression): Enrico { + return new Enrico(this.dkgPublicKey, undefined, conditionExpr); } public static fromJSON(json: string) { diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index 40f0cb12d..24d46c132 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -8,7 +8,7 @@ import { PreTDecDecrypter, PreTDecDecrypterJSON, } from '../../characters/pre-recipient'; -import { ConditionSet } from '../../conditions'; +import { ConditionExpression } from '../../conditions'; import { EnactedPolicy } from '../../policies/policy'; import { base64ToU8Receiver, bytesEquals, toJSON } from '../../utils'; import { Cohort, CohortJSON } from '../cohort'; @@ -165,8 +165,8 @@ export class DeployedPreStrategy { return new DeployedPreStrategy(cohort, decrypter, policy.policyKey); } - public makeEncrypter(conditionSet: ConditionSet): Enrico { - return new Enrico(this.policyKey, undefined, conditionSet); + public makeEncrypter(conditionExpr: ConditionExpression): Enrico { + return new Enrico(this.policyKey, undefined, conditionExpr); } public static fromJSON(json: string) { diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts index b5de452be..cac579632 100644 --- a/test/docs/cbd.test.ts +++ b/test/docs/cbd.test.ts @@ -19,7 +19,7 @@ import { const { predefined: { ERC721Ownership }, base: { ContractCondition }, - ConditionSet, + ConditionExpression, } = conditions; describe('Get Started (CBD PoC)', () => { @@ -74,7 +74,7 @@ describe('Get Started (CBD PoC)', () => { parameters: [5954], }); - const conditions = new ConditionSet( + const conditions = new ConditionExpression( NFTOwnership // Other conditions can be added here ); @@ -101,7 +101,7 @@ describe('Get Started (CBD PoC)', () => { }, }; const NFTBalance = new ContractCondition(NFTBalanceConfig); - const newConditions = new ConditionSet(NFTBalance); + const newConditions = new ConditionExpression(NFTBalance); const plaintext = 'this is a secret'; const encrypter = newDeployed.makeEncrypter(newConditions); const encryptedMessageKit = encrypter.encryptMessagePre(plaintext); diff --git a/test/integration/enrico.test.ts b/test/integration/enrico.test.ts index 86eb4f18e..d28ea9f77 100644 --- a/test/integration/enrico.test.ts +++ b/test/integration/enrico.test.ts @@ -13,7 +13,7 @@ import { const { predefined: { ERC721Ownership }, - ConditionSet, + ConditionExpression, } = conditions; describe('enrico', () => { @@ -105,7 +105,7 @@ describe('enrico', () => { chain: 5, }); - const conditions = new ConditionSet(ownsBufficornNFT); + const conditions = new ConditionExpression(ownsBufficornNFT); const enrico = new Enrico(policyKey, undefined, conditions); const encrypted = enrico.encryptMessagePre(toBytes(message)); @@ -135,8 +135,8 @@ describe('enrico', () => { parameters: [6969], }); - const conditions = new ConditionSet(ownsBufficornNFT); - const updatedConditions = new ConditionSet(ownsNonsenseNFT); + const conditions = new ConditionExpression(ownsBufficornNFT); + const updatedConditions = new ConditionExpression(ownsNonsenseNFT); const enrico = new Enrico(policyKey, undefined, conditions); const encrypted = enrico.encryptMessagePre( diff --git a/test/integration/pre.test.ts b/test/integration/pre.test.ts index 1db2b8df2..e3087e16c 100644 --- a/test/integration/pre.test.ts +++ b/test/integration/pre.test.ts @@ -8,7 +8,7 @@ import { fakeAlice, fakeBob, fakeUrsulas, reencryptKFrags } from '../utils'; const { predefined: { ERC721Ownership }, - ConditionSet, + ConditionExpression, } = conditions; describe('proxy reencryption', () => { @@ -94,7 +94,7 @@ describe('proxy reencryption', () => { chain: 1, parameters: [1], }); - const conditionsSet = new ConditionSet( + const conditionsSet = new ConditionExpression( new CompoundCondition({ operator: 'or', operands: [genuineUndead.toObj(), gnomePals.toObj()], diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 743c65d0b..1e45606a4 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -27,7 +27,7 @@ import { aliceSecretKeyBytes } from './testVariables'; const { predefined: { ERC721Ownership }, - ConditionSet, + ConditionExpression, } = conditions; // Shared test variables @@ -38,7 +38,7 @@ const ownsNFT = new ERC721Ownership({ parameters: [3591], chain: 5, }); -const conditionSet = new ConditionSet(ownsNFT); +const conditionExpr = new ConditionExpression(ownsNFT); const ursulas = fakeUrsulas().slice(0, 3); const variant = FerveoVariant.Precomputed; @@ -105,7 +105,7 @@ describe('CbdDeployedStrategy', () => { const message = 'this is a secret'; const { ciphertext, aad } = deployedStrategy - .makeEncrypter(conditionSet) + .makeEncrypter(conditionExpr) .encryptMessageCbd(message); // Setup mocks for `retrieveAndDecrypt` @@ -134,7 +134,7 @@ describe('CbdDeployedStrategy', () => { const decryptedMessage = await deployedStrategy.decrypter.retrieveAndDecrypt( aliceProvider, - conditionSet, + conditionExpr, variant, ciphertext, aad diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts index 3be6bdcd6..25fdddd8b 100644 --- a/test/unit/conditions/base/contract.test.ts +++ b/test/unit/conditions/base/contract.test.ts @@ -1,6 +1,9 @@ import { SecretKey } from '@nucypher/nucypher-core'; -import { ConditionSet, CustomContextParam } from '../../../../src/conditions'; +import { + ConditionExpression, + CustomContextParam, +} from '../../../../src/conditions'; import { ContractCondition } from '../../../../src/conditions/base'; import { USER_ADDRESS_PARAM } from '../../../../src/conditions/const'; import { fakeWeb3Provider } from '../../../utils'; @@ -134,8 +137,8 @@ describe('supports custom function abi', () => { }; const contractCondition = new ContractCondition(contractConditionObj); const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); - const conditionSet = new ConditionSet(contractCondition); - const conditionContext = conditionSet.buildContext(web3Provider); + const conditionExpr = new ConditionExpression(contractCondition); + const conditionContext = conditionExpr.buildContext(web3Provider); const myCustomParam = ':customParam'; const customParams: Record = {}; customParams[myCustomParam] = 1234; diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts new file mode 100644 index 000000000..429aad867 --- /dev/null +++ b/test/unit/conditions/condition-expr.test.ts @@ -0,0 +1,353 @@ +import { + CompoundCondition, + ConditionExpression, +} from '../../../src/conditions'; +import { + ContractCondition, + RpcCondition, + TimeCondition, +} from '../../../src/conditions/base'; +import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; +import { ERC721Balance } from '../../../src/conditions/predefined'; +import { toJSON } from '../../../src/utils'; +import { + TEST_CHAIN_ID, + TEST_CONTRACT_ADDR, + testFunctionAbi, + testReturnValueTest, +} from '../testVariables'; +import { + testContractConditionObj, + testRpcConditionObj, + testTimeConditionObj, +} from '../testVariables'; + +describe('condition set', () => { + const erc721BalanceCondition = new ERC721Balance({ + chain: TEST_CHAIN_ID, + contractAddress: TEST_CONTRACT_ADDR, + }); + + const contractConditionNoAbi = new ContractCondition( + testContractConditionObj + ); + + const customParamKey = ':customParam'; + const contractConditionWithAbiObj = { + ...testContractConditionObj, + standardContractType: undefined, + functionAbi: testFunctionAbi, + method: testFunctionAbi.name, + parameters: [USER_ADDRESS_PARAM, customParamKey], + returnValueTest: { + ...testReturnValueTest, + }, + }; + const contractConditionWithAbi = new ContractCondition( + contractConditionWithAbiObj + ); + + const rpcCondition = new RpcCondition(testRpcConditionObj); + const timeCondition = new TimeCondition(testTimeConditionObj); + const compoundCondition = new CompoundCondition({ + operator: 'and', + operands: [ + testContractConditionObj, + testTimeConditionObj, + testRpcConditionObj, + { + operator: 'or', + operands: [testTimeConditionObj, testContractConditionObj], + }, + ], + }); + + it('equality', async () => { + const conditionExprCurrentVersion = new ConditionExpression(rpcCondition); + const conditionExprSameCurrentVerstion = new ConditionExpression( + rpcCondition, + ConditionExpression.VERSION + ); + // same version and condition + expect( + conditionExprCurrentVersion.equals(conditionExprSameCurrentVerstion) + ).toBeTruthy(); + + // same version but different condition + const conditionExprSameVersionDifferentCondition = new ConditionExpression( + timeCondition + ); + expect( + conditionExprCurrentVersion.equals( + conditionExprSameVersionDifferentCondition + ) + ).not.toBeTruthy(); + + // different minor/patch version but same condition + const conditionExprOlderMinorVersion = new ConditionExpression( + rpcCondition, + '0.1.0' + ); + const conditionExprOlderPatchVersion = new ConditionExpression( + rpcCondition, + '0.0.1' + ); + expect( + conditionExprCurrentVersion.equals(conditionExprOlderMinorVersion) + ).not.toBeTruthy(); + expect( + conditionExprCurrentVersion.equals(conditionExprOlderPatchVersion) + ).not.toBeTruthy(); + expect( + conditionExprOlderMinorVersion.equals(conditionExprOlderPatchVersion) + ).not.toBeTruthy(); + }); + + describe('serialization / deserialization', () => { + it.each([ + erc721BalanceCondition, + contractConditionNoAbi, + contractConditionWithAbi, + rpcCondition, + timeCondition, + compoundCondition, + ])('serializes to and from json', async (condition) => { + const conditionExpr = new ConditionExpression(condition); + const conditionExprJson = conditionExpr.toJson(); + expect(conditionExprJson).toBeDefined(); + expect(conditionExprJson).toContain('version'); + expect(conditionExprJson).toContain(ConditionExpression.VERSION); + expect(conditionExprJson).toContain('condition'); + expect(conditionExprJson).toContain(toJSON(condition.toObj())); + + const conditionExprFromJson = + ConditionExpression.fromJSON(conditionExprJson); + expect(conditionExprFromJson).toBeDefined(); + expect(conditionExprFromJson.equals(conditionExprFromJson)).toBeTruthy(); + }); + + it('incompatible version', async () => { + const invalidVersion = '100.0.0'; + expect(() => { + ConditionExpression.fromObj({ + version: invalidVersion, + condition: testTimeConditionObj, + }); + }).toThrow( + `Version provided, ${invalidVersion}, is incompatible with current version, ${ConditionExpression.VERSION}` + ); + }); + + it.each([ + // no "operator" nor "method" value + { + version: ConditionExpression.VERSION, + condition: { + randoKey: 'randoValue', + otherKey: 'otherValue', + }, + }, + // invalid "method" and no "contractAddress" + { + version: ConditionExpression.VERSION, + condition: { + method: 'doWhatIWant', + returnValueTest: { + index: 0, + comparator: '>', + value: '100', + }, + chain: 5, + }, + }, + // condition with wrong method "method" and no contract address + { + version: ConditionExpression.VERSION, + condition: { + ...testTimeConditionObj, + method: 'doWhatIWant', + }, + }, + // rpc condition (no contract address) with disallowed method + { + version: ConditionExpression.VERSION, + condition: { + ...testRpcConditionObj, + method: 'isPolicyActive', + }, + }, + ])("can't determine condition type", async (invalidCondition) => { + expect(() => { + ConditionExpression.fromObj(invalidCondition); + }).toThrow('unrecognized condition data'); + }); + + it('erc721 condition serialization', async () => { + const conditionExpr = new ConditionExpression(erc721BalanceCondition); + + const erc721BalanceConditionObj = erc721BalanceCondition.toObj(); + const conditionExprJson = conditionExpr.toJson(); + expect(conditionExprJson).toBeDefined(); + expect(conditionExprJson).toContain('chain'); + expect(conditionExprJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionExprJson).toContain('contractAddress'); + expect(conditionExprJson).toContain( + erc721BalanceConditionObj.contractAddress + ); + expect(conditionExprJson).toContain('standardContractType'); + expect(conditionExprJson).toContain('ERC721'); + expect(conditionExprJson).toContain('method'); + expect(conditionExprJson).toContain(erc721BalanceConditionObj.method); + expect(conditionExprJson).toContain('returnValueTest'); + + expect(conditionExprJson).not.toContain('functionAbi'); + expect(conditionExprJson).not.toContain('operator'); + expect(conditionExprJson).not.toContain('operands'); + + const conditionExprFromJson = + ConditionExpression.fromJSON(conditionExprJson); + expect(conditionExprFromJson).toBeDefined(); + expect(conditionExprFromJson.condition).toBeInstanceOf(ContractCondition); + }); + + it('contract condition no abi serialization', async () => { + const conditionExpr = new ConditionExpression(contractConditionNoAbi); + + const conditionExprJson = conditionExpr.toJson(); + expect(conditionExprJson).toBeDefined(); + expect(conditionExprJson).toContain('chain'); + expect(conditionExprJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionExprJson).toContain('contractAddress'); + expect(conditionExprJson).toContain( + testContractConditionObj.contractAddress + ); + expect(conditionExprJson).toContain('standardContractType'); + expect(conditionExprJson).toContain( + testContractConditionObj.standardContractType + ); + expect(conditionExprJson).toContain('method'); + expect(conditionExprJson).toContain(testContractConditionObj.method); + expect(conditionExprJson).toContain('parameters'); + expect(conditionExprJson).toContain( + testContractConditionObj.parameters[0] + ); + expect(conditionExprJson).toContain('returnValueTest'); + expect(conditionExprJson).not.toContain('functionAbi'); + expect(conditionExprJson).not.toContain('operator'); + expect(conditionExprJson).not.toContain('operands'); + + const conditionExprFromJson = + ConditionExpression.fromJSON(conditionExprJson); + expect(conditionExprFromJson).toBeDefined(); + expect(conditionExprFromJson.condition).toBeInstanceOf(ContractCondition); + }); + + it('contract condition with abi serialization', async () => { + const conditionExpr = new ConditionExpression(contractConditionWithAbi); + + const conditionExprJson = conditionExpr.toJson(); + expect(conditionExprJson).toBeDefined(); + expect(conditionExprJson).toContain('chain'); + expect(conditionExprJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionExprJson).toContain('contractAddress'); + expect(conditionExprJson).toContain( + contractConditionWithAbiObj.contractAddress + ); + expect(conditionExprJson).toContain('method'); + expect(conditionExprJson).toContain(contractConditionWithAbiObj.method); + expect(conditionExprJson).toContain('parameters'); + expect(conditionExprJson).toContain( + contractConditionWithAbiObj.parameters[0] + ); + expect(conditionExprJson).toContain( + contractConditionWithAbiObj.parameters[1] + ); + expect(conditionExprJson).toContain('returnValueTest'); + expect(conditionExprJson).toContain('functionAbi'); + + expect(conditionExprJson).not.toContain('standardContractType'); + expect(conditionExprJson).not.toContain('operator'); + expect(conditionExprJson).not.toContain('operands'); + + const conditionExprFromJson = + ConditionExpression.fromJSON(conditionExprJson); + expect(conditionExprFromJson).toBeDefined(); + expect(conditionExprFromJson.condition).toBeInstanceOf(ContractCondition); + }); + + it('time condition serialization', async () => { + const conditionExpr = new ConditionExpression(timeCondition); + + const conditionExprJson = conditionExpr.toJson(); + expect(conditionExprJson).toBeDefined(); + expect(conditionExprJson).toContain('chain'); + expect(conditionExprJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionExprJson).toContain('method'); + expect(conditionExprJson).toContain(testTimeConditionObj.method); + expect(conditionExprJson).toContain('returnValueTest'); + expect(conditionExprJson).not.toContain('parameters'); + expect(conditionExprJson).not.toContain('contractAddress'); + expect(conditionExprJson).not.toContain('standardContractType'); + expect(conditionExprJson).not.toContain('functionAbi'); + expect(conditionExprJson).not.toContain('operator'); + expect(conditionExprJson).not.toContain('operands'); + + const conditionExprFromJson = + ConditionExpression.fromJSON(conditionExprJson); + expect(conditionExprFromJson).toBeDefined(); + expect(conditionExprFromJson.condition).toBeInstanceOf(TimeCondition); + }); + + it('rpc condition serialization', async () => { + const conditionExpr = new ConditionExpression(rpcCondition); + + const conditionExprJson = conditionExpr.toJson(); + expect(conditionExprJson).toBeDefined(); + expect(conditionExprJson).toContain('chain'); + expect(conditionExprJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionExprJson).toContain('method'); + expect(conditionExprJson).toContain(testRpcConditionObj.method); + expect(conditionExprJson).toContain('parameters'); + expect(conditionExprJson).toContain(testRpcConditionObj.parameters[0]); + expect(conditionExprJson).toContain('returnValueTest'); + expect(conditionExprJson).not.toContain('contractAddress'); + expect(conditionExprJson).not.toContain('standardContractType'); + expect(conditionExprJson).not.toContain('functionAbi'); + expect(conditionExprJson).not.toContain('operator'); + expect(conditionExprJson).not.toContain('operands'); + + const conditionExprFromJson = + ConditionExpression.fromJSON(conditionExprJson); + expect(conditionExprFromJson).toBeDefined(); + expect(conditionExprFromJson.condition).toBeInstanceOf(RpcCondition); + }); + + it('compound condition serialization', async () => { + const conditionExpr = new ConditionExpression(compoundCondition); + const compoundConditionObj = compoundCondition.toObj(); + + const conditionExprJson = conditionExpr.toJson(); + expect(conditionExprJson).toContain('operator'); + expect(conditionExprJson).toContain(compoundConditionObj.operator); + expect(conditionExprJson).toContain('operands'); + + expect(conditionExprJson).toBeDefined(); + expect(conditionExprJson).toContain('chain'); + expect(conditionExprJson).toContain(TEST_CHAIN_ID.toString()); + expect(conditionExprJson).toContain('method'); + expect(conditionExprJson).toContain(testRpcConditionObj.method); + expect(conditionExprJson).toContain(testTimeConditionObj.method); + expect(conditionExprJson).toContain(testContractConditionObj.method); + expect(conditionExprJson).toContain('parameters'); + expect(conditionExprJson).toContain(testRpcConditionObj.parameters[0]); + expect(conditionExprJson).toContain( + testContractConditionObj.parameters[0] + ); + + const conditionExprFromJson = + ConditionExpression.fromJSON(conditionExprJson); + expect(conditionExprFromJson).toBeDefined(); + expect(conditionExprFromJson.condition).toBeInstanceOf(CompoundCondition); + }); + }); +}); diff --git a/test/unit/conditions/condition-set.test.ts b/test/unit/conditions/condition-set.test.ts deleted file mode 100644 index 6a2465f02..000000000 --- a/test/unit/conditions/condition-set.test.ts +++ /dev/null @@ -1,343 +0,0 @@ -import { CompoundCondition, ConditionSet } from '../../../src/conditions'; -import { - ContractCondition, - RpcCondition, - TimeCondition, -} from '../../../src/conditions/base'; -import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; -import { ERC721Balance } from '../../../src/conditions/predefined'; -import { toJSON } from '../../../src/utils'; -import { - TEST_CHAIN_ID, - TEST_CONTRACT_ADDR, - testFunctionAbi, - testReturnValueTest, -} from '../testVariables'; -import { - testContractConditionObj, - testRpcConditionObj, - testTimeConditionObj, -} from '../testVariables'; - -describe('condition set', () => { - const erc721BalanceCondition = new ERC721Balance({ - chain: TEST_CHAIN_ID, - contractAddress: TEST_CONTRACT_ADDR, - }); - - const contractConditionNoAbi = new ContractCondition( - testContractConditionObj - ); - - const customParamKey = ':customParam'; - const contractConditionWithAbiObj = { - ...testContractConditionObj, - standardContractType: undefined, - functionAbi: testFunctionAbi, - method: testFunctionAbi.name, - parameters: [USER_ADDRESS_PARAM, customParamKey], - returnValueTest: { - ...testReturnValueTest, - }, - }; - const contractConditionWithAbi = new ContractCondition( - contractConditionWithAbiObj - ); - - const rpcCondition = new RpcCondition(testRpcConditionObj); - const timeCondition = new TimeCondition(testTimeConditionObj); - const compoundCondition = new CompoundCondition({ - operator: 'and', - operands: [ - testContractConditionObj, - testTimeConditionObj, - testRpcConditionObj, - { - operator: 'or', - operands: [testTimeConditionObj, testContractConditionObj], - }, - ], - }); - - it('equality', async () => { - const conditionSetCurrentVersion = new ConditionSet(rpcCondition); - const conditionSetSameCurrentVerstion = new ConditionSet( - rpcCondition, - ConditionSet.VERSION - ); - // same version and condition - expect( - conditionSetCurrentVersion.equals(conditionSetSameCurrentVerstion) - ).toBeTruthy(); - - // same version but different condition - const conditionSetSameVersionDifferentCondition = new ConditionSet( - timeCondition - ); - expect( - conditionSetCurrentVersion.equals( - conditionSetSameVersionDifferentCondition - ) - ).not.toBeTruthy(); - - // different minor/patch version but same condition - const conditionSetOlderMinorVersion = new ConditionSet( - rpcCondition, - '0.1.0' - ); - const conditionSetOlderPatchVersion = new ConditionSet( - rpcCondition, - '0.0.1' - ); - expect( - conditionSetCurrentVersion.equals(conditionSetOlderMinorVersion) - ).not.toBeTruthy(); - expect( - conditionSetCurrentVersion.equals(conditionSetOlderPatchVersion) - ).not.toBeTruthy(); - expect( - conditionSetOlderMinorVersion.equals(conditionSetOlderPatchVersion) - ).not.toBeTruthy(); - }); - - describe('serialization / deserialization', () => { - it.each([ - erc721BalanceCondition, - contractConditionNoAbi, - contractConditionWithAbi, - rpcCondition, - timeCondition, - compoundCondition, - ])('serializes to and from json', async (condition) => { - const conditionSet = new ConditionSet(condition); - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('version'); - expect(conditionSetJson).toContain(ConditionSet.VERSION); - expect(conditionSetJson).toContain('condition'); - expect(conditionSetJson).toContain(toJSON(condition.toObj())); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.equals(conditionSetFromJson)).toBeTruthy(); - }); - - it('incompatible version', async () => { - const invalidVersion = '100.0.0'; - expect(() => { - ConditionSet.fromObj({ - version: invalidVersion, - condition: testTimeConditionObj, - }); - }).toThrow( - `Version provided, ${invalidVersion}, is incompatible with current version, ${ConditionSet.VERSION}` - ); - }); - - it.each([ - // no "operator" nor "method" value - { - version: ConditionSet.VERSION, - condition: { - randoKey: 'randoValue', - otherKey: 'otherValue', - }, - }, - // invalid "method" and no "contractAddress" - { - version: ConditionSet.VERSION, - condition: { - method: 'doWhatIWant', - returnValueTest: { - index: 0, - comparator: '>', - value: '100', - }, - chain: 5, - }, - }, - // condition with wrong method "method" and no contract address - { - version: ConditionSet.VERSION, - condition: { - ...testTimeConditionObj, - method: 'doWhatIWant', - }, - }, - // rpc condition (no contract address) with disallowed method - { - version: ConditionSet.VERSION, - condition: { - ...testRpcConditionObj, - method: 'isPolicyActive', - }, - }, - ])("can't determine condition type", async (invalidCondition) => { - expect(() => { - ConditionSet.fromObj(invalidCondition); - }).toThrow('unrecognized condition data'); - }); - - it('erc721 condition serialization', async () => { - const conditionSet = new ConditionSet(erc721BalanceCondition); - - const erc721BalanceConditionObj = erc721BalanceCondition.toObj(); - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('contractAddress'); - expect(conditionSetJson).toContain( - erc721BalanceConditionObj.contractAddress - ); - expect(conditionSetJson).toContain('standardContractType'); - expect(conditionSetJson).toContain('ERC721'); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(erc721BalanceConditionObj.method); - expect(conditionSetJson).toContain('returnValueTest'); - - expect(conditionSetJson).not.toContain('functionAbi'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); - }); - - it('contract condition no abi serialization', async () => { - const conditionSet = new ConditionSet(contractConditionNoAbi); - - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('contractAddress'); - expect(conditionSetJson).toContain( - testContractConditionObj.contractAddress - ); - expect(conditionSetJson).toContain('standardContractType'); - expect(conditionSetJson).toContain( - testContractConditionObj.standardContractType - ); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(testContractConditionObj.method); - expect(conditionSetJson).toContain('parameters'); - expect(conditionSetJson).toContain( - testContractConditionObj.parameters[0] - ); - expect(conditionSetJson).toContain('returnValueTest'); - expect(conditionSetJson).not.toContain('functionAbi'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); - }); - - it('contract condition with abi serialization', async () => { - const conditionSet = new ConditionSet(contractConditionWithAbi); - - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('contractAddress'); - expect(conditionSetJson).toContain( - contractConditionWithAbiObj.contractAddress - ); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(contractConditionWithAbiObj.method); - expect(conditionSetJson).toContain('parameters'); - expect(conditionSetJson).toContain( - contractConditionWithAbiObj.parameters[0] - ); - expect(conditionSetJson).toContain( - contractConditionWithAbiObj.parameters[1] - ); - expect(conditionSetJson).toContain('returnValueTest'); - expect(conditionSetJson).toContain('functionAbi'); - - expect(conditionSetJson).not.toContain('standardContractType'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(ContractCondition); - }); - - it('time condition serialization', async () => { - const conditionSet = new ConditionSet(timeCondition); - - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(testTimeConditionObj.method); - expect(conditionSetJson).toContain('returnValueTest'); - expect(conditionSetJson).not.toContain('parameters'); - expect(conditionSetJson).not.toContain('contractAddress'); - expect(conditionSetJson).not.toContain('standardContractType'); - expect(conditionSetJson).not.toContain('functionAbi'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(TimeCondition); - }); - - it('rpc condition serialization', async () => { - const conditionSet = new ConditionSet(rpcCondition); - - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(testRpcConditionObj.method); - expect(conditionSetJson).toContain('parameters'); - expect(conditionSetJson).toContain(testRpcConditionObj.parameters[0]); - expect(conditionSetJson).toContain('returnValueTest'); - expect(conditionSetJson).not.toContain('contractAddress'); - expect(conditionSetJson).not.toContain('standardContractType'); - expect(conditionSetJson).not.toContain('functionAbi'); - expect(conditionSetJson).not.toContain('operator'); - expect(conditionSetJson).not.toContain('operands'); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(RpcCondition); - }); - - it('compound condition serialization', async () => { - const conditionSet = new ConditionSet(compoundCondition); - const compoundConditionObj = compoundCondition.toObj(); - - const conditionSetJson = conditionSet.toJson(); - expect(conditionSetJson).toContain('operator'); - expect(conditionSetJson).toContain(compoundConditionObj.operator); - expect(conditionSetJson).toContain('operands'); - - expect(conditionSetJson).toBeDefined(); - expect(conditionSetJson).toContain('chain'); - expect(conditionSetJson).toContain(TEST_CHAIN_ID.toString()); - expect(conditionSetJson).toContain('method'); - expect(conditionSetJson).toContain(testRpcConditionObj.method); - expect(conditionSetJson).toContain(testTimeConditionObj.method); - expect(conditionSetJson).toContain(testContractConditionObj.method); - expect(conditionSetJson).toContain('parameters'); - expect(conditionSetJson).toContain(testRpcConditionObj.parameters[0]); - expect(conditionSetJson).toContain( - testContractConditionObj.parameters[0] - ); - - const conditionSetFromJson = ConditionSet.fromJSON(conditionSetJson); - expect(conditionSetFromJson).toBeDefined(); - expect(conditionSetFromJson.condition).toBeInstanceOf(CompoundCondition); - }); - }); -}); diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts index 958454cd1..035b704d6 100644 --- a/test/unit/conditions/context.test.ts +++ b/test/unit/conditions/context.test.ts @@ -1,7 +1,7 @@ import { SecretKey } from '@nucypher/nucypher-core'; import { CustomContextParam } from '../../../src'; -import { ConditionSet } from '../../../src/conditions'; +import { ConditionExpression } from '../../../src/conditions'; import { ContractCondition, RpcCondition } from '../../../src/conditions/base'; import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; import { RESERVED_CONTEXT_PARAMS } from '../../../src/conditions/context/context'; @@ -26,7 +26,7 @@ describe('serialization', () => { value: USER_ADDRESS_PARAM, }, }); - const conditionContext = new ConditionSet(rpcCondition).buildContext( + const conditionContext = new ConditionExpression(rpcCondition).buildContext( web3Provider ); const asJson = await conditionContext.toJson(); @@ -48,8 +48,8 @@ describe('context parameters', () => { }, }; const contractCondition = new ContractCondition(contractConditionObj); - const conditionSet = new ConditionSet(contractCondition); - const conditionContext = conditionSet.buildContext(web3Provider); + const conditionExpr = new ConditionExpression(contractCondition); + const conditionContext = conditionExpr.buildContext(web3Provider); describe('return value test', () => { it('accepts on a custom context parameters', async () => { @@ -94,7 +94,7 @@ describe('context parameters', () => { ...contractConditionObj, parameters: [USER_ADDRESS_PARAM, customParamKey], }); - const conditionContext = new ConditionSet( + const conditionContext = new ConditionExpression( customContractCondition ).buildContext(web3Provider); @@ -108,7 +108,7 @@ describe('context parameters', () => { ...contractConditionObj, parameters: [USER_ADDRESS_PARAM, 100], }); - const conditionContext = new ConditionSet( + const conditionContext = new ConditionExpression( customContractCondition ).buildContext(web3Provider); diff --git a/test/unit/pre-strategy.test.ts b/test/unit/pre-strategy.test.ts index 3b15ba0eb..9fa599a22 100644 --- a/test/unit/pre-strategy.test.ts +++ b/test/unit/pre-strategy.test.ts @@ -24,7 +24,7 @@ import { aliceSecretKeyBytes, bobSecretKeyBytes } from './testVariables'; const { predefined: { ERC721Ownership }, - ConditionSet, + ConditionExpression, } = conditions; // Shared test variables @@ -37,7 +37,7 @@ const ownsNFT = new ERC721Ownership({ parameters: [3591], chain: 5, }); -const conditionSet = new ConditionSet(ownsNFT); +const conditionExpr = new ConditionExpression(ownsNFT); const mockedUrsulas = fakeUrsulas().slice(0, 3); const makePreStrategy = async () => { @@ -113,7 +113,7 @@ describe('PreDeployedStrategy', () => { const plaintext = 'this is a secret'; const encryptedMessageKit = deployedStrategy - .makeEncrypter(conditionSet) + .makeEncrypter(conditionExpr) .encryptMessagePre(plaintext); // Setup mocks for `retrieveAndDecrypt` From 324d2d952cb0028dc99b9520436f22011aceaa6c Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 20 Jun 2023 09:42:27 -0400 Subject: [PATCH 82/98] Additional testing for equality of `ConditionExpression` objects. --- test/unit/conditions/condition-expr.test.ts | 134 ++++++++++++++------ 1 file changed, 96 insertions(+), 38 deletions(-) diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts index 429aad867..a824ee2f4 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -62,45 +62,103 @@ describe('condition set', () => { ], }); - it('equality', async () => { + describe('equality', () => { const conditionExprCurrentVersion = new ConditionExpression(rpcCondition); - const conditionExprSameCurrentVerstion = new ConditionExpression( - rpcCondition, - ConditionExpression.VERSION - ); - // same version and condition - expect( - conditionExprCurrentVersion.equals(conditionExprSameCurrentVerstion) - ).toBeTruthy(); - - // same version but different condition - const conditionExprSameVersionDifferentCondition = new ConditionExpression( - timeCondition - ); - expect( - conditionExprCurrentVersion.equals( - conditionExprSameVersionDifferentCondition - ) - ).not.toBeTruthy(); - - // different minor/patch version but same condition - const conditionExprOlderMinorVersion = new ConditionExpression( - rpcCondition, - '0.1.0' - ); - const conditionExprOlderPatchVersion = new ConditionExpression( - rpcCondition, - '0.0.1' - ); - expect( - conditionExprCurrentVersion.equals(conditionExprOlderMinorVersion) - ).not.toBeTruthy(); - expect( - conditionExprCurrentVersion.equals(conditionExprOlderPatchVersion) - ).not.toBeTruthy(); - expect( - conditionExprOlderMinorVersion.equals(conditionExprOlderPatchVersion) - ).not.toBeTruthy(); + + it('same version and condition', async () => { + const conditionExprSameCurrentVerstion = new ConditionExpression( + rpcCondition, + ConditionExpression.VERSION + ); + expect( + conditionExprCurrentVersion.equals(conditionExprSameCurrentVerstion) + ).toBeTruthy(); + }); + + it('different minor/patch version but same condition', async () => { + const conditionExprOlderMinorVersion = new ConditionExpression( + rpcCondition, + '0.1.0' + ); + const conditionExprOlderPatchVersion = new ConditionExpression( + rpcCondition, + '0.0.1' + ); + expect( + conditionExprCurrentVersion.equals(conditionExprOlderMinorVersion) + ).not.toBeTruthy(); + expect( + conditionExprCurrentVersion.equals(conditionExprOlderPatchVersion) + ).not.toBeTruthy(); + expect( + conditionExprOlderMinorVersion.equals(conditionExprOlderPatchVersion) + ).not.toBeTruthy(); + }); + + it('minor/patch number greater than major; still older', async () => { + const conditionExprOlderMinorVersion = new ConditionExpression( + rpcCondition, + '0.9.0' + ); + const conditionExprOlderPatchVersion = new ConditionExpression( + rpcCondition, + '0.0.9' + ); + const conditionExprOlderMinorPatchVersion = new ConditionExpression( + rpcCondition, + '0.9.9' + ); + expect( + conditionExprCurrentVersion.equals(conditionExprOlderMinorVersion) + ).not.toBeTruthy(); + expect( + conditionExprCurrentVersion.equals(conditionExprOlderPatchVersion) + ).not.toBeTruthy(); + expect( + conditionExprCurrentVersion.equals(conditionExprOlderMinorPatchVersion) + ).not.toBeTruthy(); + expect( + conditionExprOlderMinorVersion.equals(conditionExprOlderPatchVersion) + ).not.toBeTruthy(); + expect( + conditionExprOlderMinorVersion.equals( + conditionExprOlderMinorPatchVersion + ) + ).not.toBeTruthy(); + expect( + conditionExprOlderPatchVersion.equals( + conditionExprOlderMinorPatchVersion + ) + ).not.toBeTruthy(); + }); + + it.each([ + erc721BalanceCondition, + contractConditionNoAbi, + contractConditionWithAbi, + timeCondition, + compoundCondition, + ])('same version but different condition', async (condition) => { + const conditionExprSameVersionDifferentCondition = + new ConditionExpression(condition); + expect( + conditionExprCurrentVersion.equals( + conditionExprSameVersionDifferentCondition + ) + ).not.toBeTruthy(); + }); + + it('same contract condition although using erc721 helper', async () => { + const erc721ConditionExpr = new ConditionExpression( + erc721BalanceCondition + ); + const erc721ConditionData = erc721BalanceCondition.toObj(); + const sameContractCondition = new ContractCondition(erc721ConditionData); + const contractConditionExpr = new ConditionExpression( + sameContractCondition + ); + expect(erc721ConditionExpr.equals(contractConditionExpr)).toBeTruthy(); + }); }); describe('serialization / deserialization', () => { From 8a599be98e32174c397dd7cadc4e37695ab62607 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 20 Jun 2023 11:45:07 -0400 Subject: [PATCH 83/98] Apply RFCs from #229. - Logic cleanup for determining condition type. - Use semver library for processing version strings - Remove resolved TODO - Added some tests --- package.json | 4 +- src/conditions/condition-expr.ts | 55 +++++++++------------ test/unit/conditions/condition-expr.test.ts | 12 +++++ yarn.lock | 12 +++++ 4 files changed, 50 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index b3a930bde..ce0808094 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,8 @@ "deep-equal": "^2.2.1", "ethers": "^5.4.1", "joi": "^17.7.0", - "qs": "^6.10.1" + "qs": "^6.10.1", + "semver": "^7.5.2" }, "devDependencies": { "@babel/core": "^7.18.10", @@ -67,6 +68,7 @@ "@types/deep-equal": "^1.0.1", "@types/jest": "^26.0.24", "@types/qs": "^6.9.7", + "@types/semver": "^7.5.0", "@typescript-eslint/eslint-plugin": "^4.0.1", "@typescript-eslint/parser": "^4.0.1", "cz-conventional-changelog": "^3.0.1", diff --git a/src/conditions/condition-expr.ts b/src/conditions/condition-expr.ts index d584a26fd..b762c6b13 100644 --- a/src/conditions/condition-expr.ts +++ b/src/conditions/condition-expr.ts @@ -1,5 +1,6 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; +import { SemVer } from 'semver'; import { objectEquals, toJSON } from '../utils'; @@ -27,7 +28,6 @@ export class ConditionExpression { ) {} public toObj(): ConditionExpressionJSON { - // TODO add version here const conditionData = this.condition.toObj(); return { version: this.version, @@ -36,49 +36,40 @@ export class ConditionExpression { } public static fromObj(obj: ConditionExpressionJSON): ConditionExpression { - const version = obj.version; - // version specific logic can go here - const receivedMajorVersion = version.split('.')[0]; - const currentMajorVersion = ConditionExpression.VERSION.split('.')[0]; - if (receivedMajorVersion > currentMajorVersion) { + const receivedVersion = new SemVer(obj.version); + const currentVersion = new SemVer(ConditionExpression.VERSION); + if (receivedVersion.major > currentVersion.major) { throw new Error( - `Version provided, ${version}, is incompatible with current version, ${ConditionExpression.VERSION}` + `Version provided, ${obj.version}, is incompatible with current version, ${ConditionExpression.VERSION}` ); } const underlyingConditionData = obj.condition; + let condition: Condition | undefined; if (underlyingConditionData.operator) { - return new ConditionExpression( - new CompoundCondition(underlyingConditionData), - version - ); - } - - if (underlyingConditionData.method) { + condition = new CompoundCondition(underlyingConditionData); + } else if (underlyingConditionData.method) { if (underlyingConditionData.method === BLOCKTIME_METHOD) { - return new ConditionExpression( - new TimeCondition(underlyingConditionData), - version - ); - } - - if (underlyingConditionData.contractAddress) { - return new ConditionExpression( - new ContractCondition(underlyingConditionData), - version - ); + condition = new TimeCondition(underlyingConditionData); + } else if (underlyingConditionData.contractAddress) { + condition = new ContractCondition(underlyingConditionData); + } else if ( + (underlyingConditionData.method as string).startsWith('eth_') + ) { + condition = new RpcCondition(underlyingConditionData); } + } - if ((underlyingConditionData.method as string).startsWith('eth_')) { - return new ConditionExpression( - new RpcCondition(underlyingConditionData), - version - ); - } + if (!condition) { + throw new Error( + `Invalid condition: unrecognized condition data ${JSON.stringify( + underlyingConditionData + )}` + ); } - throw new Error('Invalid condition: unrecognized condition data'); + return new ConditionExpression(condition, obj.version); } public toJson(): string { diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts index a824ee2f4..5ee867200 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -196,6 +196,18 @@ describe('condition set', () => { ); }); + it.each(['version', 'x.y', 'x.y.z', '-1,0.0', '1.0.0.0.0.0.0'])( + 'invalid versions', + async (invalidVersion) => { + expect(() => { + ConditionExpression.fromObj({ + version: invalidVersion, + condition: testTimeConditionObj, + }); + }).toThrow(`Invalid Version: ${invalidVersion}`); + } + ); + it.each([ // no "operator" nor "method" value { diff --git a/yarn.lock b/yarn.lock index 6f4792ae7..4836111c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1891,6 +1891,11 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== +"@types/semver@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -6183,6 +6188,13 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== + dependencies: + lru-cache "^6.0.0" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" From 553f9304de1226b75e1fd5cda743bb5d03024faf Mon Sep 17 00:00:00 2001 From: derekpierre Date: Wed, 21 Jun 2023 08:15:20 -0400 Subject: [PATCH 84/98] Make incompatible version test more robust. --- test/unit/conditions/condition-expr.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts index 5ee867200..806269a7f 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -1,3 +1,5 @@ +import { SemVer } from 'semver'; + import { CompoundCondition, ConditionExpression, @@ -185,10 +187,11 @@ describe('condition set', () => { }); it('incompatible version', async () => { - const invalidVersion = '100.0.0'; + const currentVersion = new SemVer(ConditionExpression.VERSION); + const invalidVersion = currentVersion.inc('major'); expect(() => { ConditionExpression.fromObj({ - version: invalidVersion, + version: invalidVersion.version, condition: testTimeConditionObj, }); }).toThrow( From ccdeba06cabf1117f103909672378ea39ebe74c2 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 22 Jun 2023 13:33:53 +0200 Subject: [PATCH 85/98] add TODOs --- src/characters/pre-recipient.ts | 11 +++-------- src/dkg.ts | 2 ++ src/sdk/strategy/pre-strategy.ts | 7 ++----- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/characters/pre-recipient.ts b/src/characters/pre-recipient.ts index 1fb3066a5..ae0fc8cda 100644 --- a/src/characters/pre-recipient.ts +++ b/src/characters/pre-recipient.ts @@ -188,18 +188,13 @@ export class PreTDecDecrypter { public equals(other: PreTDecDecrypter): boolean { return ( this.porter.porterUrl.toString() === other.porter.porterUrl.toString() && - bytesEquals( - this.policyEncryptingKey.toCompressedBytes(), - other.policyEncryptingKey.toCompressedBytes() - ) && + this.policyEncryptingKey.equals(other.policyEncryptingKey) && + // TODO: Replace with `equals` after https://github.com/nucypher/nucypher-core/issues/56 is fixed bytesEquals( this.encryptedTreasureMap.toBytes(), other.encryptedTreasureMap.toBytes() ) && - bytesEquals( - this.publisherVerifyingKey.toCompressedBytes(), - other.publisherVerifyingKey.toCompressedBytes() - ) + this.publisherVerifyingKey.equals(other.publisherVerifyingKey) ); } } diff --git a/src/dkg.ts b/src/dkg.ts index 50798ce19..3569f3cc2 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -86,7 +86,9 @@ export class DkgRitual { public equals(other: DkgRitual): boolean { return ( this.id === other.id && + // TODO: Replace with `equals` after https://github.com/nucypher/nucypher-core/issues/56 is fixed bytesEquals(this.dkgPublicKey.toBytes(), other.dkgPublicKey.toBytes()) && + // TODO: Replace with `equals` after https://github.com/nucypher/nucypher-core/issues/56 is fixed bytesEquals( this.dkgPublicParams.toBytes(), other.dkgPublicParams.toBytes() diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts index 24d46c132..acc6d997f 100644 --- a/src/sdk/strategy/pre-strategy.ts +++ b/src/sdk/strategy/pre-strategy.ts @@ -128,7 +128,7 @@ export class PreStrategy { public equals(other: PreStrategy) { return ( this.cohort.equals(other.cohort) && - // TODO: Add equality to WASM bindings + // TODO: Replace with `equals` after https://github.com/nucypher/nucypher-core/issues/56 is fixed bytesEquals( this.aliceSecretKey.toBEBytes(), other.aliceSecretKey.toBEBytes() @@ -201,10 +201,7 @@ export class DeployedPreStrategy { return ( this.cohort.equals(other.cohort) && this.decrypter.equals(other.decrypter) && - bytesEquals( - this.policyKey.toCompressedBytes(), - other.policyKey.toCompressedBytes() - ) + this.policyKey.equals(other.policyKey) ); } } From 87e237fcbad8cab509de12d2f4ff27c8ab79dc20 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 19 Jun 2023 15:49:19 +0200 Subject: [PATCH 86/98] feat!: hide dkg public params --- package.json | 2 +- src/agents/contracts.ts | 2 +- src/agents/coordinator.ts | 5 +- src/characters/cbd-recipient.ts | 24 +- src/dkg.ts | 35 +- src/index.ts | 5 + test/unit/cbd-strategy.test.ts | 2 +- test/unit/ritual.test.ts | 19 + test/utils.ts | 17 +- yarn.lock | 1283 +++++++++++++++---------------- 10 files changed, 689 insertions(+), 705 deletions(-) create mode 100644 test/unit/ritual.test.ts diff --git a/package.json b/package.json index ce0808094..4d9af47ff 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "prebuild": "yarn typechain" }, "dependencies": { - "@nucypher/nucypher-core": "^0.9.0", + "@nucypher/nucypher-core": "../nucypher-core/nucypher-core-wasm/pkg", "axios": "^0.21.1", "deep-equal": "^2.2.1", "ethers": "^5.4.1", diff --git a/src/agents/contracts.ts b/src/agents/contracts.ts index c6b022421..0ed03352f 100644 --- a/src/agents/contracts.ts +++ b/src/agents/contracts.ts @@ -12,7 +12,7 @@ const POLYGON: Contracts = { const MUMBAI: Contracts = { SUBSCRIPTION_MANAGER: '0xb9015d7b35ce7c81dde38ef7136baa3b1044f313', - COORDINATOR: undefined, + COORDINATOR: '0x0f019Ade1D34399D946CF2f161386362655Dd1A4', }; const GOERLI: Contracts = { diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts index 80fd82fc0..841375e94 100644 --- a/src/agents/coordinator.ts +++ b/src/agents/coordinator.ts @@ -28,9 +28,8 @@ export class DkgCoordinatorAgent { ): Promise { const Coordinator = await this.connectReadOnly(provider); // TODO: Remove `as unknown` cast after regenerating the contract types: https://github.com/nucypher/nucypher-contracts/pull/77 - return (await Coordinator.getParticipants( - ritualId - )) as unknown as DkgParticipant[]; + const participants = await Coordinator.getParticipants(ritualId); + return participants as unknown as DkgParticipant[]; } public static async getRitual( diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 1a350755f..2d5f3a048 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -4,7 +4,6 @@ import { DecryptionSharePrecomputed, DecryptionShareSimple, decryptWithSharedSecret, - DkgPublicParameters, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, SessionSharedSecret, @@ -28,7 +27,6 @@ import { Porter } from './porter'; export type CbdTDecDecrypterJSON = { porterUri: string; ritualId: number; - dkgPublicParams: Uint8Array; threshold: number; }; @@ -38,7 +36,6 @@ export class CbdTDecDecrypter { private constructor( private readonly porter: Porter, private readonly ritualId: number, - private readonly dkgPublicParams: DkgPublicParameters, private readonly threshold: number ) {} @@ -46,7 +43,6 @@ export class CbdTDecDecrypter { return new CbdTDecDecrypter( new Porter(porterUri), dkgRitual.id, - dkgRitual.dkgPublicParams, dkgRitual.threshold ); } @@ -58,7 +54,7 @@ export class CbdTDecDecrypter { variant: number, ciphertext: Ciphertext, aad: Uint8Array - ): Promise { + ): Promise { const decryptionShares = await this.retrieve( provider, conditionExpr, @@ -69,14 +65,7 @@ export class CbdTDecDecrypter { const combineDecryptionSharesFn = getCombineDecryptionSharesFunction(variant); const sharedSecret = combineDecryptionSharesFn(decryptionShares); - - const plaintext = decryptWithSharedSecret( - ciphertext, - aad, - sharedSecret, - this.dkgPublicParams - ); - return [plaintext]; + return decryptWithSharedSecret(ciphertext, aad, sharedSecret); } // Retrieve decryption shares @@ -205,7 +194,6 @@ export class CbdTDecDecrypter { return { porterUri: this.porter.porterUrl.toString(), ritualId: this.ritualId, - dkgPublicParams: this.dkgPublicParams.toBytes(), threshold: this.threshold, }; } @@ -217,15 +205,9 @@ export class CbdTDecDecrypter { public static fromObj({ porterUri, ritualId, - dkgPublicParams, threshold, }: CbdTDecDecrypterJSON) { - return new CbdTDecDecrypter( - new Porter(porterUri), - ritualId, - DkgPublicParameters.fromBytes(dkgPublicParams), - threshold - ); + return new CbdTDecDecrypter(new Porter(porterUri), ritualId, threshold); } public static fromJSON(json: string) { diff --git a/src/dkg.ts b/src/dkg.ts index 3569f3cc2..a6a478806 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -4,12 +4,11 @@ import { DecryptionSharePrecomputed, DecryptionShareSimple, DkgPublicKey, - DkgPublicParameters, SharedSecret, } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { bytesEquals } from './utils'; +import { bytesEquals, fromHexString } from './utils'; // TODO: Expose from @nucypher/nucypher-core export enum FerveoVariant { @@ -48,7 +47,6 @@ export function getCombineDecryptionSharesFunction( export interface DkgRitualJSON { id: number; dkgPublicKey: Uint8Array; - dkgPublicParams: Uint8Array; threshold: number; } @@ -56,7 +54,6 @@ export class DkgRitual { constructor( public readonly id: number, public readonly dkgPublicKey: DkgPublicKey, - public readonly dkgPublicParams: DkgPublicParameters, public readonly threshold: number ) {} @@ -64,7 +61,6 @@ export class DkgRitual { return { id: this.id, dkgPublicKey: this.dkgPublicKey.toBytes(), - dkgPublicParams: this.dkgPublicParams.toBytes(), threshold: this.threshold, }; } @@ -72,27 +68,16 @@ export class DkgRitual { public static fromObj({ id, dkgPublicKey, - dkgPublicParams, threshold, }: DkgRitualJSON): DkgRitual { - return new DkgRitual( - id, - DkgPublicKey.fromBytes(dkgPublicKey), - DkgPublicParameters.fromBytes(dkgPublicParams), - threshold - ); + return new DkgRitual(id, DkgPublicKey.fromBytes(dkgPublicKey), threshold); } public equals(other: DkgRitual): boolean { return ( this.id === other.id && // TODO: Replace with `equals` after https://github.com/nucypher/nucypher-core/issues/56 is fixed - bytesEquals(this.dkgPublicKey.toBytes(), other.dkgPublicKey.toBytes()) && - // TODO: Replace with `equals` after https://github.com/nucypher/nucypher-core/issues/56 is fixed - bytesEquals( - this.dkgPublicParams.toBytes(), - other.dkgPublicParams.toBytes() - ) + bytesEquals(this.dkgPublicKey.toBytes(), other.dkgPublicKey.toBytes()) ); } } @@ -112,7 +97,19 @@ export class DkgClient { throw new Error('Invalid provider'); } // TODO: Create a new DKG ritual here - throw new Error('Not implemented'); + const pkWord1 = fromHexString( + '9045795411ed251bf2eecc9415552c41863502a207104ef7ab482bc2364729d9' + ); + console.assert(pkWord1.length === 32); + const pkWord2 = fromHexString('b99e2949cee8d888663b2995fc647fcf'); + // We need to concat two words returned by the DKG contract + const dkgPkBytes = new Uint8Array([...pkWord1, ...pkWord2]); + console.assert(dkgPkBytes.length === 48); + + return { + id: 0, + dkgPublicKey: DkgPublicKey.fromBytes(dkgPkBytes), + } as DkgRitual; } // TODO: Without Validator public key in Coordinator, we cannot verify the diff --git a/src/index.ts b/src/index.ts index f755a3aff..b8caa482b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,9 +28,13 @@ import * as conditions from './conditions'; // TODO: Not sure how to re-export this type from the conditions module export { conditions, CustomContextParam }; +// DKG +export { FerveoVariant } from './dkg'; + // SDK export { Cohort } from './sdk/cohort'; export { DeployedPreStrategy, PreStrategy } from './sdk/strategy/pre-strategy'; +export { DeployedCbdStrategy, CbdStrategy } from './sdk/strategy/cbd-strategy'; // Re-exports export { @@ -41,4 +45,5 @@ export { Signer, TreasureMap, MessageKit, + Ciphertext, } from '@nucypher/nucypher-core'; diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 1e45606a4..68f6bed9a 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -143,7 +143,7 @@ describe('CbdDeployedStrategy', () => { expect(getParticipantsSpy).toHaveBeenCalled(); expect(sessionKeySpy).toHaveBeenCalled(); expect(decryptSpy).toHaveBeenCalled(); - expect(decryptedMessage[0]).toEqual(toBytes(message)); + expect(decryptedMessage).toEqual(toBytes(message)); }); describe('serialization', () => { diff --git a/test/unit/ritual.test.ts b/test/unit/ritual.test.ts new file mode 100644 index 000000000..28ac7a140 --- /dev/null +++ b/test/unit/ritual.test.ts @@ -0,0 +1,19 @@ +import { DkgPublicKey } from '@nucypher/nucypher-core'; + +import { fromHexString } from '../../src/utils'; + +describe('Ritual', () => { + it('deserializes premade dkg ritual', async () => { + const pkWord1 = fromHexString( + '9045795411ed251bf2eecc9415552c41863502a207104ef7ab482bc2364729d9' + ); + const pkWord2 = fromHexString('b99e2949cee8d888663b2995fc647fcf'); + + // We need to concat two words returned by the DKG contract + const dkgPkBytes = new Uint8Array([...pkWord1, ...pkWord2]); + expect(dkgPkBytes.length).toEqual(48); + + const dkgPk = DkgPublicKey.fromBytes(dkgPkBytes); + expect(dkgPk.toBytes()).toEqual(dkgPkBytes); + }); +}); diff --git a/test/utils.ts b/test/utils.ts index cf2311d2e..ed180623f 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -302,7 +302,6 @@ export const fakeTDecFlow = ({ variant, ciphertext, aad, - dkg, message, }: FakeDkgRitualFlow) => { // Having aggregated the transcripts, the validators can now create decryption shares @@ -348,12 +347,7 @@ export const fakeTDecFlow = ({ } // The client should have access to the public parameters of the DKG - const plaintext = decryptWithSharedSecret( - ciphertext, - aad, - sharedSecret, - dkg.publicParams() - ); + const plaintext = decryptWithSharedSecret(ciphertext, aad, sharedSecret); if (!bytesEqual(plaintext, message)) { throw new Error('Decryption failed'); } @@ -500,13 +494,8 @@ export const mockRandomSessionStaticSecret = (secret: SessionStaticSecret) => { export const fakeRitualId = 0; -export const fakeDkgRitual = (ritual: { dkg: Dkg }, thresold: number) => { - return new DkgRitual( - fakeRitualId, - ritual.dkg.publicKey(), - ritual.dkg.publicParams(), - thresold - ); +export const fakeDkgRitual = (ritual: { dkg: Dkg }, threshold: number) => { + return new DkgRitual(fakeRitualId, ritual.dkg.publicKey(), threshold); }; export const mockInitializeRitual = (fakeRitual: unknown) => { diff --git a/yarn.lock b/yarn.lock index 4836111c9..fd815fc63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17,95 +17,95 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.22.5" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.22.0", "@babel/compat-data@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.3.tgz#cd502a6a0b6e37d7ad72ce7e71a7160a3ae36f7e" - integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" + integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.18.10", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.1.tgz#5de51c5206f4c6f5533562838337a603c1033cfd" - integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" + integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.22.0" - "@babel/helper-compilation-targets" "^7.22.1" - "@babel/helper-module-transforms" "^7.22.1" - "@babel/helpers" "^7.22.0" - "@babel/parser" "^7.22.0" - "@babel/template" "^7.21.9" - "@babel/traverse" "^7.22.1" - "@babel/types" "^7.22.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helpers" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.22.0", "@babel/generator@^7.22.3", "@babel/generator@^7.7.2": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" - integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== +"@babel/generator@^7.22.5", "@babel/generator@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" + integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== dependencies: - "@babel/types" "^7.22.3" + "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz#c9b83d1ba74e163e023f008a3d3204588a7ceb60" - integrity sha512-ahEoxgqNoYXm0k22TvOke48i1PkavGu0qGCmcq9ugi6gnmvKNaMjKBSrZTnWUi1CFEeNAUiVba0Wtzm03aSkJg== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878" + integrity sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw== dependencies: - "@babel/types" "^7.22.3" + "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz#bfcd6b7321ffebe33290d68550e2c9d7eb7c7a58" - integrity sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz#fc7319fc54c5e2fa14b2909cf3c5fd3046813e02" + integrity sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw== dependencies: - "@babel/compat-data" "^7.22.0" - "@babel/helper-validator-option" "^7.21.0" + "@babel/compat-data" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz#ae3de70586cc757082ae3eba57240d42f468c41b" - integrity sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.22.1" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.22.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.22.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/helper-create-class-features-plugin@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" + integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" semver "^6.3.0" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz#a7ed9a8488b45b467fca353cd1a44dc5f0cf5c70" - integrity sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz#bb2bf0debfe39b831986a4efbf4066586819c6e4" + integrity sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" regexpu-core "^5.3.1" semver "^6.3.0" @@ -121,182 +121,177 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" - integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== - -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" - -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-member-expression-to-functions@^7.22.0": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz#4b77a12c1b4b8e9e28736ed47d8b91f00976911f" - integrity sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA== - dependencies: - "@babel/types" "^7.22.3" - -"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" - integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== - dependencies: - "@babel/types" "^7.21.4" - -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz#e0cad47fedcf3cae83c11021696376e2d5a50c63" - integrity sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw== - dependencies: - "@babel/helper-environment-visitor" "^7.22.1" - "@babel/helper-module-imports" "^7.21.4" - "@babel/helper-simple-access" "^7.21.5" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.21.9" - "@babel/traverse" "^7.22.1" - "@babel/types" "^7.22.0" - -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" - integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== - -"@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz#38cf6e56f7dc614af63a21b45565dd623f0fdc95" - integrity sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.1" - "@babel/helper-member-expression-to-functions" "^7.22.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.21.9" - "@babel/traverse" "^7.22.1" - "@babel/types" "^7.22.0" - -"@babel/helper-simple-access@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" - integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== - dependencies: - "@babel/types" "^7.21.5" - -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== - dependencies: - "@babel/types" "^7.20.0" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" - integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== - -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-option@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" - integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== - -"@babel/helper-wrap-function@^7.18.9": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" - integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== - dependencies: - "@babel/helper-function-name" "^7.19.0" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - -"@babel/helpers@^7.22.0": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.3.tgz#53b74351da9684ea2f694bf0877998da26dd830e" - integrity sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w== - dependencies: - "@babel/template" "^7.21.9" - "@babel/traverse" "^7.22.1" - "@babel/types" "^7.22.3" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" + integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-remap-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz#14a38141a7bf2165ad38da61d61cf27b43015da2" + integrity sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-replace-supers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" + integrity sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" + integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + +"@babel/helper-wrap-function@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz#44d205af19ed8d872b4eefb0d2fa65f45eb34f06" + integrity sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helpers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.5.tgz#74bb4373eb390d1ceed74a15ef97767e63120820" + integrity sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q== + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/highlight@^7.10.4", "@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.9", "@babel/parser@^7.22.0", "@babel/parser@^7.22.4": - version "7.22.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" - integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" + integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" + integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz#a75be1365c0c3188c51399a662168c1c98108659" - integrity sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" + integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-transform-optional-chaining" "^7.22.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.5" -"@babel/plugin-proposal-private-property-in-object@^7.21.0": - version "7.21.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c" - integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.21.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.18.6" @@ -348,19 +343,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" - integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== +"@babel/plugin-syntax-import-assertions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-import-attributes@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.3.tgz#d7168f22b9b49a6cc1792cec78e06a18ad2e7b4b" - integrity sha512-i35jZJv6aO7hxEbIWQ41adVfOzjm9dcYDNeWlBMd8p0ZQRtNUCBrmGwZt+H5lb+oOC9a3svp956KP0oWGA1YsA== +"@babel/plugin-syntax-import-attributes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" + integrity sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -433,11 +428,11 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" - integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" + integrity sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" @@ -447,413 +442,413 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" - integrity sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA== +"@babel/plugin-transform-arrow-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.3.tgz#3ed99924c354fb9e80dabb2cc8d002c702e94527" - integrity sha512-36A4Aq48t66btydbZd5Fk0/xJqbpg/v4QWI4AH4cYHBXy9Mu42UOupZpebKFiCFNT9S9rJFcsld0gsv0ayLjtA== +"@babel/plugin-transform-async-generator-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.5.tgz#7336356d23380eda9a56314974f053a020dab0c3" + integrity sha512-gGOEvFzm3fWoyD5uZq7vVTD57pPJ3PczPUD/xCFGjzBpUosnklmXyKnGQbbbGs1NPNPskFex0j93yKbHt0cHyg== dependencies: - "@babel/helper-environment-visitor" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-transform-async-to-generator@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== +"@babel/plugin-transform-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" + integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" -"@babel/plugin-transform-block-scoped-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== +"@babel/plugin-transform-block-scoped-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-block-scoping@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" - integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== +"@babel/plugin-transform-block-scoping@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" + integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-properties@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.3.tgz#3407145e513830df77f0cef828b8b231c166fe4c" - integrity sha512-mASLsd6rhOrLZ5F3WbCxkzl67mmOnqik0zrg5W6D/X0QMW7HtvnoL1dRARLKIbMP3vXwkwziuLesPqWVGIl6Bw== +"@babel/plugin-transform-class-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" + integrity sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-static-block@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.3.tgz#e352cf33567385c731a8f21192efeba760358773" - integrity sha512-5BirgNWNOx7cwbTJCOmKFJ1pZjwk5MUfMIwiBBvsirCJMZeQgs5pk6i1OlkVg+1Vef5LfBahFOrdCnAWvkVKMw== +"@babel/plugin-transform-class-static-block@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" + integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" - integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/plugin-transform-classes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz#635d4e98da741fad814984639f4c0149eb0135e1" + integrity sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz#3a2d8bb771cd2ef1cd736435f6552fe502e11b44" - integrity sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q== +"@babel/plugin-transform-computed-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/template" "^7.20.7" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" -"@babel/plugin-transform-destructuring@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" - integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== +"@babel/plugin-transform-destructuring@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" + integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== +"@babel/plugin-transform-dotall-regex@^7.22.5", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" + integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-duplicate-keys@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== +"@babel/plugin-transform-duplicate-keys@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" + integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dynamic-import@^7.22.1": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.1.tgz#6c56afaf896a07026330cf39714532abed8d9ed1" - integrity sha512-rlhWtONnVBPdmt+jeewS0qSnMz/3yLFrqAP8hHC6EDcrYRSyuz9f9yQhHvVn2Ad6+yO9fHXac5piudeYrInxwQ== +"@babel/plugin-transform-dynamic-import@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" + integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-transform-exponentiation-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== +"@babel/plugin-transform-exponentiation-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" + integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-export-namespace-from@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.3.tgz#9b8700aa495007d3bebac8358d1c562434b680b9" - integrity sha512-5Ti1cHLTDnt3vX61P9KZ5IG09bFXp4cDVFJIAeCZuxu9OXXJJZp5iP0n/rzM2+iAutJY+KWEyyHcRaHlpQ/P5g== +"@babel/plugin-transform-export-namespace-from@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" + integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-for-of@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" - integrity sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ== +"@babel/plugin-transform-for-of@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" + integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== +"@babel/plugin-transform-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-json-strings@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.3.tgz#a181b8679cf7c93e9d0e3baa5b1776d65be601a9" - integrity sha512-IuvOMdeOOY2X4hRNAT6kwbePtK21BUyrAEgLKviL8pL6AEEVUVcqtRdN/HJXBLGIbt9T3ETmXRnFedRRmQNTYw== +"@babel/plugin-transform-json-strings@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" + integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-transform-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== +"@babel/plugin-transform-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-logical-assignment-operators@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.3.tgz#9e021455810f33b0baccb82fb759b194f5dc36f0" - integrity sha512-CbayIfOw4av2v/HYZEsH+Klks3NC2/MFIR3QR8gnpGNNPEaq2fdlVCRYG/paKs7/5hvBLQ+H70pGWOHtlNEWNA== +"@babel/plugin-transform-logical-assignment-operators@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" + integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-transform-member-expression-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== +"@babel/plugin-transform-member-expression-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-amd@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" - integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== +"@babel/plugin-transform-modules-amd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" + integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" - integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== +"@babel/plugin-transform-modules-commonjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== dependencies: - "@babel/helper-module-transforms" "^7.21.5" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-simple-access" "^7.21.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-systemjs@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz#cc507e03e88d87b016feaeb5dae941e6ef50d91e" - integrity sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw== +"@babel/plugin-transform-modules-systemjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" + integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" -"@babel/plugin-transform-modules-umd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== +"@babel/plugin-transform-modules-umd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" + integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-named-capturing-groups-regex@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz#db6fb77e6b3b53ec3b8d370246f0b7cf67d35ab4" - integrity sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q== +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-new-target@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz#deb0377d741cbee2f45305868b9026dcd6dd96e2" - integrity sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w== +"@babel/plugin-transform-new-target@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" + integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-nullish-coalescing-operator@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.3.tgz#8c519f8bf5af94a9ca6f65cf422a9d3396e542b9" - integrity sha512-CpaoNp16nX7ROtLONNuCyenYdY/l7ZsR6aoVa7rW7nMWisoNoQNIH5Iay/4LDyRjKMuElMqXiBoOQCDLTMGZiw== +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" + integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-transform-numeric-separator@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.3.tgz#02493070ca6685884b0eee705363ee4da2132ab0" - integrity sha512-+AF88fPDJrnseMh5vD9+SH6wq4ZMvpiTMHh58uLs+giMEyASFVhcT3NkoyO+NebFCNnpHJEq5AXO2txV4AGPDQ== +"@babel/plugin-transform-numeric-separator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" + integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-transform-object-rest-spread@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.3.tgz#da6fba693effb8c203d8c3bdf7bf4e2567e802e9" - integrity sha512-38bzTsqMMCI46/TQnJwPPpy33EjLCc1Gsm2hRTF6zTMWnKsN61vdrpuzIEGQyKEhDSYDKyZHrrd5FMj4gcUHhw== +"@babel/plugin-transform-object-rest-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" + integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== dependencies: - "@babel/compat-data" "^7.22.3" - "@babel/helper-compilation-targets" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/compat-data" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.22.3" + "@babel/plugin-transform-parameters" "^7.22.5" -"@babel/plugin-transform-object-super@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== +"@babel/plugin-transform-object-super@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" -"@babel/plugin-transform-optional-catch-binding@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.3.tgz#e971a083fc7d209d9cd18253853af1db6d8dc42f" - integrity sha512-bnDFWXFzWY0BsOyqaoSXvMQ2F35zutQipugog/rqotL2S4ciFOKlRYUu9djt4iq09oh2/34hqfRR2k1dIvuu4g== +"@babel/plugin-transform-optional-catch-binding@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" + integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz#5fd24a4a7843b76da6aeec23c7f551da5d365290" - integrity sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg== +"@babel/plugin-transform-optional-chaining@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.5.tgz#1003762b9c14295501beb41be72426736bedd1e0" + integrity sha512-AconbMKOMkyG+xCng2JogMCDcqW8wedQAqpVIL4cOSescZ7+iW8utC6YDZLMCSUIReEA733gzRSaOSXMAt/4WQ== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-transform-parameters@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz#24477acfd2fd2bc901df906c9bf17fbcfeee900d" - integrity sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw== +"@babel/plugin-transform-parameters@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" + integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-methods@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.3.tgz#adac38020bab5047482d3297107c1f58e9c574f6" - integrity sha512-fC7jtjBPFqhqpPAE+O4LKwnLq7gGkD3ZmC2E3i4qWH34mH3gOg2Xrq5YMHUq6DM30xhqM1DNftiRaSqVjEG+ug== +"@babel/plugin-transform-private-methods@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" + integrity sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-property-in-object@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.3.tgz#031621b02c7b7d95389de1a3dba2fe9e8c548e56" - integrity sha512-C7MMl4qWLpgVCbXfj3UW8rR1xeCnisQ0cU7YJHV//8oNBS0aCIVg1vFnZXxOckHhEpQyqNNkWmvSEWnMLlc+Vw== +"@babel/plugin-transform-private-property-in-object@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" + integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-transform-property-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== +"@babel/plugin-transform-property-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-regenerator@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz#576c62f9923f94bcb1c855adc53561fd7913724e" - integrity sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w== +"@babel/plugin-transform-regenerator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa" + integrity sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" regenerator-transform "^0.15.1" -"@babel/plugin-transform-reserved-words@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== +"@babel/plugin-transform-reserved-words@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" + integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-shorthand-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== +"@babel/plugin-transform-shorthand-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-spread@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== +"@babel/plugin-transform-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" -"@babel/plugin-transform-sticky-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== +"@babel/plugin-transform-sticky-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" + integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-template-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== +"@babel/plugin-transform-template-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typeof-symbol@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== +"@babel/plugin-transform-typeof-symbol@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" + integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-escapes@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" - integrity sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg== +"@babel/plugin-transform-unicode-escapes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c" + integrity sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg== dependencies: - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-property-regex@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.3.tgz#597b6a614dc93eaae605ee293e674d79d32eb380" - integrity sha512-5ScJ+OmdX+O6HRuMGW4kv7RL9vIKdtdAj9wuWUKy1wbHY3jaM/UlyIiC1G7J6UJiiyMukjjK0QwL3P0vBd0yYg== +"@babel/plugin-transform-unicode-property-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" + integrity sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== +"@babel/plugin-transform-unicode-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" + integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-sets-regex@^7.22.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.3.tgz#7c14ee33fa69782b0101d0f7143d3fc73ce00700" - integrity sha512-hNufLdkF8vqywRp+P55j4FHXqAX2LRUccoZHH7AFn1pq5ZOO2ISKW9w13bFZVjBoTqeve2HOgoJCcaziJVhGNw== +"@babel/plugin-transform-unicode-sets-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" + integrity sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.15.6": - version "7.22.4" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.4.tgz#c86a82630f0e8c61d9bb9327b7b896732028cbed" - integrity sha512-c3lHOjbwBv0TkhYCr+XCR6wKcSZ1QbQTVdSkZUaVpLv8CVWotBMArWUi5UAJrcrQaEnleVkkvaV8F/pmc/STZQ== - dependencies: - "@babel/compat-data" "^7.22.3" - "@babel/helper-compilation-targets" "^7.22.1" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-validator-option" "^7.21.0" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.3" - "@babel/plugin-proposal-private-property-in-object" "^7.21.0" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.5.tgz#3da66078b181f3d62512c51cf7014392c511504e" + integrity sha512-fj06hw89dpiZzGZtxn+QybifF07nNiZjZ7sazs2aVDcysAZVGjW7+7iFYxg6GLNM47R/thYfLdrXc+2f11Vi9A== + dependencies: + "@babel/compat-data" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.20.0" - "@babel/plugin-syntax-import-attributes" "^7.22.3" + "@babel/plugin-syntax-import-assertions" "^7.22.5" + "@babel/plugin-syntax-import-attributes" "^7.22.5" "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -865,56 +860,56 @@ "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.21.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.3" - "@babel/plugin-transform-async-to-generator" "^7.20.7" - "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.21.0" - "@babel/plugin-transform-class-properties" "^7.22.3" - "@babel/plugin-transform-class-static-block" "^7.22.3" - "@babel/plugin-transform-classes" "^7.21.0" - "@babel/plugin-transform-computed-properties" "^7.21.5" - "@babel/plugin-transform-destructuring" "^7.21.3" - "@babel/plugin-transform-dotall-regex" "^7.18.6" - "@babel/plugin-transform-duplicate-keys" "^7.18.9" - "@babel/plugin-transform-dynamic-import" "^7.22.1" - "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-export-namespace-from" "^7.22.3" - "@babel/plugin-transform-for-of" "^7.21.5" - "@babel/plugin-transform-function-name" "^7.18.9" - "@babel/plugin-transform-json-strings" "^7.22.3" - "@babel/plugin-transform-literals" "^7.18.9" - "@babel/plugin-transform-logical-assignment-operators" "^7.22.3" - "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.20.11" - "@babel/plugin-transform-modules-commonjs" "^7.21.5" - "@babel/plugin-transform-modules-systemjs" "^7.22.3" - "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.3" - "@babel/plugin-transform-new-target" "^7.22.3" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.3" - "@babel/plugin-transform-numeric-separator" "^7.22.3" - "@babel/plugin-transform-object-rest-spread" "^7.22.3" - "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-optional-catch-binding" "^7.22.3" - "@babel/plugin-transform-optional-chaining" "^7.22.3" - "@babel/plugin-transform-parameters" "^7.22.3" - "@babel/plugin-transform-private-methods" "^7.22.3" - "@babel/plugin-transform-private-property-in-object" "^7.22.3" - "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.21.5" - "@babel/plugin-transform-reserved-words" "^7.18.6" - "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.20.7" - "@babel/plugin-transform-sticky-regex" "^7.18.6" - "@babel/plugin-transform-template-literals" "^7.18.9" - "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.21.5" - "@babel/plugin-transform-unicode-property-regex" "^7.22.3" - "@babel/plugin-transform-unicode-regex" "^7.18.6" - "@babel/plugin-transform-unicode-sets-regex" "^7.22.3" + "@babel/plugin-transform-arrow-functions" "^7.22.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.5" + "@babel/plugin-transform-async-to-generator" "^7.22.5" + "@babel/plugin-transform-block-scoped-functions" "^7.22.5" + "@babel/plugin-transform-block-scoping" "^7.22.5" + "@babel/plugin-transform-class-properties" "^7.22.5" + "@babel/plugin-transform-class-static-block" "^7.22.5" + "@babel/plugin-transform-classes" "^7.22.5" + "@babel/plugin-transform-computed-properties" "^7.22.5" + "@babel/plugin-transform-destructuring" "^7.22.5" + "@babel/plugin-transform-dotall-regex" "^7.22.5" + "@babel/plugin-transform-duplicate-keys" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.5" + "@babel/plugin-transform-exponentiation-operator" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.5" + "@babel/plugin-transform-for-of" "^7.22.5" + "@babel/plugin-transform-function-name" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.5" + "@babel/plugin-transform-literals" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" + "@babel/plugin-transform-member-expression-literals" "^7.22.5" + "@babel/plugin-transform-modules-amd" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-modules-systemjs" "^7.22.5" + "@babel/plugin-transform-modules-umd" "^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" + "@babel/plugin-transform-numeric-separator" "^7.22.5" + "@babel/plugin-transform-object-rest-spread" "^7.22.5" + "@babel/plugin-transform-object-super" "^7.22.5" + "@babel/plugin-transform-optional-catch-binding" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.5" + "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-private-methods" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.5" + "@babel/plugin-transform-property-literals" "^7.22.5" + "@babel/plugin-transform-regenerator" "^7.22.5" + "@babel/plugin-transform-reserved-words" "^7.22.5" + "@babel/plugin-transform-shorthand-properties" "^7.22.5" + "@babel/plugin-transform-spread" "^7.22.5" + "@babel/plugin-transform-sticky-regex" "^7.22.5" + "@babel/plugin-transform-template-literals" "^7.22.5" + "@babel/plugin-transform-typeof-symbol" "^7.22.5" + "@babel/plugin-transform-unicode-escapes" "^7.22.5" + "@babel/plugin-transform-unicode-property-regex" "^7.22.5" + "@babel/plugin-transform-unicode-regex" "^7.22.5" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.22.4" + "@babel/types" "^7.22.5" babel-plugin-polyfill-corejs2 "^0.4.3" babel-plugin-polyfill-corejs3 "^0.8.1" babel-plugin-polyfill-regenerator "^0.5.0" @@ -938,44 +933,44 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.8.4": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" - integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" + integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.21.9", "@babel/template@^7.3.3": - version "7.21.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" - integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/parser" "^7.21.9" - "@babel/types" "^7.21.5" - -"@babel/traverse@^7.20.5", "@babel/traverse@^7.22.1", "@babel/traverse@^7.7.2": - version "7.22.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.4.tgz#c3cf96c5c290bd13b55e29d025274057727664c0" - integrity sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.22.3" - "@babel/helper-environment-visitor" "^7.22.1" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.22.4" - "@babel/types" "^7.22.4" +"@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.22.5", "@babel/traverse@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" + integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.22.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" - integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== dependencies: - "@babel/helper-string-parser" "^7.21.5" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1697,10 +1692,8 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nucypher/nucypher-core@^0.9.0": +"@nucypher/nucypher-core@../nucypher-core/nucypher-core-wasm/pkg": version "0.9.0" - resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.9.0.tgz#7bb8a41cce4978121256ca3e03fc9dc37ce7de91" - integrity sha512-X0aI14W1wSWm08agKrShpvvDMbvkqc7QJTi0KhdwxkMZxwDLxzunzPbLmet8KzinvmpsoCIssWmYr11V0XmfdQ== "@sideway/address@^4.1.3": version "4.1.4" @@ -1872,9 +1865,9 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "20.2.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb" - integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ== + version "20.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" + integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2032,9 +2025,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + version "8.9.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" + integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== add-stream@^1.0.0: version "1.0.0" @@ -2396,12 +2389,12 @@ browser-process-hrtime@^1.0.0: integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.7" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.7.tgz#e2b420947e5fb0a58e8f4668ae6e23488127e551" - integrity sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA== + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== dependencies: - caniuse-lite "^1.0.30001489" - electron-to-chromium "^1.4.411" + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" node-releases "^2.0.12" update-browserslist-db "^1.0.11" @@ -2469,10 +2462,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001489: - version "1.0.30001495" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz#64a0ccef1911a9dcff647115b4430f8eff1ef2d9" - integrity sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg== +caniuse-lite@^1.0.30001503: + version "1.0.30001503" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz#88b6ff1b2cf735f1f3361dc1a15b59f0561aa398" + integrity sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw== chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" @@ -2507,9 +2500,9 @@ ci-info@^3.2.0: integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== cli-cursor@^3.1.0: version "3.1.0" @@ -2830,9 +2823,9 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== core-js-compat@^3.30.1, core-js-compat@^3.30.2: - version "3.30.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" - integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== + version "3.31.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.0.tgz#4030847c0766cc0e803dcdfb30055d7ef2064bf1" + integrity sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw== dependencies: browserslist "^4.21.5" @@ -3100,10 +3093,10 @@ dotgitignore@^2.1.0: find-up "^3.0.0" minimatch "^3.0.4" -electron-to-chromium@^1.4.411: - version "1.4.423" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.423.tgz#99567f3a0563fe0d1d0931e9ce851bca239f6658" - integrity sha512-y4A7YfQcDGPAeSWM1IuoWzXpg9RY1nwHzHSwRtCSQFp9FgAVDgdWlFf0RbdWfLWQ2WUI+bddUgk5RgTjqRE6FQ== +electron-to-chromium@^1.4.431: + version "1.4.432" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.432.tgz#154a69d5ead974347f534aea4d28b03c7149fd7b" + integrity sha512-yz3U/khQgAFT2HURJA3/F4fKIyO2r5eK09BQzBZFd6BvBSSaRuzKc2ZNBHtJcO75/EKiRYbVYJZ2RB0P4BuD2g== elliptic@6.5.4: version "6.5.4" @@ -6177,9 +6170,9 @@ scrypt-js@3.0.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.x, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" - integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== dependencies: lru-cache "^6.0.0" From 4cb467a65ead4c321ac5a6372e1e21d697265c3e Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 27 Jun 2023 14:32:12 +0200 Subject: [PATCH 87/98] tdec runs e2e --- abi/Coordinator.json | 1163 +++++++++++++------------- package.json | 2 +- src/agents/coordinator.ts | 22 +- src/characters/cbd-recipient.ts | 28 +- src/characters/enrico.ts | 6 +- src/characters/porter.ts | 21 +- src/conditions/compound-condition.ts | 2 +- src/conditions/condition-expr.ts | 6 +- src/conditions/context/context.ts | 2 +- src/dkg.ts | 38 +- src/sdk/strategy/cbd-strategy.ts | 5 +- src/utils.ts | 4 + test/unit/cbd-strategy.test.ts | 10 +- test/unit/ritual.test.ts | 2 +- test/utils.ts | 4 +- yarn.lock | 51 +- 16 files changed, 680 insertions(+), 686 deletions(-) diff --git a/abi/Coordinator.json b/abi/Coordinator.json index 604b03c6d..c572ea259 100644 --- a/abi/Coordinator.json +++ b/abi/Coordinator.json @@ -1,605 +1,572 @@ -{ - "abi": [ - { - "inputs": [ - { - "internalType": "contract IAccessControlApplication", - "name": "app", - "type": "address" - }, - { - "internalType": "uint32", - "name": "_timeout", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "_maxDkgSize", - "type": "uint32" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - }, - { - "indexed": true, - "internalType": "address", - "name": "node", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "aggregatedTranscriptDigest", - "type": "bytes32" - } - ], - "name": "AggregationPosted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - }, - { - "indexed": true, - "internalType": "address", - "name": "initiator", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "successful", - "type": "bool" - } - ], - "name": "EndRitual", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint32", - "name": "oldSize", - "type": "uint32" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "newSize", - "type": "uint32" - } - ], - "name": "MaxDkgSizeChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - } - ], - "name": "StartAggregationRound", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - }, - { - "indexed": true, - "internalType": "address", - "name": "initiator", - "type": "address" - }, - { - "indexed": false, - "internalType": "address[]", - "name": "participants", - "type": "address[]" - } - ], - "name": "StartRitual", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint32", - "name": "oldTimeout", - "type": "uint32" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "newTimeout", - "type": "uint32" - } - ], - "name": "TimeoutChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - }, - { - "indexed": true, - "internalType": "address", - "name": "node", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "transcriptDigest", - "type": "bytes32" - } - ], - "name": "TranscriptPosted", - "type": "event" - }, - { - "inputs": [], - "name": "application", - "outputs": [ - { - "internalType": "contract IAccessControlApplication", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "nodes", - "type": "address[]" - } - ], - "name": "cohortFingerprint", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "ritualID", - "type": "uint256" - }, - { - "internalType": "address", - "name": "provider", - "type": "address" - } - ], - "name": "getParticipantFromProvider", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "provider", - "type": "address" - }, - { - "internalType": "bool", - "name": "aggregated", - "type": "bool" - }, - { - "internalType": "bytes", - "name": "transcript", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "decryptionRequestStaticKey", - "type": "bytes" - } - ], - "internalType": "struct Coordinator.Participant", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - } - ], - "name": "getParticipants", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "provider", - "type": "address" - }, - { - "internalType": "bool", - "name": "aggregated", - "type": "bool" - }, - { - "internalType": "bytes", - "name": "transcript", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "decryptionRequestStaticKey", - "type": "bytes" - } - ], - "internalType": "struct Coordinator.Participant[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "ritualId", - "type": "uint256" - } - ], - "name": "getRitualState", - "outputs": [ - { - "internalType": "enum Coordinator.RitualState", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "providers", - "type": "address[]" - } - ], - "name": "initiateRitual", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "maxDkgSize", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "numberOfRituals", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - }, - { - "internalType": "bytes", - "name": "aggregatedTranscript", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "word0", - "type": "bytes32" - }, - { - "internalType": "bytes16", - "name": "word1", - "type": "bytes16" - } - ], - "internalType": "struct BLS12381.G1Point", - "name": "publicKey", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "decryptionRequestStaticKey", - "type": "bytes" - } - ], - "name": "postAggregation", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "ritualId", - "type": "uint32" - }, - { - "internalType": "bytes", - "name": "transcript", - "type": "bytes" - } - ], - "name": "postTranscript", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "rituals", - "outputs": [ - { - "internalType": "address", - "name": "initiator", - "type": "address" - }, - { - "internalType": "uint32", - "name": "dkgSize", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "initTimestamp", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "totalTranscripts", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "totalAggregations", - "type": "uint32" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "word0", - "type": "bytes32" - }, - { - "internalType": "bytes16", - "name": "word1", - "type": "bytes16" - } - ], - "internalType": "struct BLS12381.G1Point", - "name": "publicKey", - "type": "tuple" - }, - { - "internalType": "bool", - "name": "aggregationMismatch", - "type": "bool" - }, - { - "internalType": "bytes", - "name": "aggregatedTranscript", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "newSize", - "type": "uint32" - } - ], - "name": "setMaxDkgSize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "newTimeout", - "type": "uint32" - } - ], - "name": "setTimeout", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "timeout", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "contractName": "Coordinator", - "deploymentBytecode": { - "bytecode": "0x60a06040523480156200001157600080fd5b506040516200208b3803806200208b8339810160408190526200003491620000e7565b6200003f336200007d565b6001600160a01b039092166080526002805463ffffffff938416640100000000026001600160401b031990911693909216929092171790556200013e565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805163ffffffff81168114620000e257600080fd5b919050565b600080600060608486031215620000fd57600080fd5b83516001600160a01b03811681146200011557600080fd5b92506200012560208501620000cd565b91506200013560408501620000cd565b90509250925092565b608051611f0e6200017d600039600081816101620152818161049101528181610b0001528181610ba601528181610ed90152610f7f0152611f0e6000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c8063715018a6116100a2578063948c73ea11610071578063948c73ea1461025f5780639c48937b1461027f578063dc80d104146102a0578063f1e0ff19146102b3578063f2fde38b146102bb57600080fd5b8063715018a6146102205780638b5eeb3f146102285780638cf950611461023b5780638da5cb5b1461024e57600080fd5b80633d796abc116100de5780633d796abc146101b45780635e748733146101db5780636b75f53e146101fb57806370dea79a1461021057600080fd5b80631057de0114610110578063214b02ad1461013d57806326e4ca821461015d5780632f2eaebc1461019c575b600080fd5b61012361011e3660046116d7565b6102ce565b60405163ffffffff90911681526020015b60405180910390f35b61015061014b366004611765565b6105aa565b604051610134919061181b565b6101847f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610134565b60025461012390640100000000900463ffffffff1681565b6101c76101c236600461187d565b610785565b604051610134989796959493929190611896565b6101ee6101e936600461192a565b6108ac565b604051610134919061195a565b61020e6102093660046119b6565b610a53565b005b6002546101239063ffffffff1681565b61020e610d9c565b61020e610236366004611765565b610db0565b61020e610249366004611a09565b610e2c565b6000546001600160a01b0316610184565b61027261026d36600461187d565b611315565b6040516101349190611abf565b61029261028d3660046116d7565b611340565b604051908152602001610134565b61020e6102ae366004611765565b611373565b600154610292565b61020e6102c9366004611ae7565b6113db565b600081600281108015906102f25750600254640100000000900463ffffffff168111155b6103435760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206e756d626572206f66206e6f64657300000000000000000060448201526064015b60405180910390fd5b6001805480820182556000918252600781027fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180544263ffffffff908116600160c01b0263ffffffff60c01b19918716600160a01b0263ffffffff60a01b1933166001600160c01b03199094169390931792909217161781559091805b8481101561055257600683018054600181018255600091825260208220600390910201908989848181106103f7576103f7611b04565b905060200201602081019061040c9190611ae7565b9050806001600160a01b0316846001600160a01b03161061046f5760405162461bcd60e51b815260206004820152601860248201527f50726f766964657273206d75737420626520736f727465640000000000000000604482015260640161033a565b60405163c4903d5b60e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063c4903d5b90602401602060405180830381865afa1580156104da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fe9190611b1a565b6001600160601b0316116105245760405162461bcd60e51b815260040161033a90611b43565b81546001600160a01b0319166001600160a01b0382161790915591508061054a81611b90565b9150506103c1565b50336001600160a01b03168363ffffffff167fa4e3b0c0b125669bbe2cfcbf57b6b13b68dc908d343dde9bdf0df75081ae403f8989604051610595929190611ba9565b60405180910390a35090925050505b92915050565b6060600060018363ffffffff16815481106105c7576105c7611b04565b9060005260206000209060070201905080600601805480602002602001604051908101604052809291908181526020016000905b82821015610779576000848152602090819020604080516080810182526003860290920180546001600160a01b0381168452600160a01b900460ff161515938301939093526001830180549293929184019161065690611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461068290611bf7565b80156106cf5780601f106106a4576101008083540402835291602001916106cf565b820191906000526020600020905b8154815290600101906020018083116106b257829003601f168201915b505050505081526020016002820180546106e890611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461071490611bf7565b80156107615780601f1061073657610100808354040283529160200191610761565b820191906000526020600020905b81548152906001019060200180831161074457829003601f168201915b505050505081525050815260200190600101906105fb565b50505050915050919050565b6001818154811061079557600080fd5b600091825260209182902060079190910201805460018201546040805180820190915260028401548152600384015460801b6001600160801b0319169481019490945260048301546005840180546001600160a01b0385169750600160a01b850463ffffffff90811697600160c01b8704821697600160e01b90970482169691909516949360ff1692909161082990611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461085590611bf7565b80156108a25780601f10610877576101008083540402835291602001916108a2565b820191906000526020600020905b81548152906001019060200180831161088557829003601f168201915b5050505050905088565b60408051608081018252600080825260208201526060918101829052818101919091526108f9600184815481106108e5576108e5611b04565b906000526020600020906007020183611454565b6040805160808101825282546001600160a01b0381168252600160a01b900460ff161515602082015260018301805491939284019161093790611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461096390611bf7565b80156109b05780601f10610985576101008083540402835291602001916109b0565b820191906000526020600020905b81548152906001019060200180831161099357829003601f168201915b505050505081526020016002820180546109c990611bf7565b80601f01602080910402602001604051908101604052809291908181526020018280546109f590611bf7565b8015610a425780601f10610a1757610100808354040283529160200191610a42565b820191906000526020600020905b815481529060010190602001808311610a2557829003601f168201915b505050505081525050905092915050565b600060018463ffffffff1681548110610a6e57610a6e611b04565b6000918252602090912060079091020190506001610a8b8261150a565b6005811115610a9c57610a9c611aa9565b14610ae95760405162461bcd60e51b815260206004820152601b60248201527f4e6f742077616974696e6720666f72207472616e736372697074730000000000604482015260640161033a565b60405162dca53b60e81b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dca53b0090602401602060405180830381865afa158015610b4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b739190611c31565b90506000610b818383611454565b60405163c4903d5b60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063c4903d5b90602401602060405180830381865afa158015610bed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c119190611b1a565b6001600160601b031611610c375760405162461bcd60e51b815260040161033a90611b43565b806001018054610c4690611bf7565b159050610c955760405162461bcd60e51b815260206004820152601e60248201527f4e6f646520616c726561647920706f73746564207472616e7363726970740000604482015260640161033a565b60008585604051610ca7929190611c4e565b604051908190039020905060018201610cc1868883611cbf565b50826001600160a01b03168763ffffffff167f66568b934e848078c9787d6a66dae153eaae57f0ec3a553c11939fcdcf9c11fb83604051610d0491815260200190565b60405180910390a38354600160e01b900463ffffffff1684601c610d2783611d80565b82546101009290920a63ffffffff8181021990931691831602179091558554600160a01b81048216600160e01b909104909116039050610d935760405163ffffffff8816907fca79a3f8fdffa27f8c0a30733144db5a5bd2663f1f2561d8d665bf4da6a3dfbe90600090a25b50505050505050565b610da46115f7565b610dae6000611651565b565b610db86115f7565b6002546040805163ffffffff6401000000009093048316815291831660208301527fbb0cedd628c5ad0619627014b51dff9ab8676ce038340c26d817b8229740d0c9910160405180910390a16002805463ffffffff9092166401000000000267ffffffff0000000019909216919091179055565b600060018763ffffffff1681548110610e4757610e47611b04565b6000918252602090912060079091020190506002610e648261150a565b6005811115610e7557610e75611aa9565b14610ec25760405162461bcd60e51b815260206004820152601c60248201527f4e6f742077616974696e6720666f72206167677265676174696f6e7300000000604482015260640161033a565b60405162dca53b60e81b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dca53b0090602401602060405180830381865afa158015610f28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4c9190611c31565b90506000610f5a8383611454565b60405163c4903d5b60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063c4903d5b90602401602060405180830381865afa158015610fc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fea9190611b1a565b6001600160601b0316116110105760405162461bcd60e51b815260040161033a90611b43565b8054600160a01b900460ff16156110695760405162461bcd60e51b815260206004820152601f60248201527f4e6f646520616c726561647920706f73746564206167677265676174696f6e00604482015260640161033a565b80600201805461107890611bf7565b1590506110dc5760405162461bcd60e51b815260206004820152602c60248201527f4e6f646520616c72656164792070726f7669646564207265717565737420656e60448201526b6372797074696e67206b657960a01b606482015260840161033a565b600088886040516110ee929190611c4e565b604051908190039020825460ff60a01b1916600160a01b178355905060028201611119868883611cbf565b50826001600160a01b03168a63ffffffff167f1884446739eb06b60e314b4c3b25f08b5c7377f20538c132ce7c064f38272bac8360405161115c91815260200190565b60405180910390a383600501805461117390611bf7565b90506000036111a3576005840161118b898b83611cbf565b50866002850161119b8282611db9565b90505061126e565b6040805180820190915260028501548152600385015460801b6001600160801b03191660208201526111e3906111de368a90038a018a611de8565b6116a1565b1580611207575080846005016040516111fc9190611e3e565b604051809103902014155b1561126e5760048401805460ff191660011790558354604051600081526001600160a01b039091169063ffffffff8c16907f9dc7c9243191ecf8e0264232a3cb660035e1563d41b1812f0fea00955a291a589060200160405180910390a35050505061130d565b60018401805463ffffffff1690600061128683611d80565b82546101009290920a63ffffffff81810219909316918316021790915585546001870154600160a01b90910482169116039050611308578354604051600181526001600160a01b039091169063ffffffff8c16907f9dc7c9243191ecf8e0264232a3cb660035e1563d41b1812f0fea00955a291a589060200160405180910390a35b505050505b505050505050565b60006105a46001838154811061132d5761132d611b04565b906000526020600020906007020161150a565b60008282604051602001611355929190611ba9565b60405160208183030381529060405280519060200120905092915050565b61137b6115f7565b6002546040805163ffffffff928316815291831660208301527feb65c6287031cadd2d71b59499e985dddd00f14b3a8b2ce8d951da00f29995f6910160405180910390a16002805463ffffffff191663ffffffff92909216919091179055565b6113e36115f7565b6001600160a01b0381166114485760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161033a565b61145181611651565b50565b6006820154600090815b818110156114c157600085600601828154811061147d5761147d611b04565b6000918252602090912060039091020180549091506001600160a01b038087169116036114ae5792506105a4915050565b50806114b981611b90565b91505061145e565b5060405162461bcd60e51b815260206004820152601e60248201527f5061727469636970616e74206e6f742070617274206f662072697475616c0000604482015260640161033a565b805460025460009163ffffffff600160c01b909104811691839161152f911683611eb4565b90508163ffffffff16600003611549575060009392505050565b8354600185015463ffffffff600160a01b9092048216911603611570575060059392505050565b600484015460ff1615611587575060049392505050565b8063ffffffff1642111561159f575060039392505050565b835463ffffffff600160a01b82048116600160e01b9092041610156115c8575060019392505050565b8354600185015463ffffffff600160a01b9092048216911610156115f0575060029392505050565b5050919050565b6000546001600160a01b03163314610dae5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161033a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805182516000911480156116d0575081602001516001600160801b03191683602001516001600160801b031916145b9392505050565b600080602083850312156116ea57600080fd5b823567ffffffffffffffff8082111561170257600080fd5b818501915085601f83011261171657600080fd5b81358181111561172557600080fd5b8660208260051b850101111561173a57600080fd5b60209290920196919550909350505050565b803563ffffffff8116811461176057600080fd5b919050565b60006020828403121561177757600080fd5b6116d08261174c565b6000815180845260005b818110156117a65760208185018101518683018201520161178a565b506000602082860101526020601f19601f83011685010191505092915050565b60018060a01b03815116825260208101511515602083015260006040820151608060408501526117f96080850182611780565b9050606083015184820360608601526118128282611780565b95945050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561187057603f1988860301845261185e8583516117c6565b94509285019290850190600101611842565b5092979650505050505050565b60006020828403121561188f57600080fd5b5035919050565b6001600160a01b038916815263ffffffff888116602080840191909152888216604084015287821660608401529086166080830152845160a08301528401516001600160801b03191660c082015282151560e0820152610120610100820181905260009061190683820185611780565b9b9a5050505050505050505050565b6001600160a01b038116811461145157600080fd5b6000806040838503121561193d57600080fd5b82359150602083013561194f81611915565b809150509250929050565b6020815260006116d060208301846117c6565b60008083601f84011261197f57600080fd5b50813567ffffffffffffffff81111561199757600080fd5b6020830191508360208285010111156119af57600080fd5b9250929050565b6000806000604084860312156119cb57600080fd5b6119d48461174c565b9250602084013567ffffffffffffffff8111156119f057600080fd5b6119fc8682870161196d565b9497909650939450505050565b60008060008060008086880360a0811215611a2357600080fd5b611a2c8861174c565b9650602088013567ffffffffffffffff80821115611a4957600080fd5b611a558b838c0161196d565b90985096508691506040603f1984011215611a6f57600080fd5b60408a01955060808a0135925080831115611a8957600080fd5b5050611a9789828a0161196d565b979a9699509497509295939492505050565b634e487b7160e01b600052602160045260246000fd5b6020810160068310611ae157634e487b7160e01b600052602160045260246000fd5b91905290565b600060208284031215611af957600080fd5b81356116d081611915565b634e487b7160e01b600052603260045260246000fd5b600060208284031215611b2c57600080fd5b81516001600160601b03811681146116d057600080fd5b60208082526018908201527f4e6f7420656e6f75676820617574686f72697a6174696f6e0000000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600060018201611ba257611ba2611b7a565b5060010190565b60208082528181018390526000908460408401835b86811015611bec578235611bd181611915565b6001600160a01b031682529183019190830190600101611bbe565b509695505050505050565b600181811c90821680611c0b57607f821691505b602082108103611c2b57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215611c4357600080fd5b81516116d081611915565b8183823760009101908152919050565b634e487b7160e01b600052604160045260246000fd5b601f821115611cba57600081815260208120601f850160051c81016020861015611c9b5750805b601f850160051c820191505b8181101561130d57828155600101611ca7565b505050565b67ffffffffffffffff831115611cd757611cd7611c5e565b611ceb83611ce58354611bf7565b83611c74565b6000601f841160018114611d1f5760008515611d075750838201355b600019600387901b1c1916600186901b178355611d79565b600083815260209020601f19861690835b82811015611d505786850135825560209485019460019092019101611d30565b5086821015611d6d5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600063ffffffff808316818103611d9957611d99611b7a565b6001019392505050565b6001600160801b03198116811461145157600080fd5b81358155600181016020830135611dcf81611da3565b81546001600160801b03191660809190911c1790555050565b600060408284031215611dfa57600080fd5b6040516040810181811067ffffffffffffffff82111715611e1d57611e1d611c5e565b604052823581526020830135611e3281611da3565b60208201529392505050565b6000808354611e4c81611bf7565b60018281168015611e645760018114611e7957611ea8565b60ff1984168752821515830287019450611ea8565b8760005260208060002060005b85811015611e9f5781548a820152908401908201611e86565b50505082870194505b50929695505050505050565b63ffffffff818116838216019080821115611ed157611ed1611b7a565b509291505056fea26469706673582212200eae299f80274d63d710b26d0bfdf0da4b44dce652d7d9a6bb31ed8cbe3b95c364736f6c63430008140033" +[ + { + "inputs": [ + { + "internalType": "contract IAccessControlApplication", + "name": "app", + "type": "address" + }, + { + "internalType": "uint32", + "name": "_timeout", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_maxDkgSize", + "type": "uint32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "node", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "aggregatedTranscriptDigest", + "type": "bytes32" + } + ], + "name": "AggregationPosted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "successful", + "type": "bool" + } + ], + "name": "EndRitual", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "oldSize", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "newSize", + "type": "uint32" + } + ], + "name": "MaxDkgSizeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "StartAggregationRound", + "type": "event" }, - "devdoc": { - "kind": "dev", - "methods": { - "owner()": { - "details": "Returns the address of the current owner." + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" }, - "renounceOwnership()": { - "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner." + { + "indexed": true, + "internalType": "address", + "name": "initiator", + "type": "address" }, - "transferOwnership(address)": { - "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner." + { + "indexed": false, + "internalType": "address[]", + "name": "participants", + "type": "address[]" } - }, - "title": "Coordinator", - "version": 1 + ], + "name": "StartRitual", + "type": "event" }, - "runtimeBytecode": { - "bytecode": "0x60a06040523480156200001157600080fd5b506040516200208b3803806200208b8339810160408190526200003491620000e7565b6200003f336200007d565b6001600160a01b039092166080526002805463ffffffff938416640100000000026001600160401b031990911693909216929092171790556200013e565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805163ffffffff81168114620000e257600080fd5b919050565b600080600060608486031215620000fd57600080fd5b83516001600160a01b03811681146200011557600080fd5b92506200012560208501620000cd565b91506200013560408501620000cd565b90509250925092565b608051611f0e6200017d600039600081816101620152818161049101528181610b0001528181610ba601528181610ed90152610f7f0152611f0e6000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c8063715018a6116100a2578063948c73ea11610071578063948c73ea1461025f5780639c48937b1461027f578063dc80d104146102a0578063f1e0ff19146102b3578063f2fde38b146102bb57600080fd5b8063715018a6146102205780638b5eeb3f146102285780638cf950611461023b5780638da5cb5b1461024e57600080fd5b80633d796abc116100de5780633d796abc146101b45780635e748733146101db5780636b75f53e146101fb57806370dea79a1461021057600080fd5b80631057de0114610110578063214b02ad1461013d57806326e4ca821461015d5780632f2eaebc1461019c575b600080fd5b61012361011e3660046116d7565b6102ce565b60405163ffffffff90911681526020015b60405180910390f35b61015061014b366004611765565b6105aa565b604051610134919061181b565b6101847f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610134565b60025461012390640100000000900463ffffffff1681565b6101c76101c236600461187d565b610785565b604051610134989796959493929190611896565b6101ee6101e936600461192a565b6108ac565b604051610134919061195a565b61020e6102093660046119b6565b610a53565b005b6002546101239063ffffffff1681565b61020e610d9c565b61020e610236366004611765565b610db0565b61020e610249366004611a09565b610e2c565b6000546001600160a01b0316610184565b61027261026d36600461187d565b611315565b6040516101349190611abf565b61029261028d3660046116d7565b611340565b604051908152602001610134565b61020e6102ae366004611765565b611373565b600154610292565b61020e6102c9366004611ae7565b6113db565b600081600281108015906102f25750600254640100000000900463ffffffff168111155b6103435760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206e756d626572206f66206e6f64657300000000000000000060448201526064015b60405180910390fd5b6001805480820182556000918252600781027fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180544263ffffffff908116600160c01b0263ffffffff60c01b19918716600160a01b0263ffffffff60a01b1933166001600160c01b03199094169390931792909217161781559091805b8481101561055257600683018054600181018255600091825260208220600390910201908989848181106103f7576103f7611b04565b905060200201602081019061040c9190611ae7565b9050806001600160a01b0316846001600160a01b03161061046f5760405162461bcd60e51b815260206004820152601860248201527f50726f766964657273206d75737420626520736f727465640000000000000000604482015260640161033a565b60405163c4903d5b60e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063c4903d5b90602401602060405180830381865afa1580156104da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fe9190611b1a565b6001600160601b0316116105245760405162461bcd60e51b815260040161033a90611b43565b81546001600160a01b0319166001600160a01b0382161790915591508061054a81611b90565b9150506103c1565b50336001600160a01b03168363ffffffff167fa4e3b0c0b125669bbe2cfcbf57b6b13b68dc908d343dde9bdf0df75081ae403f8989604051610595929190611ba9565b60405180910390a35090925050505b92915050565b6060600060018363ffffffff16815481106105c7576105c7611b04565b9060005260206000209060070201905080600601805480602002602001604051908101604052809291908181526020016000905b82821015610779576000848152602090819020604080516080810182526003860290920180546001600160a01b0381168452600160a01b900460ff161515938301939093526001830180549293929184019161065690611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461068290611bf7565b80156106cf5780601f106106a4576101008083540402835291602001916106cf565b820191906000526020600020905b8154815290600101906020018083116106b257829003601f168201915b505050505081526020016002820180546106e890611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461071490611bf7565b80156107615780601f1061073657610100808354040283529160200191610761565b820191906000526020600020905b81548152906001019060200180831161074457829003601f168201915b505050505081525050815260200190600101906105fb565b50505050915050919050565b6001818154811061079557600080fd5b600091825260209182902060079190910201805460018201546040805180820190915260028401548152600384015460801b6001600160801b0319169481019490945260048301546005840180546001600160a01b0385169750600160a01b850463ffffffff90811697600160c01b8704821697600160e01b90970482169691909516949360ff1692909161082990611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461085590611bf7565b80156108a25780601f10610877576101008083540402835291602001916108a2565b820191906000526020600020905b81548152906001019060200180831161088557829003601f168201915b5050505050905088565b60408051608081018252600080825260208201526060918101829052818101919091526108f9600184815481106108e5576108e5611b04565b906000526020600020906007020183611454565b6040805160808101825282546001600160a01b0381168252600160a01b900460ff161515602082015260018301805491939284019161093790611bf7565b80601f016020809104026020016040519081016040528092919081815260200182805461096390611bf7565b80156109b05780601f10610985576101008083540402835291602001916109b0565b820191906000526020600020905b81548152906001019060200180831161099357829003601f168201915b505050505081526020016002820180546109c990611bf7565b80601f01602080910402602001604051908101604052809291908181526020018280546109f590611bf7565b8015610a425780601f10610a1757610100808354040283529160200191610a42565b820191906000526020600020905b815481529060010190602001808311610a2557829003601f168201915b505050505081525050905092915050565b600060018463ffffffff1681548110610a6e57610a6e611b04565b6000918252602090912060079091020190506001610a8b8261150a565b6005811115610a9c57610a9c611aa9565b14610ae95760405162461bcd60e51b815260206004820152601b60248201527f4e6f742077616974696e6720666f72207472616e736372697074730000000000604482015260640161033a565b60405162dca53b60e81b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dca53b0090602401602060405180830381865afa158015610b4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b739190611c31565b90506000610b818383611454565b60405163c4903d5b60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063c4903d5b90602401602060405180830381865afa158015610bed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c119190611b1a565b6001600160601b031611610c375760405162461bcd60e51b815260040161033a90611b43565b806001018054610c4690611bf7565b159050610c955760405162461bcd60e51b815260206004820152601e60248201527f4e6f646520616c726561647920706f73746564207472616e7363726970740000604482015260640161033a565b60008585604051610ca7929190611c4e565b604051908190039020905060018201610cc1868883611cbf565b50826001600160a01b03168763ffffffff167f66568b934e848078c9787d6a66dae153eaae57f0ec3a553c11939fcdcf9c11fb83604051610d0491815260200190565b60405180910390a38354600160e01b900463ffffffff1684601c610d2783611d80565b82546101009290920a63ffffffff8181021990931691831602179091558554600160a01b81048216600160e01b909104909116039050610d935760405163ffffffff8816907fca79a3f8fdffa27f8c0a30733144db5a5bd2663f1f2561d8d665bf4da6a3dfbe90600090a25b50505050505050565b610da46115f7565b610dae6000611651565b565b610db86115f7565b6002546040805163ffffffff6401000000009093048316815291831660208301527fbb0cedd628c5ad0619627014b51dff9ab8676ce038340c26d817b8229740d0c9910160405180910390a16002805463ffffffff9092166401000000000267ffffffff0000000019909216919091179055565b600060018763ffffffff1681548110610e4757610e47611b04565b6000918252602090912060079091020190506002610e648261150a565b6005811115610e7557610e75611aa9565b14610ec25760405162461bcd60e51b815260206004820152601c60248201527f4e6f742077616974696e6720666f72206167677265676174696f6e7300000000604482015260640161033a565b60405162dca53b60e81b81523360048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dca53b0090602401602060405180830381865afa158015610f28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4c9190611c31565b90506000610f5a8383611454565b60405163c4903d5b60e01b81526001600160a01b0384811660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063c4903d5b90602401602060405180830381865afa158015610fc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fea9190611b1a565b6001600160601b0316116110105760405162461bcd60e51b815260040161033a90611b43565b8054600160a01b900460ff16156110695760405162461bcd60e51b815260206004820152601f60248201527f4e6f646520616c726561647920706f73746564206167677265676174696f6e00604482015260640161033a565b80600201805461107890611bf7565b1590506110dc5760405162461bcd60e51b815260206004820152602c60248201527f4e6f646520616c72656164792070726f7669646564207265717565737420656e60448201526b6372797074696e67206b657960a01b606482015260840161033a565b600088886040516110ee929190611c4e565b604051908190039020825460ff60a01b1916600160a01b178355905060028201611119868883611cbf565b50826001600160a01b03168a63ffffffff167f1884446739eb06b60e314b4c3b25f08b5c7377f20538c132ce7c064f38272bac8360405161115c91815260200190565b60405180910390a383600501805461117390611bf7565b90506000036111a3576005840161118b898b83611cbf565b50866002850161119b8282611db9565b90505061126e565b6040805180820190915260028501548152600385015460801b6001600160801b03191660208201526111e3906111de368a90038a018a611de8565b6116a1565b1580611207575080846005016040516111fc9190611e3e565b604051809103902014155b1561126e5760048401805460ff191660011790558354604051600081526001600160a01b039091169063ffffffff8c16907f9dc7c9243191ecf8e0264232a3cb660035e1563d41b1812f0fea00955a291a589060200160405180910390a35050505061130d565b60018401805463ffffffff1690600061128683611d80565b82546101009290920a63ffffffff81810219909316918316021790915585546001870154600160a01b90910482169116039050611308578354604051600181526001600160a01b039091169063ffffffff8c16907f9dc7c9243191ecf8e0264232a3cb660035e1563d41b1812f0fea00955a291a589060200160405180910390a35b505050505b505050505050565b60006105a46001838154811061132d5761132d611b04565b906000526020600020906007020161150a565b60008282604051602001611355929190611ba9565b60405160208183030381529060405280519060200120905092915050565b61137b6115f7565b6002546040805163ffffffff928316815291831660208301527feb65c6287031cadd2d71b59499e985dddd00f14b3a8b2ce8d951da00f29995f6910160405180910390a16002805463ffffffff191663ffffffff92909216919091179055565b6113e36115f7565b6001600160a01b0381166114485760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161033a565b61145181611651565b50565b6006820154600090815b818110156114c157600085600601828154811061147d5761147d611b04565b6000918252602090912060039091020180549091506001600160a01b038087169116036114ae5792506105a4915050565b50806114b981611b90565b91505061145e565b5060405162461bcd60e51b815260206004820152601e60248201527f5061727469636970616e74206e6f742070617274206f662072697475616c0000604482015260640161033a565b805460025460009163ffffffff600160c01b909104811691839161152f911683611eb4565b90508163ffffffff16600003611549575060009392505050565b8354600185015463ffffffff600160a01b9092048216911603611570575060059392505050565b600484015460ff1615611587575060049392505050565b8063ffffffff1642111561159f575060039392505050565b835463ffffffff600160a01b82048116600160e01b9092041610156115c8575060019392505050565b8354600185015463ffffffff600160a01b9092048216911610156115f0575060029392505050565b5050919050565b6000546001600160a01b03163314610dae5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161033a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805182516000911480156116d0575081602001516001600160801b03191683602001516001600160801b031916145b9392505050565b600080602083850312156116ea57600080fd5b823567ffffffffffffffff8082111561170257600080fd5b818501915085601f83011261171657600080fd5b81358181111561172557600080fd5b8660208260051b850101111561173a57600080fd5b60209290920196919550909350505050565b803563ffffffff8116811461176057600080fd5b919050565b60006020828403121561177757600080fd5b6116d08261174c565b6000815180845260005b818110156117a65760208185018101518683018201520161178a565b506000602082860101526020601f19601f83011685010191505092915050565b60018060a01b03815116825260208101511515602083015260006040820151608060408501526117f96080850182611780565b9050606083015184820360608601526118128282611780565b95945050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561187057603f1988860301845261185e8583516117c6565b94509285019290850190600101611842565b5092979650505050505050565b60006020828403121561188f57600080fd5b5035919050565b6001600160a01b038916815263ffffffff888116602080840191909152888216604084015287821660608401529086166080830152845160a08301528401516001600160801b03191660c082015282151560e0820152610120610100820181905260009061190683820185611780565b9b9a5050505050505050505050565b6001600160a01b038116811461145157600080fd5b6000806040838503121561193d57600080fd5b82359150602083013561194f81611915565b809150509250929050565b6020815260006116d060208301846117c6565b60008083601f84011261197f57600080fd5b50813567ffffffffffffffff81111561199757600080fd5b6020830191508360208285010111156119af57600080fd5b9250929050565b6000806000604084860312156119cb57600080fd5b6119d48461174c565b9250602084013567ffffffffffffffff8111156119f057600080fd5b6119fc8682870161196d565b9497909650939450505050565b60008060008060008086880360a0811215611a2357600080fd5b611a2c8861174c565b9650602088013567ffffffffffffffff80821115611a4957600080fd5b611a558b838c0161196d565b90985096508691506040603f1984011215611a6f57600080fd5b60408a01955060808a0135925080831115611a8957600080fd5b5050611a9789828a0161196d565b979a9699509497509295939492505050565b634e487b7160e01b600052602160045260246000fd5b6020810160068310611ae157634e487b7160e01b600052602160045260246000fd5b91905290565b600060208284031215611af957600080fd5b81356116d081611915565b634e487b7160e01b600052603260045260246000fd5b600060208284031215611b2c57600080fd5b81516001600160601b03811681146116d057600080fd5b60208082526018908201527f4e6f7420656e6f75676820617574686f72697a6174696f6e0000000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600060018201611ba257611ba2611b7a565b5060010190565b60208082528181018390526000908460408401835b86811015611bec578235611bd181611915565b6001600160a01b031682529183019190830190600101611bbe565b509695505050505050565b600181811c90821680611c0b57607f821691505b602082108103611c2b57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215611c4357600080fd5b81516116d081611915565b8183823760009101908152919050565b634e487b7160e01b600052604160045260246000fd5b601f821115611cba57600081815260208120601f850160051c81016020861015611c9b5750805b601f850160051c820191505b8181101561130d57828155600101611ca7565b505050565b67ffffffffffffffff831115611cd757611cd7611c5e565b611ceb83611ce58354611bf7565b83611c74565b6000601f841160018114611d1f5760008515611d075750838201355b600019600387901b1c1916600186901b178355611d79565b600083815260209020601f19861690835b82811015611d505786850135825560209485019460019092019101611d30565b5086821015611d6d5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600063ffffffff808316818103611d9957611d99611b7a565b6001019392505050565b6001600160801b03198116811461145157600080fd5b81358155600181016020830135611dcf81611da3565b81546001600160801b03191660809190911c1790555050565b600060408284031215611dfa57600080fd5b6040516040810181811067ffffffffffffffff82111715611e1d57611e1d611c5e565b604052823581526020830135611e3281611da3565b60208201529392505050565b6000808354611e4c81611bf7565b60018281168015611e645760018114611e7957611ea8565b60ff1984168752821515830287019450611ea8565b8760005260208060002060005b85811015611e9f5781548a820152908401908201611e86565b50505082870194505b50929695505050505050565b63ffffffff818116838216019080821115611ed157611ed1611b7a565b509291505056fea26469706673582212200eae299f80274d63d710b26d0bfdf0da4b44dce652d7d9a6bb31ed8cbe3b95c364736f6c63430008140033" + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "oldTimeout", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "newTimeout", + "type": "uint32" + } + ], + "name": "TimeoutChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "node", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "transcriptDigest", + "type": "bytes32" + } + ], + "name": "TranscriptPosted", + "type": "event" + }, + { + "inputs": [], + "name": "application", + "outputs": [ + { + "internalType": "contract IAccessControlApplication", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" }, - "sourceId": "contracts/coordination/Coordinator.sol", - "sourcemap": "282:9240:36:-:0;;;1917:176;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;936:32:17;719:10:24;936:18:17;:32::i;:::-;-1:-1:-1;;;;;2007:17:36;;;;;2034:7;:18;;;2062:24;;;;;-1:-1:-1;;;;;;2062:24:36;;;2034:18;;;;2062:24;;;;;;;282:9240;;2433:187:17;2506:16;2525:6;;-1:-1:-1;;;;;2541:17:17;;;-1:-1:-1;;;;;;2541:17:17;;;;;;2573:40;;2525:6;;;;;;;2573:40;;2506:16;2573:40;2496:124;2433:187;:::o;14:167:63:-;92:13;;145:10;134:22;;124:33;;114:61;;171:1;168;161:12;114:61;14:167;;;:::o;186:491::-;307:6;315;323;376:2;364:9;355:7;351:23;347:32;344:52;;;392:1;389;382:12;344:52;418:16;;-1:-1:-1;;;;;463:31:63;;453:42;;443:70;;509:1;506;499:12;443:70;532:5;-1:-1:-1;556:48:63;600:2;585:18;;556:48;:::i;:::-;546:58;;623:48;667:2;656:9;652:18;623:48;:::i;:::-;613:58;;186:491;;;;;:::o;:::-;282:9240:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;", - "userdoc": { - "kind": "user", - "methods": {}, - "notice": "Coordination layer for DKG-TDec", - "version": 1 + { + "inputs": [ + { + "internalType": "address[]", + "name": "nodes", + "type": "address[]" + } + ], + "name": "cohortFingerprint", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "ritualID", + "type": "uint256" + }, + { + "internalType": "address", + "name": "provider", + "type": "address" + } + ], + "name": "getParticipantFromProvider", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "provider", + "type": "address" + }, + { + "internalType": "bool", + "name": "aggregated", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "transcript", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "decryptionRequestStaticKey", + "type": "bytes" + } + ], + "internalType": "struct Coordinator.Participant", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + } + ], + "name": "getParticipants", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "provider", + "type": "address" + }, + { + "internalType": "bool", + "name": "aggregated", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "transcript", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "decryptionRequestStaticKey", + "type": "bytes" + } + ], + "internalType": "struct Coordinator.Participant[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "ritualId", + "type": "uint256" + } + ], + "name": "getRitualState", + "outputs": [ + { + "internalType": "enum Coordinator.RitualState", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "providers", + "type": "address[]" + } + ], + "name": "initiateRitual", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "maxDkgSize", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "numberOfRituals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "aggregatedTranscript", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes16", + "name": "word1", + "type": "bytes16" + } + ], + "internalType": "struct BLS12381.G1Point", + "name": "publicKey", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "decryptionRequestStaticKey", + "type": "bytes" + } + ], + "name": "postAggregation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "ritualId", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "transcript", + "type": "bytes" + } + ], + "name": "postTranscript", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "rituals", + "outputs": [ + { + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "internalType": "uint32", + "name": "dkgSize", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "initTimestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "totalTranscripts", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "totalAggregations", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "word0", + "type": "bytes32" + }, + { + "internalType": "bytes16", + "name": "word1", + "type": "bytes16" + } + ], + "internalType": "struct BLS12381.G1Point", + "name": "publicKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "aggregationMismatch", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "aggregatedTranscript", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "newSize", + "type": "uint32" + } + ], + "name": "setMaxDkgSize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "newTimeout", + "type": "uint32" + } + ], + "name": "setTimeout", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "timeout", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" } -} +] diff --git a/package.json b/package.json index 4d9af47ff..ce5a215c7 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "prebuild": "yarn typechain" }, "dependencies": { - "@nucypher/nucypher-core": "../nucypher-core/nucypher-core-wasm/pkg", + "@nucypher/nucypher-core": "^0.10.0", "axios": "^0.21.1", "deep-equal": "^2.2.1", "ethers": "^5.4.1", diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts index 841375e94..264e4ea50 100644 --- a/src/agents/coordinator.ts +++ b/src/agents/coordinator.ts @@ -1,3 +1,4 @@ +import { SessionStaticKey } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { @@ -5,6 +6,7 @@ import { Coordinator__factory, } from '../../types/ethers-contracts'; import { BLS12381 } from '../../types/ethers-contracts/Coordinator'; +import { fromHexString } from '../utils'; import { getContract } from './contracts'; @@ -19,7 +21,11 @@ export interface CoordinatorRitual { aggregatedTranscript: string; } -export type DkgParticipant = Coordinator.ParticipantStructOutput; +export type DkgParticipant = { + provider: string; + aggregated: boolean; + decryptionRequestStaticKey: SessionStaticKey; +}; export class DkgCoordinatorAgent { public static async getParticipants( @@ -27,9 +33,17 @@ export class DkgCoordinatorAgent { ritualId: number ): Promise { const Coordinator = await this.connectReadOnly(provider); - // TODO: Remove `as unknown` cast after regenerating the contract types: https://github.com/nucypher/nucypher-contracts/pull/77 const participants = await Coordinator.getParticipants(ritualId); - return participants as unknown as DkgParticipant[]; + + return participants.map((participant) => { + return { + provider: participant.provider, + aggregated: participant.aggregated, + decryptionRequestStaticKey: SessionStaticKey.fromBytes( + fromHexString(participant.decryptionRequestStaticKey) + ), + }; + }); } public static async getRitual( @@ -37,7 +51,7 @@ export class DkgCoordinatorAgent { ritualId: number ): Promise { const Coordinator = await this.connectReadOnly(provider); - return await Coordinator.rituals(ritualId); + return Coordinator.rituals(ritualId); } private static async connectReadOnly(provider: ethers.providers.Provider) { diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 2d5f3a048..f9ed96c55 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -7,7 +7,6 @@ import { EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, SessionSharedSecret, - SessionStaticKey, SessionStaticSecret, ThresholdDecryptionRequest, } from '@nucypher/nucypher-core'; @@ -17,10 +16,11 @@ import { DkgCoordinatorAgent, DkgParticipant } from '../agents/coordinator'; import { ConditionExpression } from '../conditions'; import { DkgRitual, + FerveoVariant, getCombineDecryptionSharesFunction, getVariantClass, } from '../dkg'; -import { fromHexString, fromJSON, toJSON } from '../utils'; +import { fromJSON, toJSON } from '../utils'; import { Porter } from './porter'; @@ -47,13 +47,12 @@ export class CbdTDecDecrypter { ); } - // Retrieve and decrypt ciphertext using provider and condition set + // Retrieve and decrypt ciphertext using provider and condition expression public async retrieveAndDecrypt( provider: ethers.providers.Web3Provider, conditionExpr: ConditionExpression, - variant: number, - ciphertext: Ciphertext, - aad: Uint8Array + variant: FerveoVariant, + ciphertext: Ciphertext ): Promise { const decryptionShares = await this.retrieve( provider, @@ -65,7 +64,11 @@ export class CbdTDecDecrypter { const combineDecryptionSharesFn = getCombineDecryptionSharesFunction(variant); const sharedSecret = combineDecryptionSharesFn(decryptionShares); - return decryptWithSharedSecret(ciphertext, aad, sharedSecret); + return decryptWithSharedSecret( + ciphertext, + conditionExpr.asAad(), + sharedSecret + ); } // Retrieve decryption shares @@ -79,6 +82,8 @@ export class CbdTDecDecrypter { provider, this.ritualId ); + // We only need the `threshold` participants + const sufficientDkgParticipants = dkgParticipants.slice(0, this.threshold); const contextStr = await conditionExpr.buildContext(provider).toJson(); const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests( this.ritualId, @@ -86,14 +91,16 @@ export class CbdTDecDecrypter { ciphertext, conditionExpr, contextStr, - dkgParticipants + sufficientDkgParticipants ); const { encryptedResponses, errors } = await this.porter.cbdDecrypt( encryptedRequests, this.threshold ); + // TODO: How many errors are acceptable? Less than (threshold - shares)? + // TODO: If Porter accepts only `threshold` decryption requests, then we may not have any errors if (Object.keys(errors).length > 0) { throw new Error( `CBD decryption failed with errors: ${JSON.stringify(errors)}` @@ -160,10 +167,9 @@ export class CbdTDecDecrypter { const sharedSecrets: Record = Object.fromEntries( dkgParticipants.map(({ provider, decryptionRequestStaticKey }) => { - const decKey = SessionStaticKey.fromBytes( - fromHexString(decryptionRequestStaticKey) + const sharedSecret = ephemeralSessionKey.deriveSharedSecret( + decryptionRequestStaticKey ); - const sharedSecret = ephemeralSessionKey.deriveSharedSecret(decKey); return [provider, sharedSecret]; }) ); diff --git a/src/characters/enrico.ts b/src/characters/enrico.ts index 2f8115937..17c6ff358 100644 --- a/src/characters/enrico.ts +++ b/src/characters/enrico.ts @@ -57,11 +57,15 @@ export class Enrico { withConditions = this.conditions; } + if (!withConditions) { + throw new Error('Conditions are required for CBD encryption.'); + } + if (!(this.encryptingKey instanceof DkgPublicKey)) { throw new Error('Wrong key type. Use encryptMessagePre instead.'); } - const aad = toBytes(withConditions?.toJson() ?? ''); + const aad = withConditions.asAad(); const ciphertext = ferveoEncrypt( plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), aad, diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 1fd05f24f..1bcc6aa41 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -14,6 +14,7 @@ import { Base64EncodedBytes, ChecksumAddress, HexEncodedBytes } from '../types'; import { fromBase64, fromHexString, toBase64, toHexString } from '../utils'; // /get_ursulas + export type Ursula = { readonly checksumAddress: ChecksumAddress; readonly uri: string; @@ -40,6 +41,7 @@ export type GetUrsulasResult = { }; // /retrieve_cfrags + type PostRetrieveCFragsRequest = { readonly treasure_map: Base64EncodedBytes; readonly retrieval_kits: readonly Base64EncodedBytes[]; @@ -79,8 +81,15 @@ type PostCbdDecryptRequest = { }; type PostCbdDecryptResponse = { - encrypted_decryption_responses: Record; - errors: Record; + result: { + decryption_results: { + encrypted_decryption_responses: Record< + ChecksumAddress, + Base64EncodedBytes + >; + errors: Record; + }; + }; }; export type CbdDecryptResult = { @@ -174,8 +183,12 @@ export class Porter { new URL('/cbd_decrypt', this.porterUrl).toString(), data ); + + const { encrypted_decryption_responses, errors } = + resp.data.result.decryption_results; + const decryptionResponses = Object.entries( - resp.data.encrypted_decryption_responses + encrypted_decryption_responses ).map(([address, encryptedResponseBase64]) => { const encryptedResponse = EncryptedThresholdDecryptionResponse.fromBytes( fromBase64(encryptedResponseBase64) @@ -186,6 +199,6 @@ export class Porter { string, EncryptedThresholdDecryptionResponse > = Object.fromEntries(decryptionResponses); - return { encryptedResponses, errors: resp.data.errors }; + return { encryptedResponses, errors }; } } diff --git a/src/conditions/compound-condition.ts b/src/conditions/compound-condition.ts index fe0249677..3b35f675a 100644 --- a/src/conditions/compound-condition.ts +++ b/src/conditions/compound-condition.ts @@ -1,6 +1,6 @@ import Joi from 'joi'; -import { Condition } from './base/condition'; +import { Condition } from './base'; import { contractConditionSchema } from './base/contract'; import { rpcConditionSchema } from './base/rpc'; import { timeConditionSchema } from './base/time'; diff --git a/src/conditions/condition-expr.ts b/src/conditions/condition-expr.ts index b762c6b13..5cc032c5e 100644 --- a/src/conditions/condition-expr.ts +++ b/src/conditions/condition-expr.ts @@ -2,7 +2,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { SemVer } from 'semver'; -import { objectEquals, toJSON } from '../utils'; +import { objectEquals, toBytes, toJSON } from '../utils'; import { Condition, @@ -90,6 +90,10 @@ export class ConditionExpression { return new ConditionContext([this.condition], provider); } + public asAad(): Uint8Array { + return toBytes(this.toJson()); + } + public equals(other: ConditionExpression): boolean { return ( this.version === other.version && diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts index dc385245b..fcf9939c3 100644 --- a/src/conditions/context/context.ts +++ b/src/conditions/context/context.ts @@ -2,7 +2,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; import { fromJSON, toJSON } from '../../utils'; -import { Condition } from '../base/condition'; +import { Condition } from '../base'; import { USER_ADDRESS_PARAM } from '../const'; import { TypedSignature, WalletAuthenticationProvider } from './providers'; diff --git a/src/dkg.ts b/src/dkg.ts index a6a478806..f0ee81e41 100644 --- a/src/dkg.ts +++ b/src/dkg.ts @@ -8,6 +8,7 @@ import { } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; +import { DkgCoordinatorAgent } from './agents/coordinator'; import { bytesEquals, fromHexString } from './utils'; // TODO: Expose from @nucypher/nucypher-core @@ -77,7 +78,8 @@ export class DkgRitual { return ( this.id === other.id && // TODO: Replace with `equals` after https://github.com/nucypher/nucypher-core/issues/56 is fixed - bytesEquals(this.dkgPublicKey.toBytes(), other.dkgPublicKey.toBytes()) + bytesEquals(this.dkgPublicKey.toBytes(), other.dkgPublicKey.toBytes()) && + this.threshold === other.threshold ); } } @@ -85,30 +87,22 @@ export class DkgRitual { export class DkgClient { constructor(private readonly provider: ethers.providers.Web3Provider) {} - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async initializeRitual( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _provider: ethers.providers.Web3Provider, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _ritualParams: unknown - ): Promise { - // TODO: Remove this check after implementing this method - if (!this.provider._isProvider) { - throw new Error('Invalid provider'); - } - // TODO: Create a new DKG ritual here - const pkWord1 = fromHexString( - '9045795411ed251bf2eecc9415552c41863502a207104ef7ab482bc2364729d9' - ); - console.assert(pkWord1.length === 32); - const pkWord2 = fromHexString('b99e2949cee8d888663b2995fc647fcf'); - // We need to concat two words returned by the DKG contract - const dkgPkBytes = new Uint8Array([...pkWord1, ...pkWord2]); - console.assert(dkgPkBytes.length === 48); + // TODO: Update API: Replace with getExistingRitual and support ritualId in Strategy + public async initializeRitual(ritualParams: { + shares: number; + threshold: number; + }): Promise { + const ritualId = 2; + const ritual = await DkgCoordinatorAgent.getRitual(this.provider, ritualId); + const dkgPkBytes = new Uint8Array([ + ...fromHexString(ritual.publicKey.word0), + ...fromHexString(ritual.publicKey.word1), + ]); return { - id: 0, + id: ritualId, dkgPublicKey: DkgPublicKey.fromBytes(dkgPkBytes), + threshold: ritualParams.threshold, } as DkgRitual; } diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts index a56f93e72..0632eeaef 100644 --- a/src/sdk/strategy/cbd-strategy.ts +++ b/src/sdk/strategy/cbd-strategy.ts @@ -37,10 +37,7 @@ export class CbdStrategy { shares: this.cohort.configuration.shares, }; const dkgClient = new DkgClient(provider); - const dkgRitual = await dkgClient.initializeRitual( - provider, - dkgRitualParams - ); + const dkgRitual = await dkgClient.initializeRitual(dkgRitualParams); return DeployedCbdStrategy.create(this.cohort, dkgRitual); } diff --git a/src/utils.ts b/src/utils.ts index 722e98488..3fc45a09a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,9 +1,13 @@ import deepEqual from 'deep-equal'; +// TODO: Replace byte and hex manipulation with ethers.js export const toBytes = (str: string): Uint8Array => new TextEncoder().encode(str); export const fromHexString = (hexString: string): Uint8Array => { + if (hexString.startsWith('0x')) { + hexString = hexString.slice(2); + } const matches = hexString.match(/.{1,2}/g) ?? []; return new Uint8Array(matches.map((byte) => parseInt(byte, 16))); }; diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index 68f6bed9a..d33e9d456 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -1,12 +1,9 @@ import { SecretKey, SessionStaticSecret } from '@nucypher/nucypher-core'; import { conditions } from '../../src'; +import { FerveoVariant } from '../../src'; +import { CbdStrategy, DeployedCbdStrategy } from '../../src'; import { CbdTDecDecrypter } from '../../src/characters/cbd-recipient'; -import { FerveoVariant } from '../../src/dkg'; -import { - CbdStrategy, - DeployedCbdStrategy, -} from '../../src/sdk/strategy/cbd-strategy'; import { toBytes } from '../../src/utils'; import { fakeDkgFlow, @@ -136,8 +133,7 @@ describe('CbdDeployedStrategy', () => { aliceProvider, conditionExpr, variant, - ciphertext, - aad + ciphertext ); expect(getUrsulasSpy).toHaveBeenCalled(); expect(getParticipantsSpy).toHaveBeenCalled(); diff --git a/test/unit/ritual.test.ts b/test/unit/ritual.test.ts index 28ac7a140..246c5a91f 100644 --- a/test/unit/ritual.test.ts +++ b/test/unit/ritual.test.ts @@ -3,7 +3,7 @@ import { DkgPublicKey } from '@nucypher/nucypher-core'; import { fromHexString } from '../../src/utils'; describe('Ritual', () => { - it('deserializes premade dkg ritual', async () => { + it('deserializes pre-made dkg ritual', async () => { const pkWord1 = fromHexString( '9045795411ed251bf2eecc9415552c41863502a207104ef7ab482bc2364729d9' ); diff --git a/test/utils.ts b/test/utils.ts index ed180623f..bdd0b0414 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -441,8 +441,8 @@ export const fakeDkgParticipants = ( return { provider: address, aggregated: true, // Assuming all validators already contributed to the aggregate - transcript: toHexString(transcript.toBytes()), - decryptionRequestStaticKey: toHexString(secret.publicKey().toBytes()), + transcript, + decryptionRequestStaticKey: secret.publicKey(), } as DkgParticipant; }); return { participantSecrets, participants }; diff --git a/yarn.lock b/yarn.lock index fd815fc63..567b36b42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1692,8 +1692,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nucypher/nucypher-core@../nucypher-core/nucypher-core-wasm/pkg": - version "0.9.0" +"@nucypher/nucypher-core@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@nucypher/nucypher-core/-/nucypher-core-0.10.0.tgz#95ba3805fa0c01510e9e012d65b735e97bd6ff08" + integrity sha512-7ZbFIZbAIO8UU++0tGhZEP8z1m4Vj5b/4+c9opBXXK88GZ7DFjdNWeTJWOCwLK3fMSpQJcUmkkqfsUd+PfGa9A== "@sideway/address@^4.1.3": version "4.1.4" @@ -1865,9 +1867,9 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "20.3.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" - integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== + version "20.3.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.2.tgz#fa6a90f2600e052a03c18b8cb3fd83dd4e599898" + integrity sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2463,9 +2465,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001503: - version "1.0.30001503" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz#88b6ff1b2cf735f1f3361dc1a15b59f0561aa398" - integrity sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw== + version "1.0.30001508" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz#4461bbc895c692a96da399639cc1e146e7302a33" + integrity sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw== chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" @@ -3094,9 +3096,9 @@ dotgitignore@^2.1.0: minimatch "^3.0.4" electron-to-chromium@^1.4.431: - version "1.4.432" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.432.tgz#154a69d5ead974347f534aea4d28b03c7149fd7b" - integrity sha512-yz3U/khQgAFT2HURJA3/F4fKIyO2r5eK09BQzBZFd6BvBSSaRuzKc2ZNBHtJcO75/EKiRYbVYJZ2RB0P4BuD2g== + version "1.4.441" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.441.tgz#94dd9c1cbf081d83f032a4f1cd9f787e21fc24ce" + integrity sha512-LlCgQ8zgYZPymf5H4aE9itwiIWH4YlCiv1HFLmmcBeFYi5E+3eaIFnjHzYtcFQbaKfAW+CqZ9pgxo33DZuoqPg== elliptic@6.5.4: version "6.5.4" @@ -5790,9 +5792,9 @@ pinkie@^2.0.0: integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" @@ -6169,10 +6171,10 @@ scrypt-js@3.0.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.5.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" - integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== +semver@7.x, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.5.2: + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" @@ -6181,13 +6183,6 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.5.2: - version "7.5.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" - integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== - dependencies: - lru-cache "^6.0.0" - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -6743,9 +6738,9 @@ tslib@^1.8.1: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.1.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== tsutils@^3.21.0: version "3.21.0" From c2acc6140b7955a02ad7bef4ca493b8a3b310617 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 27 Jun 2023 15:33:50 +0200 Subject: [PATCH 88/98] fix tests --- src/characters/cbd-recipient.ts | 11 ++++++++--- test/unit/cbd-strategy.test.ts | 2 +- test/utils.ts | 10 ++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index f9ed96c55..482eeb501 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -120,9 +120,14 @@ export class CbdTDecDecrypter { variant: number, expectedRitualId: number ) { - const decryptedResponses = Object.entries(encryptedResponses).map( - ([ursula, encryptedResponse]) => - encryptedResponse.decrypt(sessionSharedSecret[ursula]) + const decryptedResponses = Object.entries(sessionSharedSecret).map( + ([ursula, sharedSecret]) => { + const encryptedResponse = encryptedResponses[ursula]; + if (!encryptedResponse) { + throw new Error(`Missing encrypted response from ${ursula}`); + } + return encryptedResponse.decrypt(sharedSecret); + } ); const ritualIds = decryptedResponses.map(({ ritualId }) => ritualId); diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts index d33e9d456..36a8f216b 100644 --- a/test/unit/cbd-strategy.test.ts +++ b/test/unit/cbd-strategy.test.ts @@ -49,7 +49,7 @@ const makeCbdStrategy = async () => { async function makeDeployedCbdStrategy() { const strategy = await makeCbdStrategy(); - const mockedDkg = fakeDkgFlow(variant, 0); + const mockedDkg = fakeDkgFlow(variant, 0, 4, 4); const mockedDkgRitual = fakeDkgRitual(mockedDkg, mockedDkg.threshold); const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes()); const getUrsulasSpy = mockGetUrsulas(ursulas); diff --git a/test/utils.ts b/test/utils.ts index bdd0b0414..b79ca46d6 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -216,8 +216,8 @@ export const mockDetectEthereumProvider = () => { export const fakeDkgFlow = ( variant: FerveoVariant | FerveoVariant.Precomputed, ritualId: number, - sharesNum = 4, - threshold = 3 + sharesNum: number, + threshold: number ) => { if ( variant !== FerveoVariant.Simple && @@ -358,9 +358,11 @@ export const fakeDkgTDecFlowE2e = ( variant: FerveoVariant, message = toBytes('fake-message'), aad = toBytes('fake-aad'), - ritualId = 0 + ritualId = 0, + sharesNum = 4, + threshold = 4 ) => { - const ritual = fakeDkgFlow(variant, ritualId, 4, 3); + const ritual = fakeDkgFlow(variant, ritualId, sharesNum, threshold); // In the meantime, the client creates a ciphertext and decryption request const ciphertext = ferveoEncrypt(message, aad, ritual.dkg.publicKey()); From f682eb2a97b898ec5f941b6f9089f48cbe48d107 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 27 Jun 2023 16:34:50 +0200 Subject: [PATCH 89/98] apply pr suggestions --- src/characters/cbd-recipient.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 482eeb501..4c32f3b2d 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -82,8 +82,6 @@ export class CbdTDecDecrypter { provider, this.ritualId ); - // We only need the `threshold` participants - const sufficientDkgParticipants = dkgParticipants.slice(0, this.threshold); const contextStr = await conditionExpr.buildContext(provider).toJson(); const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests( this.ritualId, @@ -91,7 +89,7 @@ export class CbdTDecDecrypter { ciphertext, conditionExpr, contextStr, - sufficientDkgParticipants + dkgParticipants ); const { encryptedResponses, errors } = await this.porter.cbdDecrypt( @@ -99,8 +97,6 @@ export class CbdTDecDecrypter { this.threshold ); - // TODO: How many errors are acceptable? Less than (threshold - shares)? - // TODO: If Porter accepts only `threshold` decryption requests, then we may not have any errors if (Object.keys(errors).length > 0) { throw new Error( `CBD decryption failed with errors: ${JSON.stringify(errors)}` @@ -120,14 +116,8 @@ export class CbdTDecDecrypter { variant: number, expectedRitualId: number ) { - const decryptedResponses = Object.entries(sessionSharedSecret).map( - ([ursula, sharedSecret]) => { - const encryptedResponse = encryptedResponses[ursula]; - if (!encryptedResponse) { - throw new Error(`Missing encrypted response from ${ursula}`); - } - return encryptedResponse.decrypt(sharedSecret); - } + const decryptedResponses = Object.entries(encryptedResponses).map( + ([ursula, response]) => response.decrypt(sessionSharedSecret[ursula]) ); const ritualIds = decryptedResponses.map(({ ritualId }) => ritualId); From 1b2ae4e9f543d039a56934332b946fc853703d4e Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 27 Jun 2023 18:46:23 +0200 Subject: [PATCH 90/98] apply pr suggestions, part 2 --- src/characters/cbd-recipient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 4c32f3b2d..8a97e2eda 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -96,12 +96,12 @@ export class CbdTDecDecrypter { encryptedRequests, this.threshold ); - - if (Object.keys(errors).length > 0) { + if (Object.keys(encryptedResponses).length < this.threshold) { throw new Error( `CBD decryption failed with errors: ${JSON.stringify(errors)}` ); } + return this.makeDecryptionShares( encryptedResponses, sharedSecrets, From e076f1d2c96b093da3c3da1b994e57cb0ec39758 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 27 Jun 2023 18:51:53 +0200 Subject: [PATCH 91/98] chore(release): 1.0.0-alpha.0 --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da3cef5ae..660bcb317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,40 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.0.0-alpha.0](https://github.com/nucypher/nucypher-ts/compare/v1.0.0-beta.1...v1.0.0-alpha.0) (2023-06-27) + + +### ⚠ BREAKING CHANGES + +* hide dkg public params +* rename EvmCondition to ContractCondition +* remove unused revocation kit +* remove unused revoked strategy +* refactor conditions into a module + +### Features + +* add bare-bones impl of dkg client ([6f3eaf6](https://github.com/nucypher/nucypher-ts/commit/6f3eaf64d6b7c419d7b1159cd0a1a3264266e1ea)) +* add class metadata to serialized conditions ([76ac0f1](https://github.com/nucypher/nucypher-ts/commit/76ac0f1ae56ffcf9bb8410f015f52e3af6c98af1)) +* add conveniance method making encrypters ([81d23ef](https://github.com/nucypher/nucypher-ts/commit/81d23efb40f343cb84f22a99264b6836d2e482ac)) +* add coordinator contract ([2258350](https://github.com/nucypher/nucypher-ts/commit/22583506e564d4525e632e35a3b70697b2e43eb9)) +* add tdec endpoint to porter ([b6eb53e](https://github.com/nucypher/nucypher-ts/commit/b6eb53e1422c099d8c663ea5a97d82c67bf41d36)) +* hide dkg public params ([87e237f](https://github.com/nucypher/nucypher-ts/commit/87e237fcbad8cab509de12d2f4ff27c8ab79dc20)) +* make index in return value test optional ([ad52ec1](https://github.com/nucypher/nucypher-ts/commit/ad52ec174de3ad0ce1118b2fba7df47a96c262fb)) +* refactor conditions into a module ([85ff684](https://github.com/nucypher/nucypher-ts/commit/85ff684efe3e4a9b32d628e65606770459d96a7c)) +* remove unused revocation kit ([a0ea384](https://github.com/nucypher/nucypher-ts/commit/a0ea384ec813052c0fbab06e7f0644ebb105c089)) +* remove unused revoked strategy ([d1b9818](https://github.com/nucypher/nucypher-ts/commit/d1b9818b0513f093094d10945fc943c1690b7bcb)) +* rename EvmCondition to ContractCondition ([77278d6](https://github.com/nucypher/nucypher-ts/commit/77278d6cccacca87be176a0b807426a5a5b5c11f)) +* support user-provided params in condition context ([fa287d6](https://github.com/nucypher/nucypher-ts/commit/fa287d6132155fa9a453b0f6e3afc76bd36b57eb)) +* update nucypher-core to 0.9.0 ([e707abf](https://github.com/nucypher/nucypher-ts/commit/e707abf44665deda4df60ee838d72f1e743a52aa)) +* use e2e-encrypted decryption requests ([f78a55f](https://github.com/nucypher/nucypher-ts/commit/f78a55fb362c5c2eccc07f34d35ab89cc34118f7)) +* validate function abi params and method ([d7db27a](https://github.com/nucypher/nucypher-ts/commit/d7db27a90f8a27448568430a0654e6721813416e)) + + +### Bug Fixes + +* struct deser ([284df4a](https://github.com/nucypher/nucypher-ts/commit/284df4a26cb6c512749d918e079052808c4335c4)) + ## [1.0.0-beta.1](https://github.com/nucypher/nucypher-ts/compare/v1.0.0-beta.0...v1.0.0-beta.1) (2023-03-27) ### ⚠ BREAKING CHANGES diff --git a/package.json b/package.json index ce5a215c7..02b36a288 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@nucypher/nucypher-ts", "author": "Piotr Roslaniec ", - "version": "1.0.0-beta.1", + "version": "1.0.0-alpha.0", "license": "GPL-3.0-only", "repository": { "type": "git", From 78b330337c11e348023b620ab07fe884994626b4 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 27 Jun 2023 22:09:29 +0200 Subject: [PATCH 92/98] apply pr suggestions, part 3 --- src/characters/cbd-recipient.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts index 8a97e2eda..cef8d360c 100644 --- a/src/characters/cbd-recipient.ts +++ b/src/characters/cbd-recipient.ts @@ -98,7 +98,9 @@ export class CbdTDecDecrypter { ); if (Object.keys(encryptedResponses).length < this.threshold) { throw new Error( - `CBD decryption failed with errors: ${JSON.stringify(errors)}` + `Threshold of responses not met; CBD decryption failed with errors: ${JSON.stringify( + errors + )}` ); } From 32e6a94ed6a6a216041af93bb7c43c4c3bbcc9e0 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 16 Jun 2023 17:00:49 +0200 Subject: [PATCH 93/98] use ethers abi parser to validate function abi --- src/conditions/base/contract.ts | 49 +++++++++++++++------- test/unit/conditions/base/contract.test.ts | 24 +---------- test/unit/testVariables.ts | 2 +- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/conditions/base/contract.ts b/src/conditions/base/contract.ts index 83a13b6ae..c7f153f31 100644 --- a/src/conditions/base/contract.ts +++ b/src/conditions/base/contract.ts @@ -1,3 +1,4 @@ +import { ethers } from 'ethers'; import Joi from 'joi'; import { ETH_ADDRESS_REGEXP } from '../const'; @@ -6,31 +7,51 @@ import { RpcCondition, rpcConditionRecord } from './rpc'; export const STANDARD_CONTRACT_TYPES = ['ERC20', 'ERC721']; -const functionAbiVariable = Joi.object({ - internalType: Joi.string(), // TODO is this needed? - name: Joi.string().required(), - type: Joi.string().required(), -}); - const functionAbiSchema = Joi.object({ name: Joi.string().required(), type: Joi.string().valid('function').required(), - inputs: Joi.array().items(functionAbiVariable), - outputs: Joi.array().items(functionAbiVariable), - // TODO: Should we restrict this to 'view'? - stateMutability: Joi.string(), + inputs: Joi.array(), + outputs: Joi.array(), + stateMutability: Joi.string().valid('view', 'pure').required(), }).custom((functionAbi, helper) => { + // Is `functionABI` a valid function fragment? + let asInterface; + try { + asInterface = new ethers.utils.Interface([functionAbi]); + } catch (e: unknown) { + const { message } = e as Error; + return helper.message({ + custom: message, + }); + } + + if (!asInterface.fragments) { + return helper.message({ + custom: '"functionAbi" is missing a function fragment', + }); + } + + if (asInterface.fragments.length > 1) { + return helper.message({ + custom: '"functionAbi" must contain exactly one function fragment', + }); + } + + // Now we just need to validate against the parent schema // Validate method name const method = helper.state.ancestors[0].method; - if (functionAbi.name !== method) { + const functionFragment = asInterface.fragments.filter( + (f) => f.name === method + )[0]; + if (!functionFragment) { return helper.message({ - custom: '"method" must be the same as "functionAbi.name"', + custom: '"functionAbi" does not contain the method specified as "method"', }); } // Validate nr of parameters const parameters = helper.state.ancestors[0].parameters; - if (functionAbi.inputs?.length !== parameters.length) { + if (functionFragment.inputs.length !== parameters.length) { return helper.message({ custom: '"parameters" must have the same length as "functionAbi.inputs"', }); @@ -39,7 +60,7 @@ const functionAbiSchema = Joi.object({ return functionAbi; }); -export const contractConditionRecord: Record = { +export const contractConditionRecord = { ...rpcConditionRecord, contractAddress: Joi.string().pattern(ETH_ADDRESS_REGEXP).required(), standardContractType: Joi.string() diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts index 25fdddd8b..05074ddd1 100644 --- a/test/unit/conditions/base/contract.test.ts +++ b/test/unit/conditions/base/contract.test.ts @@ -7,7 +7,7 @@ import { import { ContractCondition } from '../../../../src/conditions/base'; import { USER_ADDRESS_PARAM } from '../../../../src/conditions/const'; import { fakeWeb3Provider } from '../../../utils'; -import { testContractConditionObj } from '../../testVariables'; +import { testContractConditionObj, testFunctionAbi } from '../../testVariables'; describe('validation', () => { it('accepts on a valid schema', () => { @@ -103,30 +103,10 @@ describe('accepts either standardContractType or functionAbi but not both or non }); describe('supports custom function abi', () => { - const fakeFunctionAbi = { - name: 'myFunction', - type: 'function', - inputs: [ - { - name: 'account', - type: 'address', - }, - { - name: 'myCustomParam', - type: 'uint256', - }, - ], - outputs: [ - { - name: 'someValue', - type: 'uint256', - }, - ], - }; const contractConditionObj = { ...testContractConditionObj, standardContractType: undefined, - functionAbi: fakeFunctionAbi, + functionAbi: testFunctionAbi, method: 'myFunction', parameters: [USER_ADDRESS_PARAM, ':customParam'], returnValueTest: { diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index ef943513a..36031e816 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -48,6 +48,7 @@ export const testContractConditionObj = { export const testFunctionAbi = { name: 'myFunction', type: 'function', + stateMutability: 'view', inputs: [ { internalType: 'address', @@ -67,5 +68,4 @@ export const testFunctionAbi = { type: 'uint256', }, ], - stateMutability: 'view', }; From 37a2b72590376baa2993bf525e605a4a666d782a Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 28 Jun 2023 15:33:38 +0200 Subject: [PATCH 94/98] apply pr suggestions --- src/conditions/base/contract.ts | 15 +-- test/unit/conditions/base/contract.test.ts | 107 ++++++++++++++++++++- 2 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src/conditions/base/contract.ts b/src/conditions/base/contract.ts index c7f153f31..522591b74 100644 --- a/src/conditions/base/contract.ts +++ b/src/conditions/base/contract.ts @@ -25,13 +25,13 @@ const functionAbiSchema = Joi.object({ }); } - if (!asInterface.fragments) { + if (!asInterface.functions) { return helper.message({ custom: '"functionAbi" is missing a function fragment', }); } - if (asInterface.fragments.length > 1) { + if (Object.values(asInterface.functions).length !== 1) { return helper.message({ custom: '"functionAbi" must contain exactly one function fragment', }); @@ -40,12 +40,15 @@ const functionAbiSchema = Joi.object({ // Now we just need to validate against the parent schema // Validate method name const method = helper.state.ancestors[0].method; - const functionFragment = asInterface.fragments.filter( - (f) => f.name === method - )[0]; + const abiMethodName = Object.keys(asInterface.functions).find((name) => + name.startsWith(`${method}(`) + ); + const functionFragment = abiMethodName + ? asInterface.functions[abiMethodName] + : null; if (!functionFragment) { return helper.message({ - custom: '"functionAbi" does not contain the method specified as "method"', + custom: `"functionAbi" not valid for method: "${method}"`, }); } diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts index 05074ddd1..ee988bbb6 100644 --- a/test/unit/conditions/base/contract.test.ts +++ b/test/unit/conditions/base/contract.test.ts @@ -123,7 +123,7 @@ describe('supports custom function abi', () => { const customParams: Record = {}; customParams[myCustomParam] = 1234; - it('accepts custom function abi', async () => { + it('accepts custom function abi with a custom parameter', async () => { const asJson = await conditionContext .withCustomParams(customParams) .toJson(); @@ -131,4 +131,109 @@ describe('supports custom function abi', () => { expect(asJson).toContain(USER_ADDRESS_PARAM); expect(asJson).toContain(myCustomParam); }); + + it.each([ + { + method: 'balanceOf', + functionAbi: { + name: 'balanceOf', + type: 'function', + inputs: [{ name: '_owner', type: 'address' }], + outputs: [{ name: 'balance', type: 'uint256' }], + stateMutability: 'view', + }, + }, + { + method: 'get', + functionAbi: { + name: 'get', + type: 'function', + inputs: [], + outputs: [], + stateMutability: 'pure', + }, + }, + ])('accepts well-formed functionAbi', ({ method, functionAbi }) => { + expect(() => + new ContractCondition({ + ...contractConditionObj, + parameters: functionAbi.inputs.map( + (input) => `fake_parameter_${input}` + ), // + functionAbi, + method, + }).toObj() + ).not.toThrow(); + }); + + it.each([ + { + method: '1234', + expectedError: '"functionAbi.name" must be a string', + functionAbi: { + name: 1234, // invalid value + type: 'function', + inputs: [{ name: '_owner', type: 'address' }], + outputs: [{ name: 'balance', type: 'uint256' }], + stateMutability: 'view', + }, + }, + { + method: 'transfer', + expectedError: '"functionAbi.inputs" must be an array', + functionAbi: { + name: 'transfer', + type: 'function', + inputs: 'invalid value', // invalid value + outputs: [{ name: '_status', type: 'bool' }], + stateMutability: 'pure', + }, + }, + { + method: 'get', + expectedError: + '"functionAbi.stateMutability" must be one of [view, pure]', + functionAbi: { + name: 'get', + type: 'function', + inputs: [], + outputs: [], + stateMutability: 'invalid', // invalid value + }, + }, + { + method: 'test', + expectedError: '"functionAbi.outputs" must be an array', + functionAbi: { + name: 'test', + type: 'function', + inputs: [], + outputs: 'invalid value', // Invalid value + stateMutability: 'pure', + }, + }, + { + method: 'calculatePow', + expectedError: + 'Invalid condition: "parameters" must have the same length as "functionAbi.inputs"', + functionAbi: { + name: 'calculatePow', + type: 'function', + // 'inputs': [] // Missing inputs array + outputs: [{ name: 'result', type: 'uint256' }], + stateMutability: 'view', + }, + }, + ])( + 'rejects malformed functionAbi', + ({ method, expectedError, functionAbi }) => { + expect(() => + new ContractCondition({ + ...contractConditionObj, + functionAbi, + method, + }).toObj() + ).toThrow(expectedError); + } + ); }); From 162e2baf2d9cfadc0108c3b6bf9cde45e6f5a0b8 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 28 Jun 2023 18:14:24 +0200 Subject: [PATCH 95/98] apply pr suggestions --- src/conditions/base/contract.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/conditions/base/contract.ts b/src/conditions/base/contract.ts index 522591b74..7f67d9be3 100644 --- a/src/conditions/base/contract.ts +++ b/src/conditions/base/contract.ts @@ -40,12 +40,16 @@ const functionAbiSchema = Joi.object({ // Now we just need to validate against the parent schema // Validate method name const method = helper.state.ancestors[0].method; - const abiMethodName = Object.keys(asInterface.functions).find((name) => - name.startsWith(`${method}(`) - ); - const functionFragment = abiMethodName - ? asInterface.functions[abiMethodName] - : null; + + let functionFragment; + try { + functionFragment = asInterface.getFunction(method); + } catch (e) { + return helper.message({ + custom: `"functionAbi" contains ambiguous "${method}"`, + }); + } + if (!functionFragment) { return helper.message({ custom: `"functionAbi" not valid for method: "${method}"`, From 5bd27b44e30d942daa9b7430a646133f8a6db4d4 Mon Sep 17 00:00:00 2001 From: piotr-roslaniec <39299780+piotr-roslaniec@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:27:04 +0200 Subject: [PATCH 96/98] Update src/conditions/base/contract.ts Co-authored-by: Derek Pierre --- src/conditions/base/contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conditions/base/contract.ts b/src/conditions/base/contract.ts index 7f67d9be3..c4221e1da 100644 --- a/src/conditions/base/contract.ts +++ b/src/conditions/base/contract.ts @@ -46,7 +46,7 @@ const functionAbiSchema = Joi.object({ functionFragment = asInterface.getFunction(method); } catch (e) { return helper.message({ - custom: `"functionAbi" contains ambiguous "${method}"`, + custom: `"functionAbi" has no matching function for "${method}"`, }); } From 5695bc0b83d97b23e9fb6f1d9b6c1f71cc604049 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 7 Jul 2023 15:25:45 +0200 Subject: [PATCH 97/98] fix!: incorrect condition parsing leading to undefined variables in context --- src/characters/porter.ts | 8 ++------ src/characters/pre-recipient.ts | 18 +++++++++--------- test/utils.ts | 2 +- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/characters/porter.ts b/src/characters/porter.ts index 1bcc6aa41..2a3ef1d6a 100644 --- a/src/characters/porter.ts +++ b/src/characters/porter.ts @@ -9,7 +9,6 @@ import { import axios, { AxiosResponse } from 'axios'; import qs from 'qs'; -import { ConditionContext } from '../conditions'; import { Base64EncodedBytes, ChecksumAddress, HexEncodedBytes } from '../types'; import { fromBase64, fromHexString, toBase64, toHexString } from '../utils'; @@ -138,18 +137,15 @@ export class Porter { aliceVerifyingKey: PublicKey, bobEncryptingKey: PublicKey, bobVerifyingKey: PublicKey, - conditionsContext?: ConditionContext + conditionContextJSON?: string | undefined ): Promise { - const context = conditionsContext - ? await conditionsContext.toJson() - : undefined; const data: PostRetrieveCFragsRequest = { treasure_map: toBase64(treasureMap.toBytes()), retrieval_kits: retrievalKits.map((rk) => toBase64(rk.toBytes())), alice_verifying_key: toHexString(aliceVerifyingKey.toCompressedBytes()), bob_encrypting_key: toHexString(bobEncryptingKey.toCompressedBytes()), bob_verifying_key: toHexString(bobVerifyingKey.toCompressedBytes()), - context, + context: conditionContextJSON, }; const resp: AxiosResponse = await axios.post( new URL('/retrieve_cfrags', this.porterUrl).toString(), diff --git a/src/characters/pre-recipient.ts b/src/characters/pre-recipient.ts index ae0fc8cda..329d9c627 100644 --- a/src/characters/pre-recipient.ts +++ b/src/characters/pre-recipient.ts @@ -8,7 +8,7 @@ import { } from '@nucypher/nucypher-core'; import { ethers } from 'ethers'; -import { Condition, ConditionContext } from '../conditions'; +import { ConditionContext, ConditionExpression } from '../conditions'; import { Keyring } from '../keyring'; import { PolicyMessageKit } from '../kits/message'; import { RetrievalResult } from '../kits/retrieval'; @@ -103,14 +103,11 @@ export class PreTDecDecrypter { const conditions = messageKits .map((mk) => mk.conditions) .filter((condition): condition is Conditions => !!condition) - .map((condition) => JSON.parse(condition.toString())) - .reduce((acc: Record[], val) => acc.concat(val), []); + .map((condition) => ConditionExpression.fromJSON(condition.toString())) + .reduce((acc: ConditionExpression[], val) => acc.concat(val), []) + .map((condExpr: ConditionExpression) => condExpr.condition); - const conditionsList = conditions.map((ele: Record) => { - return Condition.fromObj(ele); - }); - - const conditionContext = new ConditionContext(conditionsList, provider); + const conditionContext = new ConditionContext(conditions, provider); const policyMessageKits = messageKits.map((mk) => PolicyMessageKit.fromMessageKit( @@ -121,13 +118,16 @@ export class PreTDecDecrypter { ); const retrievalKits = policyMessageKits.map((pk) => pk.asRetrievalKit()); + const conditionContextJSON = conditionContext + ? await conditionContext.toJson() + : undefined; const retrieveCFragsResponses = await this.porter.retrieveCFrags( treasureMap, retrievalKits, this.publisherVerifyingKey, this.decryptingKey, this.keyring.publicKey, - conditionContext + conditionContextJSON ); return zip(policyMessageKits, retrieveCFragsResponses).map((pair) => { diff --git a/test/utils.ts b/test/utils.ts index b79ca46d6..46dce7df5 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -82,7 +82,7 @@ export const fakeAlice = (aliceKey = 'fake-secret-key-32-bytes-alice-x') => { }; export const fakeWeb3Provider = ( - secretKeyBytes: Uint8Array, + secretKeyBytes = SecretKey.random().toBEBytes(), blockNumber?: number, blockTimestamp?: number ): ethers.providers.Web3Provider => { From a0c34d88d595db1ab1b86174f5328fe4c9aebb81 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 10 Jul 2023 12:30:48 +0200 Subject: [PATCH 98/98] chore(release): 1.0.0-alpha.1 --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 660bcb317..dbafcf0f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.0.0-alpha.1](https://github.com/nucypher/nucypher-ts/compare/v1.0.0-alpha.0...v1.0.0-alpha.1) (2023-07-10) + + +### ⚠ BREAKING CHANGES + +* incorrect condition parsing leading to undefined variables in context + +### Bug Fixes + +* incorrect condition parsing leading to undefined variables in context ([5695bc0](https://github.com/nucypher/nucypher-ts/commit/5695bc0b83d97b23e9fb6f1d9b6c1f71cc604049)) + ## [1.0.0-alpha.0](https://github.com/nucypher/nucypher-ts/compare/v1.0.0-beta.1...v1.0.0-alpha.0) (2023-06-27) diff --git a/package.json b/package.json index 02b36a288..66c131599 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@nucypher/nucypher-ts", "author": "Piotr Roslaniec ", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.1", "license": "GPL-3.0-only", "repository": { "type": "git",