From 1bf0d3029e3d5aec919d4e73696ca8c8d7252d32 Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Tue, 4 Oct 2022 13:22:55 +0200 Subject: [PATCH] Add linkReferences map as optional parameter to linkBytecode function Co-authored-by: arthcp --- linker.ts | 26 +++++++++++++++++++------- test/linker.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/linker.ts b/linker.ts index 619cae57..fa2929b2 100644 --- a/linker.ts +++ b/linker.ts @@ -27,14 +27,24 @@ function libraryHashPlaceholder (fullyQualifiedLibraryName) { * * @param address Address to replace placeholders with. Must be the right length. * It will **not** be padded with zeros if too short. + * + * @param linkReferences A optional mapping of libraries to lists of placeholder positions in the binary. */ -function replacePlaceholder (bytecode, label, address) { +function replacePlaceholder (bytecode: string, label: string, address: string, linkReferences?: LinkReferences): string { + // Try to find link references if `linkReferences` is not provided + if (!linkReferences) { + linkReferences = findLinkReferences(bytecode); + } + // truncate to 36 characters const truncatedName = label.slice(0, 36); - const libLabel = `__${truncatedName.padEnd(36, '_')}__`; - while (bytecode.indexOf(libLabel) >= 0) { - bytecode = bytecode.replace(libLabel, address); + if (linkReferences && linkReferences[truncatedName]) { + linkReferences[truncatedName].forEach(function (reference) { + const start = reference.start * 2; + const end = (reference.start + reference.length) * 2; + bytecode = bytecode.replace(bytecode.substring(start, end), address); + }); } return bytecode; @@ -55,10 +65,12 @@ function replacePlaceholder (bytecode, label, address) { * @param libraries Mapping between fully qualified library names and the hex-encoded * addresses they should be replaced with. Addresses shorter than 40 characters are automatically padded with zeros. * + * @param linkReferences A optional mapping of libraries to lists of placeholder positions in the binary. + * * @returns bytecode Hex-encoded bytecode string with placeholders replaced with addresses. * Note that some placeholders may remain in the bytecode if `libraries` does not provide addresses for all of them. */ -function linkBytecode (bytecode: string, libraries: LibraryAddresses): string { +function linkBytecode (bytecode: string, libraries: LibraryAddresses, linkReferences?: LinkReferences): string { assert(typeof bytecode === 'string'); assert(typeof libraries === 'object'); @@ -103,8 +115,8 @@ function linkBytecode (bytecode: string, libraries: LibraryAddresses): string { // remove 0x prefix hexAddress = hexAddress.slice(2).padStart(40, '0'); - bytecode = replacePlaceholder(bytecode, libraryName, hexAddress); - bytecode = replacePlaceholder(bytecode, libraryHashPlaceholder(libraryName), hexAddress); + bytecode = replacePlaceholder(bytecode, libraryName, hexAddress, linkReferences); + bytecode = replacePlaceholder(bytecode, libraryHashPlaceholder(libraryName), hexAddress, linkReferences); } return bytecode; diff --git a/test/linker.ts b/test/linker.ts index 588b9992..a074b2ba 100644 --- a/test/linker.ts +++ b/test/linker.ts @@ -166,4 +166,36 @@ tape('Linking', function (t) { st.ok(bytecode.indexOf('_') < 0); st.end(); }); + + t.test('link properly by providing link reference', function (st) { + /* + 'lib.sol': 'library L { function f() public returns (uint) { return 7; } }', + 'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }' + */ + let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; + bytecode = linker.linkBytecode(bytecode, { 'lib.sol:L': '0x123456' }, { 'lib.sol:L': [{ start: 122, length: 20 }] }); + st.ok(bytecode.indexOf('_') < 0); + st.end(); + }); + + t.test('link properly by providing two references with same library name', function (st) { + let bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080fd5b5050505600a165627a7a72305820fdfb8eab411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029'; + bytecode = linker.linkBytecode(bytecode, { 'lib2.sol:L': '0x123456' }, { 'lib2.sol:L': [{ start: 92, length: 20 }, { start: 180, length: 20 }] }); + st.ok(bytecode.indexOf('_') < 0); + st.end(); + }); + + t.test('link properly by providing multiple references for multiple libraries', function (st) { + let bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:Lx___________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__lib2.sol:Lx___________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080__lib1.sol:L____________________________411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029'; + bytecode = linker.linkBytecode(bytecode, { 'lib1.sol:L': '0x123456', 'lib2.sol:Lx': '0x789012' }, { 'lib2.sol:Lx': [{ start: 92, length: 20 }, { start: 180, length: 20 }], 'lib1.sol:L': [{ start: 262, length: 20 }] }); + st.ok(bytecode.indexOf('_') < 0); + st.end(); + }); + + t.test('linker fails if wrong link references is provided', function (st) { + let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029'; + bytecode = linker.linkBytecode(bytecode, { 'lib1.sol:L': '0x123456' }, {}); + st.ok(bytecode.indexOf('_') >= 0); + st.end(); + }); });