diff --git a/package.json b/package.json index e1ecd9b3f..967697468 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,8 @@ "@babel/traverse": ">=7.23.2", "browserify-sign": ">=4.2.2", "axios": ">=1.6.0", - "word-wrap": ">=1.2.4" + "word-wrap": ">=1.2.4", + "braces@<3.0.3": ">=3.0.3" } } } diff --git a/packages/cli/src/project.ts b/packages/cli/src/project.ts index 7d0c9a049..e2a1d7772 100644 --- a/packages/cli/src/project.ts +++ b/packages/cli/src/project.ts @@ -436,6 +436,24 @@ export class Project { return forceRecompile || changedSources.includes(name) } + private async checkMethodIndex(newArtifact: Compiled) { + const artifactPath = newArtifact.sourceInfo.getArtifactPath(this.artifactsRootDir) + let oldArtifact: Contract + try { + oldArtifact = await Contract.fromArtifactFile(artifactPath, '', '') + } catch (error) { + throw new Error(`Failed to load contract artifact, error: ${error}, contract: ${newArtifact.sourceInfo.name}`) + } + newArtifact.artifact.functions.forEach((newFuncSig, index) => { + const oldFuncSig = oldArtifact.functions[`${index}`] + if (oldFuncSig.name !== newFuncSig.name) { + throw new Error( + `The newly compiled contract ${newArtifact.artifact.name} has different method indexes compared to the existing deployment on mainnet/testnet` + ) + } + }) + } + private async saveArtifactsToFile( projectRootDir: string, forceRecompile: boolean, @@ -453,6 +471,8 @@ export class Project { for (const [_, contract] of this.contracts) { if (Project.needToUpdate(forceRecompile, changedSources, contract.sourceInfo.name)) { await saveToFile(contract) + } else { + await this.checkMethodIndex(contract) } } for (const [_, script] of this.scripts) { diff --git a/packages/web3/src/address/address.test.ts b/packages/web3/src/address/address.test.ts index 359feacfc..eb8a06170 100644 --- a/packages/web3/src/address/address.test.ts +++ b/packages/web3/src/address/address.test.ts @@ -47,6 +47,12 @@ describe('address', function () { expect(validateAddress('yya86C6UemCeLs5Ztwjcf2Mp2Kkt4mwzzRpBiG6qQ9kj')).toBeUndefined() expect(() => validateAddress('yya86C6UemCeLs5Ztwjcf2Mp2Kkt4mwzzRpBiG6qQ9k')).toThrow('Invalid address:') expect(() => validateAddress('asd')).toThrow('Invalid multisig address: asd') + expect(() => validateAddress('asdasdf')).toThrow('Invalid multisig address') + expect(() => + validateAddress('2jW1n2icPtc55Cdm8TF9FjGH681cWthsaZW3gaUFekFZepJoeyY3ZbY7y5SCtAjyCjLL24c4L2Vnfv3KDdAypCddfAY1') + ).toThrow('Invalid address:') + // both n and m are 0 + expect(() => validateAddress('LUw')).toThrow('Invalid multisig address') expect(() => validateAddress('2jVWAcAPphJ8ueZNG1BPwbfPFjjbvorprceuqzgmJQ1ZRyELRpWgARvdB3T9trqpiJs7f4GkudPt6rQLnGbQYqq2NCi') ).toThrow('Invalid multisig address, n: 2, m: 3') diff --git a/packages/web3/src/address/address.ts b/packages/web3/src/address/address.ts index d7070693a..a9837d711 100644 --- a/packages/web3/src/address/address.ts +++ b/packages/web3/src/address/address.ts @@ -28,6 +28,7 @@ import { MultiSig, lockupScriptCodec } from '../codec/lockup-script-codec' import { compactSignedIntCodec } from '../codec' const ec = new EC('secp256k1') +const PublicKeyHashSize = 32 export enum AddressType { P2PKH = 0x00, @@ -59,10 +60,13 @@ function decodeAndValidateAddress(address: string): Uint8Array { } const n = multisig.publicKeyHashes.value.length const m = compactSignedIntCodec.toI32(multisig.m) - if (n < m) { + if (n < m || m <= 0) { throw new Error(`Invalid multisig address, n: ${n}, m: ${m}`) } - return decoded + const encodedNSize = compactSignedIntCodec.encodeI32(n).length + const encodedMSize = multisig.m.rest.length + 1 + const size = encodedNSize + PublicKeyHashSize * n + encodedMSize + 1 // 1 for the P2MPKH prefix + if (decoded.length === size) return decoded } else if (addressType === AddressType.P2PKH || addressType === AddressType.P2SH || addressType === AddressType.P2C) { // [type, ...hash] if (decoded.length === 33) return decoded diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7bff9e5f7..012bc9839 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,7 @@ overrides: browserify-sign: '>=4.2.2' axios: '>=1.6.0' word-wrap: '>=1.2.4' + braces@<3.0.3: '>=3.0.3' importers: @@ -4787,11 +4788,11 @@ packages: balanced-match: 1.0.2 dev: true - /braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + /braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} dependencies: - fill-range: 7.0.1 + fill-range: 7.1.1 /brorand@1.1.0: resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} @@ -4993,7 +4994,7 @@ packages: engines: {node: '>= 8.10.0'} dependencies: anymatch: 3.1.3 - braces: 3.0.2 + braces: 3.0.3 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -6603,8 +6604,8 @@ packages: flat-cache: 3.0.4 dev: true - /fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + /fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 @@ -8673,7 +8674,7 @@ packages: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} dependencies: - braces: 3.0.2 + braces: 3.0.3 picomatch: 2.3.1 /miller-rabin@4.0.1: