import { Program, RawTransaction, SignedTransaction, SignedTransactionWithProof, } from '../__generated__/transaction_pb'; import { CursorBuffer } from '../common/CursorBuffer'; import PathValues from '../constants/PathValues'; import { UnichainDeserializationError, UnichainExecutionError, UnichainExecutionErrorType, UnichainGasConstraint, UnichainInvariantViolationError, UnichainProgram, UnichainProgramArgumentType, UnichainSignedTransaction, UnichainSignedTransactionWithProof, UnichainTransaction, UnichainTransactionEvent, UnichainValidationStatusCode, UnichainValidationStatusError, UnichainVerificationError, UnichainVerificationStatusError, UnichainVerificationStatusKind, UnichainVMStatusError, } from '..'; import { EventsList } from '../__generated__/events_pb'; import { AccountAddress, AccountState } from '../wallet/Accounts'; import { AccessPath } from '../__generated__/access_path_pb'; import BigNumber from 'bignumber.js'; import { BinaryError, ExecutionStatus, VMInvariantViolationError, VMStatus, VMValidationStatus, VMVerificationStatusList, } from '../__generated__/vm_errors_pb'; /** * Internal class used by UnichainClient * to decode pb generated classes to Unichain* Classes export by this library * */ export class ClientDecoder { public decodeAccountStateBlob(blob: Uint8Array): AccountState { const cursor = new CursorBuffer(blob); const blobLen = cursor.read32(); const state: { [key: string]: Uint8Array } = {}; for (let i = 0; i < blobLen; i++) { const keyLen = cursor.read32(); const keyBuffer = new Uint8Array(keyLen); for (let j = 0; j < keyLen; j++) { keyBuffer[j] = cursor.read8(); } const valueLen = cursor.read32(); const valueBuffer = new Uint8Array(valueLen); for (let k = 0; k < valueLen; k++) { valueBuffer[k] = cursor.read8(); } state[Buffer.from(keyBuffer).toString('hex')] = valueBuffer; } return AccountState.fromBytes(state[PathValues.AccountStatePath]); } public decodeSignedTransactionWithProof( signedTransactionWP: SignedTransactionWithProof, ): UnichainSignedTransactionWithProof { // decode transaction const signedTransaction = signedTransactionWP.getSignedTransaction() as SignedTransaction; const unichainTransaction = this.decodeRawTransactionBytes(signedTransaction.getRawTxnBytes_asU8()); const unichainSignedtransaction = new UnichainSignedTransaction( unichainTransaction, signedTransaction.getSenderPublicKey_asU8(), signedTransaction.getSenderSignature_asU8(), ); // decode event let eventsList: UnichainTransactionEvent[] | undefined; if (signedTransactionWP.hasEvents()) { const events = signedTransactionWP.getEvents() as EventsList; eventsList = events.getEventsList().map(event => { let address: AccountAddress | undefined; let path: Uint8Array | undefined; if (event.hasAccessPath()) { const accessPath = event.getAccessPath() as AccessPath; address = new AccountAddress(accessPath.getAddress_asU8()); path = accessPath.getPath_asU8(); } return new UnichainTransactionEvent( event.getEventData_asU8(), new BigNumber(event.getSequenceNumber()), address, path, ); }); } return new UnichainSignedTransactionWithProof(unichainSignedtransaction, signedTransactionWP.getProof(), eventsList); } public decodeRawTransactionBytes(rawTxnBytes: Uint8Array): UnichainTransaction { const rawTxn = RawTransaction.deserializeBinary(rawTxnBytes); const rawProgram = rawTxn.getProgram() as Program; const program: UnichainProgram = { arguments: rawProgram.getArgumentsList().map(argument => ({ type: (argument.getType() as unknown) as UnichainProgramArgumentType, value: argument.getData_asU8(), })), code: rawProgram.getCode_asU8(), modules: rawProgram.getModulesList_asU8(), }; const gasContraint: UnichainGasConstraint = { gasUnitPrice: new BigNumber(rawTxn.getGasUnitPrice()), maxGasAmount: new BigNumber(rawTxn.getMaxGasAmount()), }; return new UnichainTransaction( program, gasContraint, new BigNumber(rawTxn.getExpirationTime()), rawTxn.getSenderAccount_asU8(), new BigNumber(rawTxn.getSequenceNumber()), ); } public decodeVMStatus(vmStatus?: VMStatus): UnichainVMStatusError | undefined { if (vmStatus === undefined) { return undefined; } let validationStatus: UnichainValidationStatusError | undefined; let verificationStatusErrors: UnichainVerificationStatusError[] | undefined; let invariantViolationError: UnichainInvariantViolationError | undefined; let deserializationError: UnichainDeserializationError | undefined; let executionError: UnichainExecutionError | undefined; if (vmStatus.hasValidation()) { const validation = vmStatus.getValidation() as VMValidationStatus; validationStatus = { code: (validation.getCode() as unknown) as UnichainValidationStatusCode, message: validation.getMessage(), }; } if (vmStatus.hasVerification()) { const verification = vmStatus.getVerification() as VMVerificationStatusList; verificationStatusErrors = verification.getStatusListList().map(status => { return new UnichainVerificationStatusError( (status.getErrorKind() as unknown) as UnichainVerificationStatusKind, status.getModuleIdx(), (status.getErrorKind() as unknown) as UnichainVerificationError, status.getMessage(), ); }); } if (vmStatus.hasInvariantViolation()) { const invariant = vmStatus.getInvariantViolation() as VMInvariantViolationError; invariantViolationError = (invariant as unknown) as UnichainInvariantViolationError; } if (vmStatus.hasDeserialization()) { const deser = vmStatus.getDeserialization() as BinaryError; deserializationError = (deser as unknown) as UnichainDeserializationError; } if (vmStatus.hasExecution()) { const execution = vmStatus.getExecution() as ExecutionStatus; executionError = { errorType: (execution.getExecutionStatusCase() as unknown) as UnichainExecutionErrorType, }; } return new UnichainVMStatusError( vmStatus.getErrorTypeCase(), validationStatus, verificationStatusErrors, invariantViolationError, deserializationError, executionError, ); } }