Skip to content

Commit

Permalink
Merge pull request #433 from alephium/issue-429
Browse files Browse the repository at this point in the history
Improve the error messages
  • Loading branch information
polarker authored Oct 17, 2024
2 parents a9fcb67 + 300aed9 commit 02a776e
Show file tree
Hide file tree
Showing 19 changed files with 118 additions and 76 deletions.
8 changes: 5 additions & 3 deletions packages/cli/scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
import { Configuration } from '../src/types'
import { deploy, Deployments } from '../src/deployment'
import { getDeploymentFilePath } from '../src'
import { NetworkId } from '@alephium/web3'
import { NetworkId, TraceableError } from '@alephium/web3'

export async function deployAndSaveProgress<Settings = unknown>(
configuration: Configuration<Settings>,
Expand All @@ -35,8 +35,10 @@ export async function deployAndSaveProgress<Settings = unknown>(
scriptExecuted = await deploy(configuration, networkId, deployments, fromIndex, toIndex, silent)
} catch (error) {
await deployments.saveToFile(deploymentsFile, configuration, false)
console.error(`Failed to deploy the project`)
throw error
if (configuration.enableDebugMode) {
console.log(`Failed to deploy the project, error: `, error)
}
throw new TraceableError('Failed to deploy the project', error)
}

await deployments.saveToFile(deploymentsFile, configuration, true)
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ import {
ContractInstance,
ExecutableScript,
isHexString,
isDevnet
isDevnet,
TraceableError
} from '@alephium/web3'
import { PrivateKeyWallet } from '@alephium/web3-wallet'
import path from 'path'
Expand Down Expand Up @@ -647,10 +648,10 @@ export async function deploy<Settings = unknown>(
func: content.default as DeployFunction<Settings>
})
} else {
throw new Error(`no default deploy function exported from ${scriptFilePath}`)
throw new Error(`No default deploy function exported from ${scriptFilePath}`)
}
} catch (error) {
throw new Error(`failed to load deploy script, filepath: ${scriptFilePath}, error: ${error}`)
throw new TraceableError(`Failed to load deploy script, filepath: ${scriptFilePath}`, error)
}
}

Expand Down Expand Up @@ -789,7 +790,7 @@ async function deployToGroup<Settings = unknown>(
deployments.migrations.set(script.func.id, Date.now())
}
} catch (error) {
throw new Error(`failed to execute deploy script, filepath: ${script.scriptFilePath}, error: ${error}`)
throw new TraceableError(`Failed to execute deploy script, filepath: ${script.scriptFilePath}`, error)
}
}
}
Expand Down
12 changes: 7 additions & 5 deletions packages/cli/src/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import {
WebCrypto,
CompilerOptions,
Constant,
Enum
Enum,
TraceableError
} from '@alephium/web3'
import * as path from 'path'
import fs from 'fs'
Expand Down Expand Up @@ -484,7 +485,7 @@ export class Project {
try {
oldArtifact = await Contract.fromArtifactFile(artifactPath, '', '')
} catch (error) {
throw new Error(`Failed to load contract artifact, error: ${error}, contract: ${newArtifact.sourceInfo.name}`)
throw new TraceableError(`Failed to load contract artifact, contract: ${newArtifact.sourceInfo.name}`, error)
}
newArtifact.artifact.functions.forEach((newFuncSig, index) => {
const oldFuncSig = oldArtifact.functions[`${index}`]
Expand Down Expand Up @@ -566,18 +567,19 @@ export class Project {
compilerOptions: compilerOptions
})
} catch (error) {
const traceableError = new TraceableError('Failed to compile the project', error)
if (!(error instanceof Error)) {
throw error
throw traceableError
}

const parsed = parseError(error.message)
if (!parsed) {
throw error
throw traceableError
}

const sourceInfo = findSourceInfoAtLineNumber(sources, parsed.lineStart)
if (!sourceInfo) {
throw error
throw traceableError
}

const shiftIndex = parsed.lineStart - sourceInfo.startIndex + 1
Expand Down
8 changes: 3 additions & 5 deletions packages/walletconnect/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ import {
networkIds,
EnableOptionsBase,
SignChainedTxParams,
SignChainedTxResult
SignChainedTxResult,
TraceableError
} from '@alephium/web3'

import { ALEPHIUM_DEEP_LINK, LOGGER, PROVIDER_NAMESPACE, RELAY_METHODS, RELAY_URL } from './constants'
Expand Down Expand Up @@ -436,10 +437,7 @@ export class WalletConnectProvider extends SignerProvider {
await this.cleanMessages()
return response
} catch (error: any) {
if (error.message) {
throw new Error(error.message)
}
throw error
throw new TraceableError(`Failed to request ${args.method}`, error)
}
}

Expand Down
9 changes: 5 additions & 4 deletions packages/web3-test/src/test-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import {
DUST_AMOUNT,
Address,
TOTAL_NUMBER_OF_GROUPS,
binToHex
binToHex,
TraceableError
} from '@alephium/web3'
import { NodeWallet, PrivateKeyWallet } from '@alephium/web3-wallet'
import { randomBytes } from 'crypto'
Expand Down Expand Up @@ -101,7 +102,7 @@ export async function getSigner(alphAmount = ONE_ALPH * 100n, group = 0): Promis
}
return wallet
} catch (error) {
throw new Error(`Failed to get signer, please restart the devnet: ${error}`)
throw new TraceableError(`Failed to get signer, please restart the devnet`, error)
}
}

