# 错误处理

OpenChain SDK 提供了标准化的错误处理机制，包括错误码、错误描述以及处理建议。

## 错误处理基础

### 基本错误处理结构
```javascript
try {
  const response = yield sdk.account.getInfo(address);
  if (response.errorCode !== 0) {
    console.error('错误码:', response.errorCode);
    console.error('错误描述:', response.errorDesc);
    throw new Error(`操作失败: [${response.errorCode}] ${response.errorDesc}`);
  }
  return response.result;
} catch (error) {
  console.error('异常:', error.message);
  throw error;
}
```

### 标准响应格式
```javascript
// 成功响应
{
  errorCode: 0,
  errorDesc: '',
  result: {
    // 操作结果
  }
}

// 错误响应
{
  errorCode: 11002,
  errorDesc: '无效的源地址',
  result: null
}
```

## 错误码分类

### 账户相关错误 (11001-11006)
```javascript
const AccountErrors = {
  CREATE_ACCOUNT_ERROR: 11001,    // 创建账户失败
  INVALID_SOURCE_ADDRESS: 11002,  // 无效的源地址
  INVALID_DEST_ADDRESS: 11003,    // 无效的目标地址
  INVALID_INIT_BALANCE: 11004,    // 初始余额必须在1和int64最大值之间
  SAME_SOURCE_DEST: 11005,       // 源地址不能等于目标地址
  INVALID_ADDRESS: 11006         // 无效的地址格式
};
```

### 资产相关错误 (11008, 11023-11024)
```javascript
const AssetErrors = {
  INVALID_ISSUE_AMOUNT: 11008,   // 发行金额必须在1和int64最大值之间
  INVALID_ASSET_CODE: 11023,     // 无效的资产代码
  INVALID_AMOUNT: 11024         // 资产金额必须在1和int64最大值之间
};
```

### Token相关错误 (11030-11043)
```javascript
const TokenErrors = {
  TOKEN_NOT_EXIST: 11030,        // Token不存在
  INVALID_TOKEN_NAME: 11031,     // Token名称长度必须在1到1024之间
  INVALID_TOKEN_SYMBOL: 11032,   // Token符号长度必须在1到1024之间
  INVALID_TOKEN_DECIMALS: 11033, // Token精度必须在0到8之间
  INVALID_TOKEN_SUPPLY: 11034,   // Token总供应量必须在1和int64最大值之间
  INVALID_TOKEN_OWNER: 11035     // 无效的Token所有者
};
```

### 合约相关错误 (11044-11047)
```javascript
const ContractErrors = {
  EMPTY_PAYLOAD: 11044,          // 载荷不能为空
  INVALID_LOG_TOPIC: 11045,      // 日志主题长度必须在1到128之间
  INVALID_LOG_DATA: 11046,       // 日志数据长度必须在1到1024之间
  INVALID_CONTRACT_TYPE: 11047   // 无效的合约类型
};
```

### 交易相关错误 (11048-11058)
```javascript
const TransactionErrors = {
  INVALID_NONCE: 11048,          // Nonce必须在1和int64最大值之间
  INVALID_GAS_PRICE: 11049,      // GasPrice必须在1和int64最大值之间
  INVALID_FEE_LIMIT: 11050,      // FeeLimit必须在1和int64最大值之间
  EMPTY_OPERATIONS: 11051,       // 操作不能为空
  INVALID_SIGNATURE: 11054,      // 签名数量必须在1和int32最大值之间
  INVALID_HASH: 11055,          // 无效的交易哈希
  INVALID_BLOB: 11056,          // 无效的blob
  EMPTY_PRIVATE_KEY: 11057,     // 私钥不能为空
  INVALID_PRIVATE_KEY: 11058    // 私钥中存在无效值
};
```

### 网络错误
```javascript
const NetworkErrors = {
  NETWORK_ERROR: 11007,          // 网络连接失败
  CONNECT_BLOCKCHAIN: 19999,     // 连接区块链失败
  SYSTEM_ERROR: 20000           // 系统错误
};
```

## 错误处理最佳实践

### 1. 统一错误处理工具
```javascript
// 错误处理工具类
class ErrorHandler {
  static isSuccess(response) {
    return response && response.errorCode === 0;
  }

  static getErrorMessage(response) {
    if (!response) return '无响应';
    return `[${response.errorCode}] ${response.errorDesc}`;
  }

  static async handleResponse(promise) {
    try {
      const response = await promise;
      if (!this.isSuccess(response)) {
        throw new Error(this.getErrorMessage(response));
      }
      return response.result;
    } catch (error) {
      console.error('操作失败:', error);
      throw error;
    }
  }
}

// 使用示例
async function getAccountInfo(address) {
  return ErrorHandler.handleResponse(
    sdk.account.getInfo(address)
  );
}
```

