From 61afbb73908fc1ee43d85831d9f9d02632c24c28 Mon Sep 17 00:00:00 2001 From: "will.li" <120463031+higherordertech@users.noreply.github.com> Date: Wed, 28 Aug 2024 23:12:35 +1000 Subject: [PATCH] fix: P-885 add $network clause in solidity VC (#3026) Co-authored-by: higherordertech --- .../src/dynamic/contracts/A20.sol | 11 +-- .../contracts/libraries/AssertionLogic.sol | 30 ++++++-- .../contracts/libraries/Identities.sol | 60 ++++++++++++++++ .../TokenHoldingAmount.sol | 23 ++++++- .../src/dynamic/tests/token-holding-amount.ts | 69 +++++++++++++++++++ 5 files changed, 181 insertions(+), 12 deletions(-) diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/A20.sol b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/A20.sol index 10b49dda7c..08ef307302 100644 --- a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/A20.sol +++ b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/A20.sol @@ -85,11 +85,12 @@ contract A20 is DynamicAssertion { } Logging.info("begin create assertion for A20"); - AssertionLogic.Condition memory condition = AssertionLogic.Condition( - "$has_joined", - AssertionLogic.Op.Equal, - "true" - ); + AssertionLogic.Condition memory condition = AssertionLogic + .newConditionWithoutSubCc( + "$has_joined", + AssertionLogic.Op.Equal, + "true" + ); string[] memory assertions = new string[](1); assertions[0] = AssertionLogic.toString(condition); diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/AssertionLogic.sol b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/AssertionLogic.sol index 4adfc0b94b..442615eb69 100644 --- a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/AssertionLogic.sol +++ b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/AssertionLogic.sol @@ -34,6 +34,7 @@ library AssertionLogic { string src; Op op; string dst; + CompositeCondition cc; } struct CompositeCondition { @@ -41,6 +42,15 @@ library AssertionLogic { bool isAnd; // true for 'And', false for 'Or' } + function newConditionWithoutSubCc( + string memory src, + Op op, + string memory dst + ) internal pure returns (Condition memory) { + CompositeCondition memory subCc; + return Condition(src, op, dst, subCc); + } + function addCondition( CompositeCondition memory cc, uint256 i, @@ -48,7 +58,16 @@ library AssertionLogic { Op op, string memory dst ) internal pure { - cc.conditions[i] = Condition(src, op, dst); + CompositeCondition memory subCc; + cc.conditions[i] = Condition(src, op, dst, subCc); + } + + function addCompositeCondition( + CompositeCondition memory cc, + uint256 i, + CompositeCondition memory subCc + ) internal pure { + cc.conditions[i] = Condition("", Op.Equal, "", subCc); } function andOp( @@ -85,12 +104,15 @@ library AssertionLogic { abi.encodePacked(result, cc.isAnd ? '"and":[' : '"or":[') ); for (uint256 i = 0; i < cc.conditions.length; i++) { + Condition memory c = cc.conditions[i]; if (i > 0) { result = string(abi.encodePacked(result, ",")); } - result = string( - abi.encodePacked(result, toString(cc.conditions[i])) - ); + if (c.cc.conditions.length > 0) { + result = string(abi.encodePacked(result, toString(c.cc))); + } else { + result = string(abi.encodePacked(result, toString(c))); + } } result = string(abi.encodePacked(result, "]")); } diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/Identities.sol b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/Identities.sol index 66298be42e..03d823b09d 100644 --- a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/Identities.sol +++ b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/Identities.sol @@ -276,4 +276,64 @@ library Identities { chain = "combo"; } } + + function get_network_name( + uint32 network + ) internal pure returns (string memory) { + if (network == Web3Networks.Polkadot) { + return "Polkadot"; + } + if (network == Web3Networks.Kusama) { + return "Kusama"; + } + if (network == Web3Networks.Litentry) { + return "Litentry"; + } + if (network == Web3Networks.Litmus) { + return "Litmus"; + } + if (network == Web3Networks.LitentryRococo) { + return "LitentryRococo"; + } + if (network == Web3Networks.Khala) { + return "Khala"; + } + if (network == Web3Networks.SubstrateTestnet) { + return "SubstrateTestnet"; + } + if (network == Web3Networks.Ethereum) { + return "Ethereum"; + } + if (network == Web3Networks.Bsc) { + return "Bsc"; + } + if (network == Web3Networks.Polygon) { + return "Polygon"; + } + if (network == Web3Networks.Arbitrum) { + return "Arbitrum"; + } + if (network == Web3Networks.Solana) { + return "Solana"; + } + if (network == Web3Networks.Combo) { + return "Combo"; + } + if (network == Web3Networks.BitcoinP2tr) { + return "BitcoinP2tr"; + } + if (network == Web3Networks.BitcoinP2pkh) { + return "BitcoinP2pkh"; + } + if (network == Web3Networks.BitcoinP2sh) { + return "BitcoinP2sh"; + } + if (network == Web3Networks.BitcoinP2wpkh) { + return "BitcoinP2wpkh"; + } + if (network == Web3Networks.BitcoinP2wsh) { + return "BitcoinP2wsh"; + } + return ""; + } } diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/token_holding_amount/TokenHoldingAmount.sol b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/token_holding_amount/TokenHoldingAmount.sol index fa24fb2f7c..7fea67b0bf 100644 --- a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/token_holding_amount/TokenHoldingAmount.sol +++ b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/token_holding_amount/TokenHoldingAmount.sol @@ -150,7 +150,7 @@ abstract contract TokenHoldingAmount is DynamicAssertion { string memory variable = "$holding_amount"; AssertionLogic.CompositeCondition memory cc = AssertionLogic .CompositeCondition( - new AssertionLogic.Condition[](max > 0 && balance > 0 ? 3 : 2), + new AssertionLogic.Condition[](max > 0 && balance > 0 ? 4 : 3), true ); AssertionLogic.andOp( @@ -160,9 +160,26 @@ abstract contract TokenHoldingAmount is DynamicAssertion { AssertionLogic.Op.Equal, tokenName ); + + AssertionLogic.CompositeCondition memory networkCc = AssertionLogic + .CompositeCondition( + new AssertionLogic.Condition[](token.networks.length), + false + ); + AssertionLogic.addCompositeCondition(cc, 1, networkCc); + for (uint256 i = 0; i < token.networks.length; i++) { + AssertionLogic.andOp( + networkCc, + i, + "$network", + AssertionLogic.Op.Equal, + Identities.get_network_name(token.networks[i].network) + ); + } + AssertionLogic.andOp( cc, - 1, + 2, variable, min == 0 ? AssertionLogic.Op.GreaterThan @@ -172,7 +189,7 @@ abstract contract TokenHoldingAmount is DynamicAssertion { if (max > 0 && balance > 0) { AssertionLogic.andOp( cc, - 2, + 3, variable, AssertionLogic.Op.LessThan, StringShift.toShiftedString( diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/tests/token-holding-amount.ts b/tee-worker/litentry/core/assertion-build/src/dynamic/tests/token-holding-amount.ts index 669b2736d7..fb8e54bfe9 100644 --- a/tee-worker/litentry/core/assertion-build/src/dynamic/tests/token-holding-amount.ts +++ b/tee-worker/litentry/core/assertion-build/src/dynamic/tests/token-holding-amount.ts @@ -62,6 +62,16 @@ describe('TokenHoldingAmount', () => { }) describe('BRC20', () => { + const networkClause = { + and: [ + { src: '$network', op: '==', dst: 'BitcoinP2tr' }, + { src: '$network', op: '==', dst: 'BitcoinP2pkh' }, + { src: '$network', op: '==', dst: 'BitcoinP2sh' }, + { src: '$network', op: '==', dst: 'BitcoinP2wpkh' }, + { src: '$network', op: '==', dst: 'BitcoinP2wsh' }, + ], + } + const expectOrdiFalseResult = (contract: any, val: any) => expectResult( contract, @@ -73,6 +83,7 @@ describe('TokenHoldingAmount', () => { op: Op.EQ, dst: 'ordi', }, + networkClause, { src: '$holding_amount', op: Op.GT, @@ -132,6 +143,7 @@ describe('TokenHoldingAmount', () => { op: Op.EQ, dst: 'ordi', }, + networkClause, { src: '$holding_amount', op: Op.GT, @@ -176,6 +188,7 @@ describe('TokenHoldingAmount', () => { op: Op.EQ, dst: 'ordi', }, + networkClause, { src: '$holding_amount', op: Op.GTE, @@ -220,6 +233,7 @@ describe('TokenHoldingAmount', () => { op: Op.EQ, dst: 'ordi', }, + networkClause, { src: '$holding_amount', op: Op.GTE, @@ -264,6 +278,7 @@ describe('TokenHoldingAmount', () => { op: Op.EQ, dst: 'ordi', }, + networkClause, { src: '$holding_amount', op: Op.GTE, @@ -334,6 +349,16 @@ describe('TokenHoldingAmount', () => { }) describe('Btc', () => { + const networkClause = { + and: [ + { src: '$network', op: '==', dst: 'BitcoinP2tr' }, + { src: '$network', op: '==', dst: 'BitcoinP2pkh' }, + { src: '$network', op: '==', dst: 'BitcoinP2sh' }, + { src: '$network', op: '==', dst: 'BitcoinP2wpkh' }, + { src: '$network', op: '==', dst: 'BitcoinP2wsh' }, + ], + } + it('should return result false when amount = 0', async () => { const { TokenMapping } = await loadFixture(deployFixture) const val = TokenMapping.execute( @@ -362,6 +387,7 @@ describe('TokenHoldingAmount', () => { op: Op.EQ, dst: 'btc', }, + networkClause, { src: '$holding_amount', op: Op.GTE, @@ -405,6 +431,7 @@ describe('TokenHoldingAmount', () => { op: Op.EQ, dst: 'btc', }, + networkClause, { src: '$holding_amount', op: Op.GTE, @@ -426,6 +453,14 @@ describe('TokenHoldingAmount', () => { describe('Atom', () => { const tokenName = 'atom' + const networkClause = { + and: [ + { src: '$network', op: '==', dst: 'Ethereum' }, + { src: '$network', op: '==', dst: 'Bsc' }, + { src: '$network', op: '==', dst: 'Polygon' }, + ], + } + it('should return result false when amount = 0', async () => { const { TokenMapping } = await loadFixture(deployFixture) const val = TokenMapping.execute( @@ -450,6 +485,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>', dst: '0' }, ], }, @@ -481,6 +517,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>', dst: '0' }, { src: '$holding_amount', op: '<', dst: '1' }, ], @@ -513,6 +550,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>=', dst: '1' }, { src: '$holding_amount', op: '<', dst: '5' }, ], @@ -545,6 +583,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>=', dst: '20' }, { src: '$holding_amount', op: '<', dst: '50' }, ], @@ -577,6 +616,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>=', dst: '50' }, { src: '$holding_amount', op: '<', dst: '80' }, ], @@ -613,6 +653,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>=', dst: '80' }, ], }, @@ -624,6 +665,13 @@ describe('TokenHoldingAmount', () => { describe('Bean', () => { const tokenName = 'bean' + const networkClause = { + and: [ + { src: '$network', op: '==', dst: 'Bsc' }, + { src: '$network', op: '==', dst: 'Combo' }, + ], + } + it('should return result false when amount = 0', async () => { const { TokenMapping } = await loadFixture(deployFixture) const val = TokenMapping.execute( @@ -648,6 +696,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>', dst: '0' }, ], }, @@ -679,6 +728,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>=', dst: '1500' }, { src: '$holding_amount', op: '<', dst: '5000' }, ], @@ -711,6 +761,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>=', dst: '50000' }, ], }, @@ -722,6 +773,16 @@ describe('TokenHoldingAmount', () => { describe('Dai', () => { const tokenName = 'dai' + const networkClause = { + and: [ + { src: '$network', op: '==', dst: 'Ethereum' }, + { src: '$network', op: '==', dst: 'Bsc' }, + { src: '$network', op: '==', dst: 'Solana' }, + { src: '$network', op: '==', dst: 'Arbitrum' }, + { src: '$network', op: '==', dst: 'Polygon' }, + ], + } + it('should return result false when amount = 0', async () => { const { TokenMapping } = await loadFixture(deployFixture) const val = TokenMapping.execute( @@ -746,6 +807,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>', dst: '0' }, ], }, @@ -777,6 +839,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>', dst: '0' }, { src: '$holding_amount', op: '<', dst: '10' }, ], @@ -789,6 +852,10 @@ describe('TokenHoldingAmount', () => { describe('Wbtc', () => { const tokenName = 'wbtc' + const networkClause = { + and: [{ src: '$network', op: '==', dst: 'Ethereum' }], + } + it('should return result false when amount = 0', async () => { const { TokenMapping } = await loadFixture(deployFixture) const val = TokenMapping.execute( @@ -813,6 +880,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>', dst: '0' }, ], }, @@ -844,6 +912,7 @@ describe('TokenHoldingAmount', () => { { and: [ { src: '$token', op: '==', dst: tokenName }, + networkClause, { src: '$holding_amount', op: '>', dst: '0' }, { src: '$holding_amount', op: '<', dst: '0.001' }, ],