Skip to content

Commit 9fbe261

Browse files
authored
Merge pull request #192 from ethereum/link-refs
Support finding link references
2 parents 2b9e51c + 7f2ae41 commit 9fbe261

File tree

5 files changed

+142
-5
lines changed

5 files changed

+142
-5
lines changed

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,27 @@ solc.loadRemoteVersion('latest', function (err, solcSnapshot) {
146146

147147
When using libraries, the resulting bytecode will contain placeholders for the real addresses of the referenced libraries. These have to be updated, via a process called linking, before deploying the contract.
148148

149+
The `linker` module (`require('solc/linker')`) offers helpers to accomplish this.
150+
149151
The `linkBytecode` method provides a simple helper for linking:
150152

151153
```javascript
152-
bytecode = solc.linkBytecode(bytecode, { 'MyLibrary': '0x123456...' })
154+
var linker = require('solc/linker')
155+
156+
bytecode = linker.linkBytecode(bytecode, { 'MyLibrary': '0x123456...' })
153157
```
154158

155-
Note: in future versions of Solidity a more sophisticated linker architecture will be introduced. Once that changes, this method will still be usable for output created by old versions of Solidity.
159+
(Note: `linkBytecode` is also exposed via `solc` as `solc.linkBytecode`, but this usage is deprecated.)
160+
161+
As of Solidity 0.4.11 the compiler supports [standard JSON input and output](https://solidity.readthedocs.io/en/develop/using-the-compiler.html#compiler-input-and-output-json-description) which outputs a *link references* map. This gives a map of library names to offsets in the bytecode to replace the addresses at. It also doesn't have the limitation on library file and contract name lengths.
162+
163+
There is a method available in the `linker` module called `findLinkReferences` which can find such link references in bytecode produced by an older compiler:
164+
165+
```javascript
166+
var linker = require('solc/linker')
167+
168+
var linkReferences = linker.findLinkReferences(bytecode)
169+
```
156170

157171
### Updating the ABI
158172

linker.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,40 @@ var linkBytecode = function (bytecode, libraries) {
3131
return bytecode;
3232
};
3333

34+
var findLinkReferences = function (bytecode) {
35+
// find 40 bytes in the pattern of __...<36 digits>...__
36+
// e.g. __Lib.sol:L_____________________________
37+
var linkReferences = {};
38+
var offset = 0;
39+
while (true) {
40+
var found = bytecode.match(/__(.{36})__/);
41+
if (!found) {
42+
break;
43+
}
44+
45+
var start = found.index;
46+
// trim trailing underscores
47+
// NOTE: this has no way of knowing if the trailing underscore was part of the name
48+
var libraryName = found[1].replace(/_+$/gm, '');
49+
50+
if (!linkReferences[libraryName]) {
51+
linkReferences[libraryName] = [];
52+
}
53+
54+
linkReferences[libraryName].push({
55+
// offsets are in bytes in binary representation (and not hex)
56+
start: (offset + start) / 2,
57+
length: 20
58+
});
59+
60+
offset += start + 20;
61+
62+
bytecode = bytecode.slice(start + 20);
63+
}
64+
return linkReferences;
65+
};
66+
3467
module.exports = {
35-
linkBytecode: linkBytecode
68+
linkBytecode: linkBytecode,
69+
findLinkReferences: findLinkReferences
3670
};

test/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require('./linker.js');
12
require('./translate.js');
23
require('./package.js');
34
require('./abi.js');

test/linker.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
const tape = require('tape');
2+
const linker = require('../linker.js');
3+
4+
tape('Link references', function (t) {
5+
t.test('Empty bytecode', function (st) {
6+
st.deepEqual(linker.findLinkReferences(''), {});
7+
st.end();
8+
});
9+
10+
t.test('No references', function (st) {
11+
st.deepEqual(linker.findLinkReferences('6060604052341561000f57600080fd'), {});
12+
st.end();
13+
});
14+
15+
t.test('One reference', function (st) {
16+
var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a723058207979b30bd4a07c77b02774a511f2a1dd04d7e5d65b5c2735b5fc96ad61d43ae40029';
17+
st.deepEqual(linker.findLinkReferences(bytecode), { 'lib2.sol:L': [ { start: 116, length: 20 } ] });
18+
st.end();
19+
});
20+
21+
t.test('Two references', function (st) {
22+
var bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__linkref.sol:Lx________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080fd5b5050505600a165627a7a72305820fdfb8eab411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029';
23+
st.deepEqual(
24+
linker.findLinkReferences(bytecode),
25+
{ 'lib2.sol:L': [ { start: 92, length: 20 } ], 'linkref.sol:Lx': [ { start: 180, length: 20 } ] }
26+
);
27+
st.end();
28+
});
29+
30+
t.test('Library name with leading underscore', function (st) {
31+
var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:_L___________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582089689827bbf0b7dc385ffcb4b1deb9f9e61741f61f89b4af65f806ff2b0d73470029';
32+
st.deepEqual(
33+
linker.findLinkReferences(bytecode),
34+
{ 'lib2.sol:_L': [ { start: 116, length: 20 } ] }
35+
);
36+
st.end();
37+
});
38+
39+
t.test('Library name with leading underscore (without fqdn)', function (st) {
40+
var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73___L____________________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582089689827bbf0b7dc385ffcb4b1deb9f9e61741f61f89b4af65f806ff2b0d73470029';
41+
st.deepEqual(
42+
linker.findLinkReferences(bytecode),
43+
{ '_L': [ { start: 116, length: 20 } ] }
44+
);
45+
st.end();
46+
});
47+
48+
t.test('Library name with underscore in the name', function (st) {
49+
var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L_L__________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a723058205cb324a27452cc7f8894a57cb0e3ddce2dce0c423e4fc993a3dd51287abd49110029';
50+
st.deepEqual(
51+
linker.findLinkReferences(bytecode),
52+
{ 'lib2.sol:L_L': [ { start: 116, length: 20 } ] }
53+
);
54+
st.end();
55+
});
56+
57+
// Note: this is a case the reference finder cannot properly handle as there's no way to tell
58+
t.test('Library name with trailing underscore', function (st) {
59+
var bytecode = '6060604052341561000f57600080fd5b60f48061001d6000396000f300606060405260043610603e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166326121ff081146043575b600080fd5b3415604d57600080fd5b60536055565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b151560b357600080fd5b6102c65a03f4151560c357600080fd5b5050505600a165627a7a7230582058e61511a603707222cfa83fd3ae4269f94eb86513cb9042cf0b44877403d85c0029';
60+
st.deepEqual(
61+
linker.findLinkReferences(bytecode),
62+
{ 'lib2.sol:L': [ { start: 116, length: 20 } ] }
63+
);
64+
st.end();
65+
});
66+
67+
t.test('Invalid input (too short)', function (st) {
68+
var bytecode = '6060604052341561000____66606060606060';
69+
st.deepEqual(
70+
linker.findLinkReferences(bytecode),
71+
{}
72+
);
73+
st.end();
74+
});
75+
76+
t.test('Invalid input (1 byte short)', function (st) {
77+
var bytecode = '6060604052341561000__lib2.sol:L___________________________66606060606060';
78+
st.deepEqual(
79+
linker.findLinkReferences(bytecode),
80+
{}
81+
);
82+
st.end();
83+
});
84+
});

translate.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
var linker = require('./linker.js');
2+
13
/// Translate old style version numbers to semver.
24
/// Old style: 0.3.6-3fc68da5/Release-Emscripten/clang
35
/// 0.3.5-371690f0/Release-Emscripten/clang/Interpreter
@@ -110,11 +112,13 @@ function translateJsonCompilerOutput (output) {
110112
'bytecode': {
111113
'object': contractInput['bytecode'],
112114
'opcodes': contractInput['opcodes'],
113-
'sourceMap': contractInput['srcmap']
115+
'sourceMap': contractInput['srcmap'],
116+
'linkReferences': linker.findLinkReferences(contractInput['bytecode'])
114117
},
115118
'deployedBytecode': {
116119
'object': contractInput['runtimeBytecode'],
117-
'sourceMap': contractInput['srcmapRuntime']
120+
'sourceMap': contractInput['srcmapRuntime'],
121+
'linkReferences': linker.findLinkReferences(contractInput['runtimeBytecode'])
118122
},
119123
'methodIdentifiers': contractInput['functionHashes'],
120124
'gasEstimates': translatedGasEstimates

0 commit comments

Comments
 (0)