From 88328656b04e344b2e8135e3fc3d9ffa51e40f68 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Wed, 26 Apr 2023 11:39:59 +0200 Subject: [PATCH 1/2] #996 handle libraries with call protection --- packages/lib-sourcify/src/lib/verification.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/lib-sourcify/src/lib/verification.ts b/packages/lib-sourcify/src/lib/verification.ts index 8ff15b931..bd70d8dbd 100644 --- a/packages/lib-sourcify/src/lib/verification.ts +++ b/packages/lib-sourcify/src/lib/verification.ts @@ -237,6 +237,13 @@ export function matchWithDeployedBytecode( deployedBytecode: string, immutableReferences?: any ) { + // Check if is a library with call protection + // See https://docs.soliditylang.org/en/v0.8.19/contracts.html#call-protection-for-libraries + recompiledDeployedBytecode = checkCallProtectionAndReplaceAddress( + recompiledDeployedBytecode, + deployedBytecode + ); + // Replace the library placeholders in the recompiled bytecode with values from the deployed bytecode const { replaced, libraryMap } = addLibraryAddresses( recompiledDeployedBytecode, @@ -506,6 +513,20 @@ export function addLibraryAddresses( }; } +export function checkCallProtectionAndReplaceAddress( + template: string, + real: string +): string { + const push20CodeOp = '73'; + const callProtection = `0x${push20CodeOp}${'00'.repeat(20)}`; + + if (template.startsWith(callProtection)) { + const replacedCallProtection = real.slice(0, 0 + callProtection.length); + return replacedCallProtection + template.substring(callProtection.length); + } + return template; +} + /** * Replaces the values of the immutable variables in the (onchain) deployed bytecode with zeros, so that the bytecode can be compared with the (offchain) recompiled bytecode. * Example immutableReferences: {"97":[{"length":32,"start":137}],"99":[{"length":32,"start":421}]} where 97 and 99 are the AST ids From 4cc151949f9c94f154b53c78b13b224c98cd4aa6 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Fri, 28 Apr 2023 10:31:36 +0200 Subject: [PATCH 2/2] #996 add tests for call protection * legacy compilation placeholder test case * via-IR test case --- .../CallProtectionForLibraries/artifact.json | 4 ++ .../CallProtectionForLibraries/metadata.json | 27 +++++++++++++ .../sources/Ballot.sol | 7 ++++ .../artifact.json | 4 ++ .../metadata.json | 28 +++++++++++++ .../sources/Ballot.sol | 7 ++++ .../lib-sourcify/test/verification.spec.ts | 40 +++++++++++++++++++ 7 files changed, 117 insertions(+) create mode 100644 packages/lib-sourcify/test/sources/CallProtectionForLibraries/artifact.json create mode 100644 packages/lib-sourcify/test/sources/CallProtectionForLibraries/metadata.json create mode 100644 packages/lib-sourcify/test/sources/CallProtectionForLibraries/sources/Ballot.sol create mode 100644 packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/artifact.json create mode 100644 packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/metadata.json create mode 100644 packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/sources/Ballot.sol diff --git a/packages/lib-sourcify/test/sources/CallProtectionForLibraries/artifact.json b/packages/lib-sourcify/test/sources/CallProtectionForLibraries/artifact.json new file mode 100644 index 000000000..a14553168 --- /dev/null +++ b/packages/lib-sourcify/test/sources/CallProtectionForLibraries/artifact.json @@ -0,0 +1,4 @@ +{ + "abi": [], + "bytecode": "0x60bd610039600b82828239805160001a60731461002c57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063fe073d11146038575b600080fd5b818015604357600080fd5b50604a604c565b005b60006057600182605b565b5050565b60ff8181168382160190811115608157634e487b7160e01b600052601160045260246000fd5b9291505056fea264697066735822122026efb28b74c882aa2f1f86cbe51d40fba727849fa6113630f5c12c98c1a9eb9b64736f6c63430008130033" +} diff --git a/packages/lib-sourcify/test/sources/CallProtectionForLibraries/metadata.json b/packages/lib-sourcify/test/sources/CallProtectionForLibraries/metadata.json new file mode 100644 index 000000000..2fb60e9be --- /dev/null +++ b/packages/lib-sourcify/test/sources/CallProtectionForLibraries/metadata.json @@ -0,0 +1,27 @@ +{ + "compiler": { "version": "0.8.19+commit.7dd6d404" }, + "language": "Solidity", + "output": { + "abi": [], + "devdoc": { "kind": "dev", "methods": {}, "version": 1 }, + "userdoc": { "kind": "user", "methods": {}, "version": 1 } + }, + "settings": { + "compilationTarget": { "Ballot.sol": "Ballot" }, + "evmVersion": "paris", + "libraries": {}, + "metadata": { "bytecodeHash": "ipfs" }, + "optimizer": { "enabled": true, "runs": 200 }, + "remappings": [] + }, + "sources": { + "Ballot.sol": { + "keccak256": "0x21d251aa06c7fcbb362bca4b1645ef4fbc3d0844f6774f4598588f9045fdfe97", + "urls": [ + "bzz-raw://7464b55b34107bd79040fa11eed489e2d20bde5e15b801e2ba144d7bd10bbeca", + "dweb:/ipfs/Qmbrd7Up3r6Q5JA9NMvZZawsJ5EuuoFfRuMBEjAYk7oDod" + ] + } + }, + "version": 1 +} diff --git a/packages/lib-sourcify/test/sources/CallProtectionForLibraries/sources/Ballot.sol b/packages/lib-sourcify/test/sources/CallProtectionForLibraries/sources/Ballot.sol new file mode 100644 index 000000000..8714a01b0 --- /dev/null +++ b/packages/lib-sourcify/test/sources/CallProtectionForLibraries/sources/Ballot.sol @@ -0,0 +1,7 @@ +pragma solidity 0.8.19; +library Ballot { + function giveRightToVote() public { + uint8 tempnumber = 0; + tempnumber += 1; + } +} \ No newline at end of file diff --git a/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/artifact.json b/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/artifact.json new file mode 100644 index 000000000..f7c1d4fb5 --- /dev/null +++ b/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/artifact.json @@ -0,0 +1,4 @@ +{ + "abi": [], + "bytecode": "0x60808060405234601a57608d90816100208239308160240152f35b600080fdfe6004361015600c57600080fd5b6000803560e01c63fe073d1114602157600080fd5b307f0000000000000000000000000000000000000000000000000000000000000000146054578060031936011260545780f35b80fdfea26469706673582212201104b959cf92e8eead2cb6513fe1d88ee97c7b264a0dea0bd7ccaffd488db72764736f6c63430008130033" +} diff --git a/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/metadata.json b/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/metadata.json new file mode 100644 index 000000000..14d67f26e --- /dev/null +++ b/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/metadata.json @@ -0,0 +1,28 @@ +{ + "compiler": { "version": "0.8.19+commit.7dd6d404" }, + "language": "Solidity", + "output": { + "abi": [], + "devdoc": { "kind": "dev", "methods": {}, "version": 1 }, + "userdoc": { "kind": "user", "methods": {}, "version": 1 } + }, + "settings": { + "compilationTarget": { "Ballot.sol": "Ballot" }, + "evmVersion": "paris", + "libraries": {}, + "metadata": { "bytecodeHash": "ipfs" }, + "optimizer": { "enabled": true, "runs": 200 }, + "remappings": [], + "viaIR": true + }, + "sources": { + "Ballot.sol": { + "keccak256": "0x21d251aa06c7fcbb362bca4b1645ef4fbc3d0844f6774f4598588f9045fdfe97", + "urls": [ + "bzz-raw://7464b55b34107bd79040fa11eed489e2d20bde5e15b801e2ba144d7bd10bbeca", + "dweb:/ipfs/Qmbrd7Up3r6Q5JA9NMvZZawsJ5EuuoFfRuMBEjAYk7oDod" + ] + } + }, + "version": 1 +} diff --git a/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/sources/Ballot.sol b/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/sources/Ballot.sol new file mode 100644 index 000000000..8714a01b0 --- /dev/null +++ b/packages/lib-sourcify/test/sources/CallProtectionForLibrariesViaIR/sources/Ballot.sol @@ -0,0 +1,7 @@ +pragma solidity 0.8.19; +library Ballot { + function giveRightToVote() public { + uint8 tempnumber = 0; + tempnumber += 1; + } +} \ No newline at end of file diff --git a/packages/lib-sourcify/test/verification.spec.ts b/packages/lib-sourcify/test/verification.spec.ts index 8a3675638..d7f400ca8 100644 --- a/packages/lib-sourcify/test/verification.spec.ts +++ b/packages/lib-sourcify/test/verification.spec.ts @@ -398,6 +398,46 @@ describe('lib-sourcify tests', () => { ); expectMatch(match, 'perfect', deployedAddress); }); + + it('should fully verify a library with call protection when viaIR is disabled (legacy compilation placeholder: 0x73 plus 20 zero bytes)', async () => { + const contractFolderPath = path.join( + __dirname, + 'sources', + 'CallProtectionForLibraries' + ); + const [deployedAddress] = await deployFromAbiAndBytecode( + localWeb3Provider, + contractFolderPath, + accounts[0] + ); + + const match = await checkAndVerifyDeployed( + contractFolderPath, + sourcifyChainGanache, + deployedAddress + ); + expectMatch(match, 'perfect', deployedAddress); + }); + + it('should fully verify a library with call protection when viaIR is enabled', async () => { + const contractFolderPath = path.join( + __dirname, + 'sources', + 'CallProtectionForLibrariesViaIR' + ); + const [deployedAddress] = await deployFromAbiAndBytecode( + localWeb3Provider, + contractFolderPath, + accounts[0] + ); + + const match = await checkAndVerifyDeployed( + contractFolderPath, + sourcifyChainGanache, + deployedAddress + ); + expectMatch(match, 'perfect', deployedAddress); + }); }); describe('Unit tests', function () {