Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bytecode hash calculation #1414

Merged
merged 18 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
17f14c1
Fix bytecode hash calculation by updating variable names
marcocastignoli May 30, 2024
4bc675b
update temporary new tables FKs
marcocastignoli May 30, 2024
65c97dc
update tmp-tables db scripts
marcocastignoli May 31, 2024
c4976d7
prevent calling getTxReceipt twice during matchWithCreationTx
marcocastignoli Jun 4, 2024
c10568f
remove database migration to add code_new, contracts_new, compiled_co…
marcocastignoli Jun 4, 2024
8256b5b
implement sourcifyFixedDatabase as storageService
marcocastignoli Jun 4, 2024
b12ab64
Merge branch 'staging' into fix-inverted-code-hashes
marcocastignoli Jun 4, 2024
2c0de2a
Update database migration script
marcocastignoli Jun 5, 2024
8028121
Update packages/lib-sourcify/src/lib/SourcifyChain.ts
marcocastignoli Jun 5, 2024
7e00318
remove old comment "For now disable the creation tests"
marcocastignoli Jun 5, 2024
75ad15e
Merge branch 'staging' into fix-inverted-code-hashes
marcocastignoli Jun 6, 2024
c4cfdc0
add tests and fix compiler setting generation
marcocastignoli Jun 6, 2024
b451810
Merge branch 'staging' into fix-inverted-code-hashes
marcocastignoli Jun 6, 2024
c2ee725
Merge branch 'staging' into fix-inverted-code-hashes
marcocastignoli Jun 10, 2024
25b9c9f
Merge branch 'staging' into fix-inverted-code-hashes
marcocastignoli Jun 12, 2024
a0e3be3
Merge branch 'staging' into fix-inverted-code-hashes
marcocastignoli Jun 13, 2024
4fac145
Merge branch 'staging' into fix-inverted-code-hashes
marcocastignoli Jun 13, 2024
10111b9
Merge branch 'staging' into fix-inverted-code-hashes
marcocastignoli Jun 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions packages/lib-sourcify/src/lib/SourcifyChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,18 @@ export default class SourcifyChain {
);
};

