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

Improve the error messages #433

Merged
merged 3 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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 @@
ContractInstance,
ExecutableScript,
isHexString,
isDevnet
isDevnet,
TraceableError
} from '@alephium/web3'
import { PrivateKeyWallet } from '@alephium/web3-wallet'
import path from 'path'
Expand Down Expand Up @@ -173,7 +174,7 @@
}

getInstance<I extends ContractInstance>(
contract: ContractFactory<I, any>,

Check warning on line 177 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (20)

Unexpected any. Specify a different type
group?: number,
taskId?: string
): I | undefined {
Expand Down Expand Up @@ -224,7 +225,7 @@
return this.contracts.size === 0 && this.scripts.size === 0 && this.migrations.size === 0
}

marshal(): any {

Check warning on line 228 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (20)

Unexpected any. Specify a different type
return {
deployerAddress: this.deployerAddress,
contracts: Object.fromEntries(this.contracts),
Expand All @@ -233,7 +234,7 @@
}
}

static unmarshal(json: any): DeploymentsPerAddress {

Check warning on line 237 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (20)

Unexpected any. Specify a different type
const deployerAddress = json.deployerAddress as string
const contracts = new Map(Object.entries<DeployContractExecutionResult>(json.contracts))
const scripts = new Map(Object.entries<RunScriptResult>(json.scripts))
Expand Down Expand Up @@ -294,7 +295,7 @@
return true
}
// previous !== undefined if retry is false
return previous!.issueTokenAmount !== issueTokenAmount

Check warning on line 298 in packages/cli/src/deployment.ts

View workflow job for this annotation

GitHub Actions / build (20)

Forbidden non-null assertion
}

async function needToRunScript(
Expand Down Expand Up @@ -647,10 +648,10 @@
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 @@
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
Loading