Expand All @@ -119,8 +120,8 @@ export async function getSigners(
wallets.push(wallet)
}
return wallets
} catch (_) {
throw new Error('Failed to get signers, please restart the devnet')
} catch (error) {
throw new TraceableError('Failed to get signers, please restart the devnet', error)
}
}

Expand Down
18 changes: 6 additions & 12 deletions packages/web3/src/address/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import { ec as EC } from 'elliptic'
import BN from 'bn.js'
import { TOTAL_NUMBER_OF_GROUPS } from '../constants'
import blake from 'blakejs'
import bs58 from '../utils/bs58'
import bs58, { base58ToBytes } from '../utils/bs58'
import { binToHex, concatBytes, hexToBinUnsafe, isHexString, xorByte } from '../utils'
import { KeyType } from '../signer'
import { P2MPKH, lockupScriptCodec } from '../codec/lockup-script-codec'
import { i32Codec } from '../codec'
import { LockupScript } from '../codec/lockup-script-codec'
import djb2 from '../utils/djb2'
import { TraceableError } from '../error'

const ec = new EC('secp256k1')
const PublicKeyHashSize = 32
Expand All @@ -52,21 +53,15 @@ export function isValidAddress(address: string): boolean {
}

function decodeAndValidateAddress(address: string): Uint8Array {
let decoded: Uint8Array
try {
decoded = bs58.decode(address)
} catch (_) {
throw new Error('Invalid base58 string')
}

const decoded = base58ToBytes(address)
if (decoded.length === 0) throw new Error('Address is empty')
const addressType = decoded[0]
if (addressType === AddressType.P2MPKH) {
let multisig: P2MPKH
try {
multisig = lockupScriptCodec.decode(decoded).value as P2MPKH
} catch (_) {
throw new Error(`Invalid multisig address: ${address}`)
} catch (error) {
throw new TraceableError(`Invalid multisig address: ${address}`, error)
}
const n = multisig.publicKeyHashes.length
const m = multisig.m
Expand Down Expand Up @@ -137,8 +132,7 @@ export function tokenIdFromAddress(address: string): Uint8Array {
}

function idFromAddress(address: string): Uint8Array {
const decoded = bs58.decode(address)

const decoded = base58ToBytes(address)
if (decoded.length == 0) throw new Error('Address string is empty')
const addressType = decoded[0]
const addressBody = decoded.slice(1)
Expand Down
27 changes: 11 additions & 16 deletions packages/web3/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.

import { ZERO_ADDRESS } from '../constants'
import { isDebugModeEnabled } from '../debug'
import { assertType, binToHex, bs58, Eq, isBase58, isHexString } from '../utils'
import { TraceableError } from '../error'
import { assertType, base58ToBytes, binToHex, bs58, Eq, isBase58, isHexString } from '../utils'
import * as node from './api-alephium'

export type Number256 = bigint | string
Expand Down Expand Up @@ -68,8 +69,8 @@ export function toApiNumber256(v: Val): string {
if (BigInt(v).toString() === v) {
return v
}
} catch (_) {
throw new Error(`Invalid value: ${v}, expected a 256 bit number`)
} catch (error) {
throw new TraceableError(`Invalid value: ${v}, expected a 256 bit number`, error)
}
}
throw new Error(`Invalid value: ${v}, expected a 256 bit number`)
Expand All @@ -90,26 +91,20 @@ export function toApiByteVec(v: Val): string {
if (isHexString(v)) return v
if (isBase58(v)) {
// try to convert from address to contract id
try {
const address = bs58.decode(v)
if (address.length == 33 && address[0] == 3) {
return binToHex(address.slice(1))
}
} catch (_) {
throw new Error(`Invalid hex-string: ${v}`)
const address = base58ToBytes(v)
if (address.length === 33 && address[0] === 3) {
return binToHex(address.slice(1))
}
}
throw new Error(`Invalid hex-string: ${v}`)
}

export function toApiAddress(v: Val): string {
if (typeof v === 'string') {
try {
bs58.decode(v)
return v as string
} catch (error) {
throw new Error(`Invalid base58 string: ${v}`)
if (isBase58(v)) {
return v
}
throw new Error(`Invalid base58 string: ${v}`)
} else {
throw new Error(`Invalid value: ${v}, expected a base58 string`)
}
Expand Down Expand Up @@ -201,7 +196,7 @@ async function call(args: ApiRequestArguments, handler: ApiRequestHandler): Prom
if (debugModeEnabled) {
console.error(`[ERROR] ${path} ${method} `, error)
}
throw error
throw new TraceableError(`Failed to request ${method}`, error)
}
}

Expand Down
8 changes: 6 additions & 2 deletions packages/web3/src/api/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

import { ONE_ALPH } from '../constants'
import { TraceableError } from '../error'
import { NodeProvider, isBalanceEqual, node } from './index'

describe('utils', function () {
it('should throw API error', async () => {
const provider = new NodeProvider('http://127.0.0.1:22973')
await expect(provider.addresses.getAddressesAddressGroup('000')).rejects.toThrowError(
new Error(
'[API Error] - Invalid value for: path parameter address (Unable to decode address from 000: 000) - Status code: 400'
new TraceableError(
'Failed to request getAddressesAddressGroup',
new Error(
'[API Error] - Invalid value for: path parameter address (Unable to decode address from 000: 000) - Status code: 400'
)
)
)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/web3/src/codec/transaction-codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { FixedAssetOutput, Transaction as ApiTransaction } from '../api/api-alep
import { binToHex, hexToBinUnsafe } from '../utils'
import { ContractOutput as ApiContractOutput } from '../api/api-alephium'
import { byteCodec, ObjectCodec } from './codec'
import { Output, outputCodec, outputsCodec } from './output-codec'
import { Output, outputsCodec } from './output-codec'

export interface Transaction {
unsigned: UnsignedTx
Expand Down
12 changes: 8 additions & 4 deletions packages/web3/src/contract/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import {
i32Codec,
BytesConst
} from '../codec'
import { TraceableError } from '../error'

const crypto = new WebCrypto()

Expand Down Expand Up @@ -602,7 +603,7 @@ export class Contract extends Artifact {
: this.bytecode
return ralph.buildContractByteCode(bytecode, initialFields, this.fieldsSig, this.structs)
} catch (error) {
throw new Error(`Failed to build bytecode for contract ${this.name}, error: ${error}`)
throw new TraceableError(`Failed to build bytecode for contract ${this.name}`, error)
}
}

Expand Down Expand Up @@ -770,7 +771,7 @@ export class Script extends Artifact {
try {
return ralph.buildScriptByteCode(this.bytecodeTemplate, initialFields, this.fieldsSig, this.structs)
} catch (error) {
throw new Error(`Failed to build bytecode for script ${this.name}, error: ${error}`)
throw new TraceableError(`Failed to build bytecode for script ${this.name}`, error)
}
}
}
Expand Down Expand Up @@ -1546,7 +1547,10 @@ export async function getMapItem<R extends Val>(
// the map item contract does not exist
return undefined
}
throw error
throw new TraceableError(
`Failed to get value from map ${mapName}, key: ${key}, parent contract id: ${parentContractId}`,
error
)
}
}

Expand Down Expand Up @@ -2097,7 +2101,7 @@ export async function getContractEventsCurrentCount(contractAddress: Address): P
if (error instanceof Error && error.message.includes(`${contractAddress} not found`)) {
return 0
}
throw error
throw new TraceableError(`Failed to get the event count for the contract ${contractAddress}`, error)
})
}

Expand Down
6 changes: 2 additions & 4 deletions packages/web3/src/contract/ralph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
U256Const5
} from '../codec'
import { boolCodec } from '../codec/codec'
import { TraceableError } from '../error'

export function encodeByteVec(hex: string): Uint8Array {
if (!isHexString(hex)) {
Expand Down Expand Up @@ -423,10 +424,7 @@ function _encodeField<T>(fieldName: string, encodeFunc: () => T): T {
try {
return encodeFunc()
} catch (error) {
if (error instanceof Error) {
throw new Error(`Invalid ${fieldName}, error: ${error.message}`)
}
throw error
throw new TraceableError(`Failed to encode the field ${fieldName}`, error)
}
}

Expand Down
37 changes: 37 additions & 0 deletions packages/web3/src/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Copyright 2018 - 2022 The Alephium Authors
This file is part of the alephium project.
The library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

export class TraceableError extends Error {
trace: any | undefined

constructor(message?: string, innerError?: any) {
const innerErrorMessage =
innerError === undefined ? undefined : innerError instanceof Error ? innerError.message : `${innerError}`
super(innerErrorMessage ? `${message}, error: ${innerErrorMessage}` : message)
this.trace = innerError

const actualProto = new.target.prototype

if (Object.setPrototypeOf) {
Object.setPrototypeOf(this, actualProto)
} else {
const object = this as any
object.__proto__ = actualProto
}
}
}
Loading

0 comments on commit 02a776e

Please sign in to comment.