### 2. 重试机制
```javascript
// 带重试的操作执行器
async function retryOperation(operation, maxRetries = 3, delay = 1000) {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await operation();
      
      if (response.errorCode === 0) {
        return response.result;
      }
      
      // 对特定错误进行重试
      if ([11007, 19999].includes(response.errorCode)) {
        lastError = new Error(response.errorDesc);
        await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
        continue;
      }
      
      throw new Error(`[${response.errorCode}] ${response.errorDesc}`);
    } catch (error) {
      lastError = error;
      
      // 网络错误重试
      if (error.message.includes('network')) {
        await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
        continue;
      }
      
      throw error;
    }
  }
  
  throw lastError;
}

// 使用示例
async function safeGetAccountInfo(address) {
  return retryOperation(() => sdk.account.getInfo(address));
}
```

### 3. 错误分类处理
```javascript
// 错误分类处理器
class ErrorClassifier {
  static classify(errorCode) {
    if (errorCode >= 11001 && errorCode <= 11006) {
      return 'ACCOUNT_ERROR';
    }
    if (errorCode >= 11030 && errorCode <= 11043) {
      return 'TOKEN_ERROR';
    }
    if (errorCode >= 11048 && errorCode <= 11058) {
      return 'TRANSACTION_ERROR';
    }
    if ([11007, 19999].includes(errorCode)) {
      return 'NETWORK_ERROR';
    }
    return 'UNKNOWN_ERROR';
  }

  static handle(error) {
    const errorCode = error.code || parseInt(error.message.match(/\[(\d+)\]/)?.[1]);
    const errorType = this.classify(errorCode);

    switch (errorType) {
      case 'ACCOUNT_ERROR':
        // 处理账户相关错误
        console.error('账户操作错误:', error.message);
        break;
      case 'TOKEN_ERROR':
        // 处理Token相关错误
        console.error('Token操作错误:', error.message);
        break;
      case 'TRANSACTION_ERROR':
        // 处理交易相关错误
        console.error('交易操作错误:', error.message);
        break;
      case 'NETWORK_ERROR':
        // 处理网络相关错误
        console.error('网络错误:', error.message);
        break;
      default:
        // 处理未知错误
        console.error('未知错误:', error.message);
    }
  }
}
```

### 4. 日志记录
```javascript
// 错误日志记录器
class ErrorLogger {
  static log(error, context = {}) {
    const errorInfo = {
      timestamp: new Date().toISOString(),
      errorCode: error.code,
      errorMessage: error.message,
      stack: error.stack,
      context: context
    };

    console.error('错误日志:', JSON.stringify(errorInfo, null, 2));
    
    // 可以添加将日志保存到文件或发送到日志服务器的逻辑
  }

  static async logOperation(operation, context) {
    try {
      return await operation();
    } catch (error) {
      this.log(error, context);
      throw error;
    }
  }
}

// 使用示例
async function transferAsset(params) {
  return ErrorLogger.logOperation(
    () => sdk.operation.asset.pay(params),
    { operation: 'transferAsset', params }
  );
}
```

### 5. 错误恢复策略
```javascript
// 错误恢复策略
class ErrorRecoveryStrategy {
  static async recover(error, operation, context) {
    const errorType = ErrorClassifier.classify(error.code);
    
    switch (errorType) {
      case 'NETWORK_ERROR':
        // 网络错误恢复策略
        return await this.handleNetworkError(operation);
        
      case 'TRANSACTION_ERROR':
        // 交易错误恢复策略
        return await this.handleTransactionError(operation, context);
        
      default:
        throw error;
    }
  }

  static async handleNetworkError(operation) {
    return retryOperation(operation);
  }

  static async handleTransactionError(operation, context) {
    // 检查nonce是否正确
    if (context.sourceAddress) {
      const accountInfo = await sdk.account.getInfo(context.sourceAddress);
      context.nonce = accountInfo.result.nonce;
    }
    return operation(context);
  }
}

// 使用示例
async function safeOperation(operation, context) {
  try {
    return await operation(context);
  } catch (error) {
    return await ErrorRecoveryStrategy.recover(error, operation, context);
  }
}
```