getContractCreationBytecode = async (
getContractCreationBytecodeAndReceipt = async (
address: string,
transactionHash: string,
creatorTx?: TransactionResponse
): Promise<string> => {
): Promise<{
creationBytecode: string;
txReceipt: TransactionReceipt;
}> => {
const txReceipt = await this.getTxReceipt(transactionHash);
if (!creatorTx) creatorTx = await this.getTx(transactionHash);
let creationBytecode = '';

let creationBytecode;
// Non null txreceipt.contractAddress means that the contract was created with an EOA
if (txReceipt.contractAddress !== null) {
if (txReceipt.contractAddress !== address) {
Expand Down Expand Up @@ -416,6 +419,13 @@ export default class SourcifyChain {
}
}

return creationBytecode;
if (!creationBytecode || creationBytecode === '') {
marcocastignoli marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('Cannot get creation bytecode');
}

return {
creationBytecode,
txReceipt,
};
};
}
37 changes: 18 additions & 19 deletions packages/lib-sourcify/src/lib/verification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -533,36 +533,35 @@ export async function matchWithCreationTx(
return;
}

const creatorTx = await sourcifyChain.getTx(creatorTxHash);
let onchainCreationBytecode = '';
// Call rpc to find creatorTx, txReceipt and onchainContractCreationBytecode
// return null creationMatch if fail
let creatorTx;
try {
onchainCreationBytecode =
(await sourcifyChain.getContractCreationBytecode(
creatorTx = await sourcifyChain.getTx(creatorTxHash);
match.creatorTxHash = creatorTxHash;
match.blockNumber = creatorTx.blockNumber;
match.deployer = creatorTx.from;

const { creationBytecode, txReceipt } =
await sourcifyChain.getContractCreationBytecodeAndReceipt(
marcocastignoli marked this conversation as resolved.
Show resolved Hide resolved
address,
creatorTxHash,
creatorTx
)) || '';
);
match.onchainCreationBytecode = creationBytecode;
match.txIndex = txReceipt.index;
} catch (e: any) {
logWarn('Failed to fetch creation bytecode', {
address,
txHash: creatorTxHash,
chainId: sourcifyChain.chainId.toString(),
error: e,
});
match.creationMatch = null;
match.message = `Failed to match with creation bytecode: couldn't get the creation bytecode.`;
return;
}

// txIndex is available only in the receipt
const txReceipt = await sourcifyChain.getTxReceipt(creatorTxHash);
match.txIndex = txReceipt.index;

match.creatorTxHash = creatorTxHash;
match.blockNumber = creatorTx.blockNumber;
match.deployer = creatorTx.from;

match.onchainCreationBytecode = onchainCreationBytecode;

// Initialize the transformations array if undefined
if (match.creationTransformations === undefined) {
match.creationTransformations = [];
Expand All @@ -575,7 +574,7 @@ export async function matchWithCreationTx(
// Replace the library placeholders in the recompiled bytecode with values from the deployed bytecode
const { replaced, libraryMap } = addLibraryAddresses(
recompiledCreationBytecode,
onchainCreationBytecode,
match.onchainCreationBytecode,
match.creationTransformations
);
recompiledCreationBytecode = replaced;
Expand All @@ -589,7 +588,7 @@ export async function matchWithCreationTx(
}, {});
}

if (onchainCreationBytecode.startsWith(recompiledCreationBytecode)) {
if (match.onchainCreationBytecode.startsWith(recompiledCreationBytecode)) {
// if the bytecode doesn't end with metadata then "partial" match
if (endsWithMetadataHash(recompiledCreationBytecode)) {
match.creationMatch = 'perfect';
Expand Down Expand Up @@ -626,7 +625,7 @@ export async function matchWithCreationTx(
creationTransformationsValuesCborAuxdata,
} = normalizeBytecodesAuxdata(
recompiledCreationBytecode,
onchainCreationBytecode,
match.onchainCreationBytecode,
cborAuxdataPositions
)!;

Expand All @@ -648,7 +647,7 @@ export async function matchWithCreationTx(
if (match.creationMatch) {
const abiEncodedConstructorArguments =
extractAbiEncodedConstructorArguments(
onchainCreationBytecode,
match.onchainCreationBytecode,
recompiledCreationBytecode
);
const constructorAbiParamInputs = (
Expand Down
56 changes: 48 additions & 8 deletions services/database/scripts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ program
});

for (const contract of contractsSorted) {
await databasePool.query(
await executeQueryWithRetry(
databasePool,
`INSERT INTO sourcify_sync (chain_id, address, match_type, created_at) VALUES ($1, $2, $3, $4) ON CONFLICT (chain_id, address) DO NOTHING`,
[
contract.chainId,
Expand Down Expand Up @@ -151,7 +152,11 @@ program
"Path to repository v1 contracts folder (e.g. /Users/user/sourcify/repository/contracts)"
)
.option(
"-c, --chainsExceptions [items]",
"-c, --chains [items]",
"List of chains to sync separated by comma (e.g. 1,5,...)"
)
.option(
"-ce, --chainsExceptions [items]",
"List of chains exceptions separated by comma (e.g. 1,5,...)"
)
.option("-sf, --start-from [number]", "Start from a specific timestamp (ms)")
Expand All @@ -176,15 +181,24 @@ program
// Get chains from database
let chains = [];
const query = "SELECT DISTINCT chain_id FROM sourcify_sync";
const chainsResult = await databasePool.query(query);
const chainsResult = await executeQueryWithRetry(databasePool, query);
if (chainsResult.rowCount > 0) {
chains = chainsResult.rows.map(({ chain_id }) => chain_id);
}

// Specify which chain to sync
if (options.chains) {
chains = chains.filter((chain) =>
options.chains.split(",").includes(`${chain}`)
);
}

// Remove exceptions using --chainsException and 0
chains = chains.filter(
(chain) => !options.chainsExceptions?.split(",").includes(`${chain}`)
);
if (options.chainsExceptions) {
chains = chains.filter(
(chain) => !options.chainsExceptions.split(",").includes(`${chain}`)
);
}

let monitoring = {
totalSynced: 0,
Expand Down Expand Up @@ -315,7 +329,8 @@ const processContract = async (
if (request.status === 200) {
const response = await request.json();
if (response.result[0].status !== null) {
await databasePool.query(
await executeQueryWithRetry(
databasePool,
`
UPDATE sourcify_sync
SET
Expand Down Expand Up @@ -368,7 +383,7 @@ const fetchNextContract = async (databasePool, options, chainId) => {
ORDER BY id ASC
LIMIT 1
`;
const contractResult = await databasePool.query(query, [
const contractResult = await executeQueryWithRetry(databasePool, query, [
options.startFrom ? options.startFrom : 0,
chainId,
]);
Expand All @@ -388,4 +403,29 @@ function isNumber(value) {
return !isNaN(parseFloat(value)) && isFinite(value);
}

const executeQueryWithRetry = async (
databasePool,
query,
params,
maxRetries = 5,
delay = 5000
) => {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await databasePool.query(query, params);
return result;
} catch (error) {
if (error.code === "ECONNREFUSED" || error.code === "ETIMEDOUT") {
console.error(
`Database connection error. Retrying attempt ${attempt + 1}...`
);
await new Promise((resolve) => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
throw new Error("Max retries reached. Could not connect to the database.");
};

program.parse(process.argv);
30 changes: 30 additions & 0 deletions services/server/src/server/services/StorageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ interface StorageServiceOptions {
repositoryV1ServiceOptions: RepositoryV1ServiceOptions;
repositoryV2ServiceOptions: RepositoryV2ServiceOptions;
sourcifyDatabaseServiceOptions?: SourcifyDatabaseServiceOptions;
sourcifyFixedDatabaseServiceOptions?: SourcifyDatabaseServiceOptions;
allianceDatabaseServiceOptions?: AllianceDatabaseServiceOptions;
}

export class StorageService {
repositoryV1: RepositoryV1Service;
repositoryV2?: RepositoryV2Service;
sourcifyDatabase?: SourcifyDatabaseService;
sourcifyFixedDatabase?: SourcifyDatabaseService;
allianceDatabase?: AllianceDatabaseService;

constructor(options: StorageServiceOptions) {
Expand Down Expand Up @@ -86,6 +88,23 @@ export class StorageService {
);
}

// SourcifyFixedDatabase
if (
options.sourcifyFixedDatabaseServiceOptions?.postgres?.host &&
options.sourcifyFixedDatabaseServiceOptions?.postgres?.database &&
options.sourcifyFixedDatabaseServiceOptions?.postgres?.user &&
options.sourcifyFixedDatabaseServiceOptions?.postgres?.password
) {
this.sourcifyFixedDatabase = new SourcifyDatabaseService(
manuelwedler marked this conversation as resolved.
Show resolved Hide resolved
options.sourcifyFixedDatabaseServiceOptions
);
} else {
logger.warn(
"Won't use SourcifyFixedDatabase, options not complete",
options.sourcifyFixedDatabaseServiceOptions
);
}

// AllianceDatabase
if (
options.allianceDatabaseServiceOptions?.googleCloudSql ||
Expand Down Expand Up @@ -428,6 +447,17 @@ export class StorageService {
})
);

if (this.sourcifyFixedDatabase) {
promises.push(
this.sourcifyFixedDatabase.storeMatch(contract, match).catch((e) => {
logger.error("Error storing to SourcifyFixedDatabase: ", {
error: e,
});
throw e;
})
);
}

promises.push(
this.repositoryV2.storeMatch(contract, match).catch((e) => {
logger.error("Error storing to RepositoryV2: ", {
Expand Down
9 changes: 9 additions & 0 deletions services/server/src/server/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ export class Services {
port: parseInt(process.env.SOURCIFY_POSTGRES_PORT || "5432"),
},
},
sourcifyFixedDatabaseServiceOptions: {
postgres: {
host: process.env.SOURCIFY_FIXED_POSTGRES_HOST as string,
database: process.env.SOURCIFY_FIXED_POSTGRES_DB as string,
user: process.env.SOURCIFY_FIXED_POSTGRES_USER as string,
password: process.env.SOURCIFY_FIXED_POSTGRES_PASSWORD as string,
port: parseInt(process.env.SOURCIFY_FIXED_POSTGRES_PORT || "5432"),
},
},
allianceDatabaseServiceOptions: {
postgres: {
host: process.env.ALLIANCE_POSTGRES_HOST as string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,12 @@ export default abstract class AbstractDatabaseService {

return {
bytecodeHashes: {
recompiledCreation: bytesFromString(keccak256OnchainCreationBytecode),
recompiledRuntime: bytesFromString(keccak256OnchainRuntimeBytecode)!,
onchainCreation: bytesFromString(keccak256RecompiledCreationBytecode),
onchainRuntime: bytesFromString(keccak256RecompiledRuntimeBytecode)!,
recompiledCreation: bytesFromString(
keccak256RecompiledCreationBytecode
),
recompiledRuntime: bytesFromString(keccak256RecompiledRuntimeBytecode)!,
onchainCreation: bytesFromString(keccak256OnchainCreationBytecode),
onchainRuntime: bytesFromString(keccak256OnchainRuntimeBytecode)!,
},
compiledContract: {
language,
Expand Down
Loading