Skip to content

Commit

Permalink
Handle libraries with call protection (#1000)
Browse files Browse the repository at this point in the history
* #996 handle libraries with call protection

* #996 add tests for call protection
* legacy compilation placeholder test case
* via-IR test case
  • Loading branch information
marcocastignoli authored May 3, 2023
1 parent 3f435a5 commit b84e466
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 0 deletions.
21 changes: 21 additions & 0 deletions packages/lib-sourcify/src/lib/verification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"abi": [],
"bytecode": "0x60bd610039600b82828239805160001a60731461002c57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063fe073d11146038575b600080fd5b818015604357600080fd5b50604a604c565b005b60006057600182605b565b5050565b60ff8181168382160190811115608157634e487b7160e01b600052601160045260246000fd5b9291505056fea264697066735822122026efb28b74c882aa2f1f86cbe51d40fba727849fa6113630f5c12c98c1a9eb9b64736f6c63430008130033"
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity 0.8.19;
library Ballot {
function giveRightToVote() public {
uint8 tempnumber = 0;
tempnumber += 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"abi": [],
"bytecode": "0x60808060405234601a57608d90816100208239308160240152f35b600080fdfe6004361015600c57600080fd5b6000803560e01c63fe073d1114602157600080fd5b307f0000000000000000000000000000000000000000000000000000000000000000146054578060031936011260545780f35b80fdfea26469706673582212201104b959cf92e8eead2cb6513fe1d88ee97c7b264a0dea0bd7ccaffd488db72764736f6c63430008130033"
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity 0.8.19;
library Ballot {
function giveRightToVote() public {
uint8 tempnumber = 0;
tempnumber += 1;
}
}
40 changes: 40 additions & 0 deletions packages/lib-sourcify/test/verification.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand Down

0 comments on commit b84e466

Please sign in to comment.