UNPKG

@naladelponce/hf-web-client

Version:

Un cliente TypeScript moderno y robusto para interactuar con Hyperledger Fabric desde entornos web y Node.js.

800 lines (787 loc) 46.2 kB
import { startRegistration, startAuthentication } from '@simplewebauthn/browser'; import { set, get } from 'idb-keyval'; import { serviceDesc, messageDesc, fileDesc } from '@bufbuild/protobuf/codegenv2'; import { TimestampSchema, file_google_protobuf_timestamp } from '@bufbuild/protobuf/wkt'; import { createClient, ConnectError } from '@connectrpc/connect'; import { createGrpcWebTransport } from '@connectrpc/connect-web'; import { toBinary, fromBinary, create, protoInt64 } from '@bufbuild/protobuf'; import BN from 'bn.js'; import { sha256 } from '@noble/hashes/sha2'; import { createRequire } from 'module'; // src/identity/identity-service.ts function uint8ArrayToBase64Url(array) { return btoa(String.fromCharCode.apply(null, Array.from(array))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } var IdentityService = class { worker; constructor() { this.worker = new Worker(new URL("./crypto-worker.js", import.meta.url), { type: "module" }); } request(action, payload, engineType) { return new Promise((resolve) => { const handleResponse = (event) => { this.worker.removeEventListener("message", handleResponse); resolve(event.data); }; this.worker.addEventListener("message", handleResponse); this.worker.postMessage({ action, payload, engineType }); }); } /** * Constructs the active AppIdentity object. * @param cert The user's certificate PEM. * @returns An AppIdentity object with a live `sign` method. */ buildActiveIdentity(cert) { const serviceInstance = this; return { cert, // We create the sign method using an arrow function to capture `serviceInstance`. // This is the core of the magic. When the user calls `identity.sign()`, // it's actually calling back into this service instance. sign: async (dataToSign) => { const signResult = await serviceInstance.request( "SIGN_PAYLOAD" /* SignPayload */, dataToSign, // The engineType here doesn't matter for signing, as the key is already // unlocked. But we'll just pick one to satisfy the contract. "password-base" ); if (!signResult.success) { throw signResult.error; } return signResult.data; } }; } doesHardwareIdentityExist() { return this.request("DOES_IDENTITY_EXIST" /* DoesIdentityExist */, null, "hardware-base"); } doesPasswordIdentityExist() { return this.request("DOES_IDENTITY_EXIST" /* DoesIdentityExist */, null, "password-base"); } async createPasswordIdentity(options) { const result = await this.request( "CREATE_IDENTITY" /* CreateIdentity */, options, "password-base" ); if (!result.success) { return result; } const activeIdentity = this.buildActiveIdentity(result.data.cert); return { success: true, data: { ...activeIdentity, phrase: result.data.phrase, recoveryShares: result.data.recoveryShares }, error: null }; } async createHardwareIdentity(options) { const cryptoResult = await this.request( "CREATE_IDENTITY_HW_CRYPTO" /* CreateHardwareIdentityCrypto */, options, "hardware-base" ); if (!cryptoResult.success) { return cryptoResult; } try { const rpName = "Fabric Client App"; const rpID = window.location.hostname; const registrationOptions = { rp: { name: rpName, id: rpID }, user: { id: uint8ArrayToBase64Url( new TextEncoder().encode(`user-${Date.now()}`) ), name: `user@${rpID}`, displayName: "Fabric User" }, challenge: uint8ArrayToBase64Url( window.crypto.getRandomValues(new Uint8Array(32)) ), pubKeyCredParams: [{ alg: -7, type: "public-key" }], // ES256 authenticatorSelection: { residentKey: "required", userVerification: "required", authenticatorAttachment: "platform" }, attestation: "none" }; const attestation = await startRegistration({ optionsJSON: registrationOptions }); await set("hw-fabric-credential-id", attestation.id); return cryptoResult; } catch (error) { return { success: false, data: null, error: new Error(`WebAuthn registration failed: ${error.message}`) }; } } async unlockIdentity(options, mode) { if (mode === "hardware-base") { const bioResult = await this.verifyBiometrics(); if (!bioResult.success) { return { success: false, data: null, error: bioResult.error }; } } const result = await this.request( "UNLOCK_IDENTITY" /* UnlockIdentity */, options, mode ); if (!result.success) { return result; } const activeIdentity = this.buildActiveIdentity(result.data.cert); return { success: true, data: activeIdentity, error: null }; } async verifyBiometrics() { try { const rpID = window.location.hostname; const credentialId = await get("hw-fabric-credential-id"); if (!credentialId) throw new Error("No hardware credential ID found in storage."); const authOptions = { challenge: uint8ArrayToBase64Url( window.crypto.getRandomValues(new Uint8Array(16)) ), allowCredentials: [{ id: credentialId, type: "public-key" }], userVerification: "required", rpId: rpID }; await startAuthentication({ optionsJSON: authOptions }); return { success: true, data: true, error: null }; } catch (error) { return { success: false, data: null, error: new Error(`Biometric verification failed: ${error.message}`) }; } } deleteIdentity(mode) { return this.request("DELETE_IDENTITY" /* DeleteIdentity */, null, mode); } }; var file_peer_chaincode_event = /* @__PURE__ */ fileDesc("ChpwZWVyL2NoYWluY29kZV9ldmVudC5wcm90bxIGcHJvdG9zIloKDkNoYWluY29kZUV2ZW50EhQKDGNoYWluY29kZV9pZBgBIAEoCRINCgV0eF9pZBgCIAEoCRISCgpldmVudF9uYW1lGAMgASgJEg8KB3BheWxvYWQYBCABKAxCaQoib3JnLmh5cGVybGVkZ2VyLmZhYnJpYy5wcm90b3MucGVlckIVQ2hhaW5jb2RlRXZlbnRQYWNrYWdlWixnaXRodWIuY29tL2h5cGVybGVkZ2VyL2ZhYnJpYy1wcm90b3MtZ28vcGVlcmIGcHJvdG8z"); var file_msp_msp_principal = /* @__PURE__ */ fileDesc("Chdtc3AvbXNwX3ByaW5jaXBhbC5wcm90bxIGY29tbW9uIsYBCgxNU1BQcmluY2lwYWwSRQoYcHJpbmNpcGFsX2NsYXNzaWZpY2F0aW9uGAEgASgOMiMuY29tbW9uLk1TUFByaW5jaXBhbC5DbGFzc2lmaWNhdGlvbhIRCglwcmluY2lwYWwYAiABKAwiXAoOQ2xhc3NpZmljYXRpb24SCAoEUk9MRRAAEhUKEU9SR0FOSVpBVElPTl9VTklUEAESDAoISURFTlRJVFkQAhINCglBTk9OWU1JVFkQAxIMCghDT01CSU5FRBAEInEKEE9yZ2FuaXphdGlvblVuaXQSFgoObXNwX2lkZW50aWZpZXIYASABKAkSJgoeb3JnYW5pemF0aW9uYWxfdW5pdF9pZGVudGlmaWVyGAIgASgJEh0KFWNlcnRpZmllcnNfaWRlbnRpZmllchgDIAEoDCKVAQoHTVNQUm9sZRIWCg5tc3BfaWRlbnRpZmllchgBIAEoCRIpCgRyb2xlGAIgASgOMhsuY29tbW9uLk1TUFJvbGUuTVNQUm9sZVR5cGUiRwoLTVNQUm9sZVR5cGUSCgoGTUVNQkVSEAASCQoFQURNSU4QARIKCgZDTElFTlQQAhIICgRQRUVSEAMSCwoHT1JERVJFUhAEIp0BChRNU1BJZGVudGl0eUFub255bWl0eRJNCg5hbm9ueW1pdHlfdHlwZRgBIAEoDjI1LmNvbW1vbi5NU1BJZGVudGl0eUFub255bWl0eS5NU1BJZGVudGl0eUFub255bWl0eVR5cGUiNgoYTVNQSWRlbnRpdHlBbm9ueW1pdHlUeXBlEgsKB05PTUlOQUwQABINCglBTk9OWU1PVVMQASI9ChFDb21iaW5lZFByaW5jaXBhbBIoCgpwcmluY2lwYWxzGAEgAygLMhQuY29tbW9uLk1TUFByaW5jaXBhbEJTCiRvcmcuaHlwZXJsZWRnZXIuZmFicmljLnByb3Rvcy5jb21tb25aK2dpdGh1Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljLXByb3Rvcy1nby9tc3BiBnByb3RvMw"); // src/generated_protos/common/policies_pb.ts var file_common_policies = /* @__PURE__ */ fileDesc("ChVjb21tb24vcG9saWNpZXMucHJvdG8SBmNvbW1vbiJrCgZQb2xpY3kSDAoEdHlwZRgBIAEoBRINCgV2YWx1ZRgCIAEoDCJECgpQb2xpY3lUeXBlEgsKB1VOS05PV04QABINCglTSUdOQVRVUkUQARIHCgNNU1AQAhIRCg1JTVBMSUNJVF9NRVRBEAMiewoXU2lnbmF0dXJlUG9saWN5RW52ZWxvcGUSDwoHdmVyc2lvbhgBIAEoBRIlCgRydWxlGAIgASgLMhcuY29tbW9uLlNpZ25hdHVyZVBvbGljeRIoCgppZGVudGl0aWVzGAMgAygLMhQuY29tbW9uLk1TUFByaW5jaXBhbCKfAQoPU2lnbmF0dXJlUG9saWN5EhMKCXNpZ25lZF9ieRgBIAEoBUgAEjIKCG5fb3V0X29mGAIgASgLMh4uY29tbW9uLlNpZ25hdHVyZVBvbGljeS5OT3V0T2ZIABo7CgZOT3V0T2YSCQoBbhgBIAEoBRImCgVydWxlcxgCIAMoCzIXLmNvbW1vbi5TaWduYXR1cmVQb2xpY3lCBgoEVHlwZSJ/ChJJbXBsaWNpdE1ldGFQb2xpY3kSEgoKc3ViX3BvbGljeRgBIAEoCRItCgRydWxlGAIgASgOMh8uY29tbW9uLkltcGxpY2l0TWV0YVBvbGljeS5SdWxlIiYKBFJ1bGUSBwoDQU5ZEAASBwoDQUxMEAESDAoITUFKT1JJVFkQAiKHAQoRQXBwbGljYXRpb25Qb2xpY3kSOwoQc2lnbmF0dXJlX3BvbGljeRgBIAEoCzIfLmNvbW1vbi5TaWduYXR1cmVQb2xpY3lFbnZlbG9wZUgAEikKH2NoYW5uZWxfY29uZmlnX3BvbGljeV9yZWZlcmVuY2UYAiABKAlIADoCGAFCBgoEVHlwZUJWCiRvcmcuaHlwZXJsZWRnZXIuZmFicmljLnByb3Rvcy5jb21tb25aLmdpdGh1Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljLXByb3Rvcy1nby9jb21tb25iBnByb3RvMw", [file_msp_msp_principal]); // src/generated_protos/peer/chaincode_pb.ts var file_peer_chaincode = /* @__PURE__ */ fileDesc("ChRwZWVyL2NoYWluY29kZS5wcm90bxIGcHJvdG9zIjoKC0NoYWluY29kZUlEEgwKBHBhdGgYASABKAkSDAoEbmFtZRgCIAEoCRIPCgd2ZXJzaW9uGAMgASgJIqEBCg5DaGFpbmNvZGVJbnB1dBIMCgRhcmdzGAEgAygMEjwKC2RlY29yYXRpb25zGAIgAygLMicucHJvdG9zLkNoYWluY29kZUlucHV0LkRlY29yYXRpb25zRW50cnkSDwoHaXNfaW5pdBgDIAEoCBoyChBEZWNvcmF0aW9uc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoDDoCOAEi3AEKDUNoYWluY29kZVNwZWMSKAoEdHlwZRgBIAEoDjIaLnByb3Rvcy5DaGFpbmNvZGVTcGVjLlR5cGUSKQoMY2hhaW5jb2RlX2lkGAIgASgLMhMucHJvdG9zLkNoYWluY29kZUlEEiUKBWlucHV0GAMgASgLMhYucHJvdG9zLkNoYWluY29kZUlucHV0Eg8KB3RpbWVvdXQYBCABKAUiPgoEVHlwZRINCglVTkRFRklORUQQABIKCgZHT0xBTkcQARIICgROT0RFEAISBwoDQ0FSEAMSCAoESkFWQRAEIoQBChdDaGFpbmNvZGVEZXBsb3ltZW50U3BlYxItCg5jaGFpbmNvZGVfc3BlYxgBIAEoCzIVLnByb3Rvcy5DaGFpbmNvZGVTcGVjEhQKDGNvZGVfcGFja2FnZRgDIAEoDEoECAIQA0oECAQQBVIOZWZmZWN0aXZlX2RhdGVSCGV4ZWNfZW52ImEKF0NoYWluY29kZUludm9jYXRpb25TcGVjEi0KDmNoYWluY29kZV9zcGVjGAEgASgLMhUucHJvdG9zLkNoYWluY29kZVNwZWNKBAgCEANSEWlkX2dlbmVyYXRpb25fYWxnIigKDkxpZmVjeWNsZUV2ZW50EhYKDmNoYWluY29kZV9uYW1lGAEgASgJIi0KB0NEU0RhdGESDAoEaGFzaBgBIAEoDBIUCgxtZXRhZGF0YWhhc2gYAiABKAwi1AEKDUNoYWluY29kZURhdGESDAoEbmFtZRgBIAEoCRIPCgd2ZXJzaW9uGAIgASgJEgwKBGVzY2MYAyABKAkSDAoEdnNjYxgEIAEoCRIvCgZwb2xpY3kYBSABKAsyHy5jb21tb24uU2lnbmF0dXJlUG9saWN5RW52ZWxvcGUSDAoEZGF0YRgGIAEoDBIKCgJpZBgHIAEoDBI9ChRpbnN0YW50aWF0aW9uX3BvbGljeRgIIAEoCzIfLmNvbW1vbi5TaWduYXR1cmVQb2xpY3lFbnZlbG9wZSKVAQoZQ2hhaW5jb2RlQWRkaXRpb25hbFBhcmFtcxIXCg91c2Vfd3JpdGVfYmF0Y2gYASABKAgSHAoUbWF4X3NpemVfd3JpdGVfYmF0Y2gYAiABKA0SHQoVdXNlX2dldF9tdWx0aXBsZV9rZXlzGAMgASgIEiIKGm1heF9zaXplX2dldF9tdWx0aXBsZV9rZXlzGAQgASgNQlIKIm9yZy5oeXBlcmxlZGdlci5mYWJyaWMucHJvdG9zLnBlZXJaLGdpdGh1Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljLXByb3Rvcy1nby9wZWVyYgZwcm90bzM", [file_common_policies]); var ChaincodeIDSchema = /* @__PURE__ */ messageDesc(file_peer_chaincode, 0); var ChaincodeInputSchema = /* @__PURE__ */ messageDesc(file_peer_chaincode, 1); var ChaincodeSpecSchema = /* @__PURE__ */ messageDesc(file_peer_chaincode, 2); var ChaincodeInvocationSpecSchema = /* @__PURE__ */ messageDesc(file_peer_chaincode, 4); var file_peer_proposal_response = /* @__PURE__ */ fileDesc("ChxwZWVyL3Byb3Bvc2FsX3Jlc3BvbnNlLnByb3RvEgZwcm90b3Mi3gEKEFByb3Bvc2FsUmVzcG9uc2USDwoHdmVyc2lvbhgBIAEoBRItCgl0aW1lc3RhbXAYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEiIKCHJlc3BvbnNlGAQgASgLMhAucHJvdG9zLlJlc3BvbnNlEg8KB3BheWxvYWQYBSABKAwSKAoLZW5kb3JzZW1lbnQYBiABKAsyEy5wcm90b3MuRW5kb3JzZW1lbnQSKwoIaW50ZXJlc3QYByABKAsyGS5wcm90b3MuQ2hhaW5jb2RlSW50ZXJlc3QiPAoIUmVzcG9uc2USDgoGc3RhdHVzGAEgASgFEg8KB21lc3NhZ2UYAiABKAkSDwoHcGF5bG9hZBgDIAEoDCJDChdQcm9wb3NhbFJlc3BvbnNlUGF5bG9hZBIVCg1wcm9wb3NhbF9oYXNoGAEgASgMEhEKCWV4dGVuc2lvbhgCIAEoDCIyCgtFbmRvcnNlbWVudBIQCghlbmRvcnNlchgBIAEoDBIRCglzaWduYXR1cmUYAiABKAwiPgoRQ2hhaW5jb2RlSW50ZXJlc3QSKQoKY2hhaW5jb2RlcxgBIAMoCzIVLnByb3Rvcy5DaGFpbmNvZGVDYWxsIsYBCg1DaGFpbmNvZGVDYWxsEgwKBG5hbWUYASABKAkSGAoQY29sbGVjdGlvbl9uYW1lcxgCIAMoCRIYChBub19wcml2YXRlX3JlYWRzGAMgASgIEhgKEG5vX3B1YmxpY193cml0ZXMYBCABKAgSNQoMa2V5X3BvbGljaWVzGAUgAygLMh8uY29tbW9uLlNpZ25hdHVyZVBvbGljeUVudmVsb3BlEiIKGmRpc3JlZ2FyZF9uYW1lc3BhY2VfcG9saWN5GAYgASgIQmsKIm9yZy5oeXBlcmxlZGdlci5mYWJyaWMucHJvdG9zLnBlZXJCF1Byb3Bvc2FsUmVzcG9uc2VQYWNrYWdlWixnaXRodWIuY29tL2h5cGVybGVkZ2VyL2ZhYnJpYy1wcm90b3MtZ28vcGVlcmIGcHJvdG8z", [file_google_protobuf_timestamp, file_common_policies]); // src/generated_protos/peer/proposal_pb.ts var file_peer_proposal = /* @__PURE__ */ fileDesc("ChNwZWVyL3Byb3Bvc2FsLnByb3RvEgZwcm90b3MiOwoOU2lnbmVkUHJvcG9zYWwSFgoOcHJvcG9zYWxfYnl0ZXMYASABKAwSEQoJc2lnbmF0dXJlGAIgASgMIj4KCFByb3Bvc2FsEg4KBmhlYWRlchgBIAEoDBIPCgdwYXlsb2FkGAIgASgMEhEKCWV4dGVuc2lvbhgDIAEoDCJeChhDaGFpbmNvZGVIZWFkZXJFeHRlbnNpb24SKQoMY2hhaW5jb2RlX2lkGAIgASgLMhMucHJvdG9zLkNoYWluY29kZUlESgQIARACUhFwYXlsb2FkX3Zpc2JpbGl0eSKoAQoYQ2hhaW5jb2RlUHJvcG9zYWxQYXlsb2FkEg0KBWlucHV0GAEgASgMEkgKDFRyYW5zaWVudE1hcBgCIAMoCzIyLnByb3Rvcy5DaGFpbmNvZGVQcm9wb3NhbFBheWxvYWQuVHJhbnNpZW50TWFwRW50cnkaMwoRVHJhbnNpZW50TWFwRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgMOgI4ASKZAQoPQ2hhaW5jb2RlQWN0aW9uEg8KB3Jlc3VsdHMYASABKAwSDgoGZXZlbnRzGAIgASgMEiIKCHJlc3BvbnNlGAMgASgLMhAucHJvdG9zLlJlc3BvbnNlEikKDGNoYWluY29kZV9pZBgEIAEoCzITLnByb3Rvcy5DaGFpbmNvZGVJREoECAUQBlIQdG9rZW5fb3BlcmF0aW9uc0JjCiJvcmcuaHlwZXJsZWRnZXIuZmFicmljLnByb3Rvcy5wZWVyQg9Qcm9wb3NhbFBhY2thZ2VaLGdpdGh1Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljLXByb3Rvcy1nby9wZWVyYgZwcm90bzM", [file_peer_chaincode, file_peer_proposal_response]); var SignedProposalSchema = /* @__PURE__ */ messageDesc(file_peer_proposal, 0); var ProposalSchema = /* @__PURE__ */ messageDesc(file_peer_proposal, 1); var ChaincodeHeaderExtensionSchema = /* @__PURE__ */ messageDesc(file_peer_proposal, 2); var ChaincodeProposalPayloadSchema = /* @__PURE__ */ messageDesc(file_peer_proposal, 3); var file_common_common = /* @__PURE__ */ fileDesc("ChNjb21tb24vY29tbW9uLnByb3RvEgZjb21tb24iGwoKTGFzdENvbmZpZxINCgVpbmRleBgBIAEoBCJICghNZXRhZGF0YRINCgV2YWx1ZRgBIAEoDBItCgpzaWduYXR1cmVzGAIgAygLMhkuY29tbW9uLk1ldGFkYXRhU2lnbmF0dXJlIlsKEU1ldGFkYXRhU2lnbmF0dXJlEhgKEHNpZ25hdHVyZV9oZWFkZXIYASABKAwSEQoJc2lnbmF0dXJlGAIgASgMEhkKEWlkZW50aWZpZXJfaGVhZGVyGAMgASgMIjUKEElkZW50aWZpZXJIZWFkZXISEgoKaWRlbnRpZmllchgBIAEoDRINCgVub25jZRgCIAEoDCI6CgZIZWFkZXISFgoOY2hhbm5lbF9oZWFkZXIYASABKAwSGAoQc2lnbmF0dXJlX2hlYWRlchgCIAEoDCK5AQoNQ2hhbm5lbEhlYWRlchIMCgR0eXBlGAEgASgFEg8KB3ZlcnNpb24YAiABKAUSLQoJdGltZXN0YW1wGAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBISCgpjaGFubmVsX2lkGAQgASgJEg0KBXR4X2lkGAUgASgJEg0KBWVwb2NoGAYgASgEEhEKCWV4dGVuc2lvbhgHIAEoDBIVCg10bHNfY2VydF9oYXNoGAggASgMIjEKD1NpZ25hdHVyZUhlYWRlchIPCgdjcmVhdG9yGAEgASgMEg0KBW5vbmNlGAIgASgMIjcKB1BheWxvYWQSHgoGaGVhZGVyGAEgASgLMg4uY29tbW9uLkhlYWRlchIMCgRkYXRhGAIgASgMIi4KCEVudmVsb3BlEg8KB3BheWxvYWQYASABKAwSEQoJc2lnbmF0dXJlGAIgASgMInYKBUJsb2NrEiMKBmhlYWRlchgBIAEoCzITLmNvbW1vbi5CbG9ja0hlYWRlchIfCgRkYXRhGAIgASgLMhEuY29tbW9uLkJsb2NrRGF0YRInCghtZXRhZGF0YRgDIAEoCzIVLmNvbW1vbi5CbG9ja01ldGFkYXRhIkcKC0Jsb2NrSGVhZGVyEg4KBm51bWJlchgBIAEoBBIVCg1wcmV2aW91c19oYXNoGAIgASgMEhEKCWRhdGFfaGFzaBgDIAEoDCIZCglCbG9ja0RhdGESDAoEZGF0YRgBIAMoDCIhCg1CbG9ja01ldGFkYXRhEhAKCG1ldGFkYXRhGAEgAygMIlsKFE9yZGVyZXJCbG9ja01ldGFkYXRhEicKC2xhc3RfY29uZmlnGAEgASgLMhIuY29tbW9uLkxhc3RDb25maWcSGgoSY29uc2VudGVyX21ldGFkYXRhGAIgASgMKsABCgZTdGF0dXMSCwoHVU5LTk9XThAAEgwKB1NVQ0NFU1MQyAESEAoLQkFEX1JFUVVFU1QQkAMSDgoJRk9SQklEREVOEJMDEg4KCU5PVF9GT1VORBCUAxIdChhSRVFVRVNUX0VOVElUWV9UT09fTEFSR0UQnQMSGgoVSU5URVJOQUxfU0VSVkVSX0VSUk9SEPQDEhQKD05PVF9JTVBMRU1FTlRFRBD1AxIYChNTRVJWSUNFX1VOQVZBSUxBQkxFEPcDKu4BCgpIZWFkZXJUeXBlEgsKB01FU1NBR0UQABIKCgZDT05GSUcQARIRCg1DT05GSUdfVVBEQVRFEAISGAoURU5ET1JTRVJfVFJBTlNBQ1RJT04QAxIbChNPUkRFUkVSX1RSQU5TQUNUSU9OEAQaAggBEhUKEURFTElWRVJfU0VFS19JTkZPEAUSFQoRQ0hBSU5DT0RFX1BBQ0tBR0UQBiIECAcQByIECAgQCCIECAkQCSoUUEVFUl9SRVNPVVJDRV9VUERBVEUqFFBFRVJfQURNSU5fT1BFUkFUSU9OKhFUT0tFTl9UUkFOU0FDVElPTip0ChJCbG9ja01ldGFkYXRhSW5kZXgSDgoKU0lHTkFUVVJFUxAAEhMKC0xBU1RfQ09ORklHEAEaAggBEhcKE1RSQU5TQUNUSU9OU19GSUxURVIQAhIPCgdPUkRFUkVSEAMaAggBEg8KC0NPTU1JVF9IQVNIEARCVgokb3JnLmh5cGVybGVkZ2VyLmZhYnJpYy5wcm90b3MuY29tbW9uWi5naXRodWIuY29tL2h5cGVybGVkZ2VyL2ZhYnJpYy1wcm90b3MtZ28vY29tbW9uYgZwcm90bzM", [file_google_protobuf_timestamp]); var HeaderSchema = /* @__PURE__ */ messageDesc(file_common_common, 4); var ChannelHeaderSchema = /* @__PURE__ */ messageDesc(file_common_common, 5); var SignatureHeaderSchema = /* @__PURE__ */ messageDesc(file_common_common, 6); var PayloadSchema = /* @__PURE__ */ messageDesc(file_common_common, 7); var EnvelopeSchema = /* @__PURE__ */ messageDesc(file_common_common, 8); // src/generated_protos/peer/transaction_pb.ts var file_peer_transaction = /* @__PURE__ */ fileDesc("ChZwZWVyL3RyYW5zYWN0aW9uLnByb3RvEgZwcm90b3MiXQoUUHJvY2Vzc2VkVHJhbnNhY3Rpb24SLQoTdHJhbnNhY3Rpb25FbnZlbG9wZRgBIAEoCzIQLmNvbW1vbi5FbnZlbG9wZRIWCg52YWxpZGF0aW9uQ29kZRgCIAEoBSI5CgtUcmFuc2FjdGlvbhIqCgdhY3Rpb25zGAEgAygLMhkucHJvdG9zLlRyYW5zYWN0aW9uQWN0aW9uIjQKEVRyYW5zYWN0aW9uQWN0aW9uEg4KBmhlYWRlchgBIAEoDBIPCgdwYXlsb2FkGAIgASgMIm0KFkNoYWluY29kZUFjdGlvblBheWxvYWQSIgoaY2hhaW5jb2RlX3Byb3Bvc2FsX3BheWxvYWQYASABKAwSLwoGYWN0aW9uGAIgASgLMh8ucHJvdG9zLkNoYWluY29kZUVuZG9yc2VkQWN0aW9uImcKF0NoYWluY29kZUVuZG9yc2VkQWN0aW9uEiEKGXByb3Bvc2FsX3Jlc3BvbnNlX3BheWxvYWQYASABKAwSKQoMZW5kb3JzZW1lbnRzGAIgAygLMhMucHJvdG9zLkVuZG9yc2VtZW50KqsFChBUeFZhbGlkYXRpb25Db2RlEgkKBVZBTElEEAASEAoMTklMX0VOVkVMT1BFEAESDwoLQkFEX1BBWUxPQUQQAhIVChFCQURfQ09NTU9OX0hFQURFUhADEhkKFUJBRF9DUkVBVE9SX1NJR05BVFVSRRAEEiAKHElOVkFMSURfRU5ET1JTRVJfVFJBTlNBQ1RJT04QBRIeChpJTlZBTElEX0NPTkZJR19UUkFOU0FDVElPThAGEhoKFlVOU1VQUE9SVEVEX1RYX1BBWUxPQUQQBxIVChFCQURfUFJPUE9TQUxfVFhJRBAIEhIKDkRVUExJQ0FURV9UWElEEAkSHgoaRU5ET1JTRU1FTlRfUE9MSUNZX0ZBSUxVUkUQChIWChJNVkNDX1JFQURfQ09ORkxJQ1QQCxIZChVQSEFOVE9NX1JFQURfQ09ORkxJQ1QQDBITCg9VTktOT1dOX1RYX1RZUEUQDRIaChZUQVJHRVRfQ0hBSU5fTk9UX0ZPVU5EEA4SFAoQTUFSU0hBTF9UWF9FUlJPUhAPEhAKDE5JTF9UWEFDVElPThAQEhUKEUVYUElSRURfQ0hBSU5DT0RFEBESHgoaQ0hBSU5DT0RFX1ZFUlNJT05fQ09ORkxJQ1QQEhIYChRCQURfSEVBREVSX0VYVEVOU0lPThATEhYKEkJBRF9DSEFOTkVMX0hFQURFUhAUEhgKFEJBRF9SRVNQT05TRV9QQVlMT0FEEBUSDQoJQkFEX1JXU0VUEBYSFAoQSUxMRUdBTF9XUklURVNFVBAXEhQKEElOVkFMSURfV1JJVEVTRVQQGBIVChFJTlZBTElEX0NIQUlOQ09ERRAZEhIKDU5PVF9WQUxJREFURUQQ/gESGQoUSU5WQUxJRF9PVEhFUl9SRUFTT04Q/wEqRQoMTWV0YURhdGFLZXlzEhgKFFZBTElEQVRJT05fUEFSQU1FVEVSEAASGwoXVkFMSURBVElPTl9QQVJBTUVURVJfVjIQAUJmCiJvcmcuaHlwZXJsZWRnZXIuZmFicmljLnByb3Rvcy5wZWVyQhJUcmFuc2FjdGlvblBhY2thZ2VaLGdpdGh1Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljLXByb3Rvcy1nby9wZWVyYgZwcm90bzM", [file_peer_proposal_response, file_common_common]); var file_orderer_ab = /* @__PURE__ */ fileDesc("ChBvcmRlcmVyL2FiLnByb3RvEgdvcmRlcmVyIkEKEUJyb2FkY2FzdFJlc3BvbnNlEh4KBnN0YXR1cxgBIAEoDjIOLmNvbW1vbi5TdGF0dXMSDAoEaW5mbxgCIAEoCSIMCgpTZWVrTmV3ZXN0IgwKClNlZWtPbGRlc3QiHwoNU2Vla1NwZWNpZmllZBIOCgZudW1iZXIYASABKAQiEAoOU2Vla05leHRDb21taXQiwQEKDFNlZWtQb3NpdGlvbhIlCgZuZXdlc3QYASABKAsyEy5vcmRlcmVyLlNlZWtOZXdlc3RIABIlCgZvbGRlc3QYAiABKAsyEy5vcmRlcmVyLlNlZWtPbGRlc3RIABIrCglzcGVjaWZpZWQYAyABKAsyFi5vcmRlcmVyLlNlZWtTcGVjaWZpZWRIABIuCgtuZXh0X2NvbW1pdBgEIAEoCzIXLm9yZGVyZXIuU2Vla05leHRDb21taXRIAEIGCgRUeXBlIqADCghTZWVrSW5mbxIkCgVzdGFydBgBIAEoCzIVLm9yZGVyZXIuU2Vla1Bvc2l0aW9uEiMKBHN0b3AYAiABKAsyFS5vcmRlcmVyLlNlZWtQb3NpdGlvbhIwCghiZWhhdmlvchgDIAEoDjIeLm9yZGVyZXIuU2Vla0luZm8uU2Vla0JlaGF2aW9yEjsKDmVycm9yX3Jlc3BvbnNlGAQgASgOMiMub3JkZXJlci5TZWVrSW5mby5TZWVrRXJyb3JSZXNwb25zZRI3Cgxjb250ZW50X3R5cGUYBSABKA4yIS5vcmRlcmVyLlNlZWtJbmZvLlNlZWtDb250ZW50VHlwZSI8CgxTZWVrQmVoYXZpb3ISFQoRQkxPQ0tfVU5USUxfUkVBRFkQABIVChFGQUlMX0lGX05PVF9SRUFEWRABIjAKEVNlZWtFcnJvclJlc3BvbnNlEgoKBlNUUklDVBAAEg8KC0JFU1RfRUZGT1JUEAEiMQoPU2Vla0NvbnRlbnRUeXBlEgkKBUJMT0NLEAASEwoPSEVBREVSX1dJVEhfU0lHEAEiWwoPRGVsaXZlclJlc3BvbnNlEiAKBnN0YXR1cxgBIAEoDjIOLmNvbW1vbi5TdGF0dXNIABIeCgVibG9jaxgCIAEoCzINLmNvbW1vbi5CbG9ja0gAQgYKBFR5cGUyiwEKD0F0b21pY0Jyb2FkY2FzdBI9CglCcm9hZGNhc3QSEC5jb21tb24uRW52ZWxvcGUaGi5vcmRlcmVyLkJyb2FkY2FzdFJlc3BvbnNlKAEwARI5CgdEZWxpdmVyEhAuY29tbW9uLkVudmVsb3BlGhgub3JkZXJlci5EZWxpdmVyUmVzcG9uc2UoATABQlgKJW9yZy5oeXBlcmxlZGdlci5mYWJyaWMucHJvdG9zLm9yZGVyZXJaL2dpdGh1Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljLXByb3Rvcy1nby9vcmRlcmVyYgZwcm90bzM", [file_common_common]); var SeekNewestSchema = /* @__PURE__ */ messageDesc(file_orderer_ab, 1); var SeekSpecifiedSchema = /* @__PURE__ */ messageDesc(file_orderer_ab, 3); var SeekPositionSchema = /* @__PURE__ */ messageDesc(file_orderer_ab, 5); var SeekInfoSchema = /* @__PURE__ */ messageDesc(file_orderer_ab, 6); // src/generated_protos/gateway/gateway_pb.ts var file_gateway_gateway = /* @__PURE__ */ fileDesc("ChVnYXRld2F5L2dhdGV3YXkucHJvdG8SB2dhdGV3YXkikwEKDkVuZG9yc2VSZXF1ZXN0EhYKDnRyYW5zYWN0aW9uX2lkGAEgASgJEhIKCmNoYW5uZWxfaWQYAiABKAkSNAoUcHJvcG9zZWRfdHJhbnNhY3Rpb24YAyABKAsyFi5wcm90b3MuU2lnbmVkUHJvcG9zYWwSHwoXZW5kb3JzaW5nX29yZ2FuaXphdGlvbnMYBCADKAkiQQoPRW5kb3JzZVJlc3BvbnNlEi4KFHByZXBhcmVkX3RyYW5zYWN0aW9uGAEgASgLMhAuY29tbW9uLkVudmVsb3BlImsKDVN1Ym1pdFJlcXVlc3QSFgoOdHJhbnNhY3Rpb25faWQYASABKAkSEgoKY2hhbm5lbF9pZBgCIAEoCRIuChRwcmVwYXJlZF90cmFuc2FjdGlvbhgDIAEoCzIQLmNvbW1vbi5FbnZlbG9wZSIQCg5TdWJtaXRSZXNwb25zZSI/ChlTaWduZWRDb21taXRTdGF0dXNSZXF1ZXN0Eg8KB3JlcXVlc3QYASABKAwSEQoJc2lnbmF0dXJlGAIgASgMIlMKE0NvbW1pdFN0YXR1c1JlcXVlc3QSFgoOdHJhbnNhY3Rpb25faWQYASABKAkSEgoKY2hhbm5lbF9pZBgCIAEoCRIQCghpZGVudGl0eRgDIAEoDCJWChRDb21taXRTdGF0dXNSZXNwb25zZRIoCgZyZXN1bHQYASABKA4yGC5wcm90b3MuVHhWYWxpZGF0aW9uQ29kZRIUCgxibG9ja19udW1iZXIYAiABKAQikQEKD0V2YWx1YXRlUmVxdWVzdBIWCg50cmFuc2FjdGlvbl9pZBgBIAEoCRISCgpjaGFubmVsX2lkGAIgASgJEjQKFHByb3Bvc2VkX3RyYW5zYWN0aW9uGAMgASgLMhYucHJvdG9zLlNpZ25lZFByb3Bvc2FsEhwKFHRhcmdldF9vcmdhbml6YXRpb25zGAQgAygJIjQKEEV2YWx1YXRlUmVzcG9uc2USIAoGcmVzdWx0GAEgASgLMhAucHJvdG9zLlJlc3BvbnNlIkIKHFNpZ25lZENoYWluY29kZUV2ZW50c1JlcXVlc3QSDwoHcmVxdWVzdBgBIAEoDBIRCglzaWduYXR1cmUYAiABKAwioQEKFkNoYWluY29kZUV2ZW50c1JlcXVlc3QSEgoKY2hhbm5lbF9pZBgBIAEoCRIUCgxjaGFpbmNvZGVfaWQYAiABKAkSEAoIaWRlbnRpdHkYAyABKAwSLQoOc3RhcnRfcG9zaXRpb24YBCABKAsyFS5vcmRlcmVyLlNlZWtQb3NpdGlvbhIcChRhZnRlcl90cmFuc2FjdGlvbl9pZBgFIAEoCSJXChdDaGFpbmNvZGVFdmVudHNSZXNwb25zZRImCgZldmVudHMYASADKAsyFi5wcm90b3MuQ2hhaW5jb2RlRXZlbnQSFAoMYmxvY2tfbnVtYmVyGAIgASgEIj8KC0Vycm9yRGV0YWlsEg8KB2FkZHJlc3MYASABKAkSDgoGbXNwX2lkGAIgASgJEg8KB21lc3NhZ2UYAyABKAkieAoTUHJvcG9zZWRUcmFuc2FjdGlvbhIWCg50cmFuc2FjdGlvbl9pZBgBIAEoCRIoCghwcm9wb3NhbBgCIAEoCzIWLnByb3Rvcy5TaWduZWRQcm9wb3NhbBIfChdlbmRvcnNpbmdfb3JnYW5pemF0aW9ucxgDIAMoCSJRChNQcmVwYXJlZFRyYW5zYWN0aW9uEhYKDnRyYW5zYWN0aW9uX2lkGAEgASgJEiIKCGVudmVsb3BlGAIgASgLMhAuY29tbW9uLkVudmVsb3BlMvQCCgdHYXRld2F5EjwKB0VuZG9yc2USFy5nYXRld2F5LkVuZG9yc2VSZXF1ZXN0GhguZ2F0ZXdheS5FbmRvcnNlUmVzcG9uc2USOQoGU3VibWl0EhYuZ2F0ZXdheS5TdWJtaXRSZXF1ZXN0GhcuZ2F0ZXdheS5TdWJtaXRSZXNwb25zZRJRCgxDb21taXRTdGF0dXMSIi5nYXRld2F5LlNpZ25lZENvbW1pdFN0YXR1c1JlcXVlc3QaHS5nYXRld2F5LkNvbW1pdFN0YXR1c1Jlc3BvbnNlEj8KCEV2YWx1YXRlEhguZ2F0ZXdheS5FdmFsdWF0ZVJlcXVlc3QaGS5nYXRld2F5LkV2YWx1YXRlUmVzcG9uc2USXAoPQ2hhaW5jb2RlRXZlbnRzEiUuZ2F0ZXdheS5TaWduZWRDaGFpbmNvZGVFdmVudHNSZXF1ZXN0GiAuZ2F0ZXdheS5DaGFpbmNvZGVFdmVudHNSZXNwb25zZTABQmgKJW9yZy5oeXBlcmxlZGdlci5mYWJyaWMucHJvdG9zLmdhdGV3YXlCDEdhdGV3YXlQcm90b1ABWi9naXRodWIuY29tL2h5cGVybGVkZ2VyL2ZhYnJpYy1wcm90b3MtZ28vZ2F0ZXdheWIGcHJvdG8z", [file_peer_chaincode_event, file_peer_proposal, file_peer_proposal_response, file_peer_transaction, file_common_common, file_orderer_ab]); var EndorseRequestSchema = /* @__PURE__ */ messageDesc(file_gateway_gateway, 0); var SubmitRequestSchema = /* @__PURE__ */ messageDesc(file_gateway_gateway, 2); var EvaluateRequestSchema = /* @__PURE__ */ messageDesc(file_gateway_gateway, 7); var SignedChaincodeEventsRequestSchema = /* @__PURE__ */ messageDesc(file_gateway_gateway, 9); var ChaincodeEventsRequestSchema = /* @__PURE__ */ messageDesc(file_gateway_gateway, 10); var Gateway = /* @__PURE__ */ serviceDesc(file_gateway_gateway, 0); function getGroundedError(error) { if (error instanceof ConnectError) { if (error.details && error.details.length > 0) { const decodedDetails = error.details.map((detail) => { if (detail && "value" in detail && detail.value instanceof Uint8Array) { try { return new TextDecoder().decode(detail.value); } catch (e) { return null; } } return null; }).filter((msg) => msg !== null); if (decodedDetails.length > 0) { return decodedDetails.join("; \n"); } } return error.message; } if (error instanceof Error) { return error.message; } return String(error); } // src/utils/try-catch.ts async function tryCatch(promiseFn, errorFn = getGroundedError) { try { const data = await promiseFn(); return { success: true, data, error: null }; } catch (caughtError) { return { success: false, data: null, error: new Error(errorFn(caughtError)) }; } } function tryCatchSync(fn) { try { const data = fn(); return { success: true, data, error: null }; } catch (caughtError) { if (caughtError instanceof Error) { return { success: false, data: null, error: caughtError }; } return { success: false, data: null, error: new Error(String(caughtError)) }; } } // src/protobuf/parser.ts function decodeChaincodePayload(payloadBytes) { if (!payloadBytes || payloadBytes.length === 0) { return null; } try { const decodedString = new TextDecoder("utf-8", { fatal: true }).decode( payloadBytes ); try { return JSON.parse(decodedString); } catch { return decodedString; } } catch { const hex = Array.from(payloadBytes).map((b) => b.toString(16).padStart(2, "0")).join(""); return `(binary) 0x${hex}`; } } function parseEvaluateResponse(response) { return tryCatchSync(() => { if (!response || !response.result) { throw new Error( "La respuesta de evaluate o su campo 'result' est\xE1n ausentes." ); } const finalResponse = response.result; const parsedData = decodeChaincodePayload(finalResponse.payload); return { status: finalResponse.status, message: finalResponse.message, parsedData }; }); } var N = new BN( "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16 ); var HALF_N = N.shrn(1); function rsToDer(rs) { const n = rs.length / 2; let r = rs.slice(0, n); let s = rs.slice(n); while (r.length > 1 && r[0] === 0 && r[1] < 128) r = r.slice(1); while (s.length > 1 && s[0] === 0 && s[1] < 128) s = s.slice(1); if (r[0] >= 128) r = new Uint8Array([0, ...r]); if (s[0] >= 128) s = new Uint8Array([0, ...s]); const encoded = new Uint8Array([2, r.length, ...r, 2, s.length, ...s]); return new Uint8Array([48, encoded.length, ...encoded]); } function preventMalleability(signature) { const r = signature.slice(0, signature.length / 2); let s = signature.slice(signature.length / 2); const sBN = new BN(s); if (sBN.cmp(HALF_N) > 0) { const newS = N.sub(sBN); s = new Uint8Array(newS.toArray("be", 32)); } return new Uint8Array([...r, ...s]); } async function signAndFormat(dataToSign, identity) { const rawSignature = await identity.sign(dataToSign); const lowSSignature = preventMalleability(rawSignature); return rsToDer(lowSSignature); } async function signProposal(proposalBytes, identity) { return signAndFormat(proposalBytes, identity); } async function signEnvelope(envelopePayload, identity) { return signAndFormat(envelopePayload, identity); } var file_msp_identities = /* @__PURE__ */ fileDesc("ChRtc3AvaWRlbnRpdGllcy5wcm90bxIDbXNwIjUKElNlcmlhbGl6ZWRJZGVudGl0eRINCgVtc3BpZBgBIAEoCRIQCghpZF9ieXRlcxgCIAEoDCJhChhTZXJpYWxpemVkSWRlbWl4SWRlbnRpdHkSDQoFbnltX3gYASABKAwSDQoFbnltX3kYAiABKAwSCgoCb3UYAyABKAwSDAoEcm9sZRgEIAEoDBINCgVwcm9vZhgFIAEoDEJQCiFvcmcuaHlwZXJsZWRnZXIuZmFicmljLnByb3Rvcy5tc3BaK2dpdGh1Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljLXByb3Rvcy1nby9tc3BiBnByb3RvMw"); var SerializedIdentitySchema = /* @__PURE__ */ messageDesc(file_msp_identities, 0); var require2 = createRequire(import.meta.url); var nodeCrypto; if (typeof window === "undefined") { const { webcrypto } = require2("crypto"); nodeCrypto = webcrypto; } function getRandomValues(array) { if (typeof window !== "undefined" && window.crypto) { return window.crypto.getRandomValues(array); } if (nodeCrypto) { return nodeCrypto.getRandomValues(array); } throw new Error( "Unsupported environment: A Web Crypto API implementation is required." ); } // src/protobuf/builder.ts function stringToUint8Array(str) { return new TextEncoder().encode(str); } function bytesToHexString(bytes) { return (bytes ?? new Uint8Array()).reduce( (s, byte) => s + byte.toString(16).padStart(2, "0"), "" ); } function generateNonce() { return getRandomValues(new Uint8Array(24)); } function createSerializedIdentityBytes(mspId, certPem) { const si = create(SerializedIdentitySchema, { mspid: mspId, idBytes: stringToUint8Array(certPem) }); return toBinary(SerializedIdentitySchema, si); } async function generateTransactionId(identity, mspId) { const nonce = generateNonce(); const creatorBytes = createSerializedIdentityBytes(mspId, identity.cert); const combined = new Uint8Array(nonce.length + creatorBytes.length); combined.set(nonce); combined.set(creatorBytes, nonce.length); const hashBytes = sha256(combined); const txId = bytesToHexString(hashBytes); return { txId, nonce, creatorBytes }; } function buildProposalPayload(params, txId, creatorBytes, nonce) { const ccId = create(ChaincodeIDSchema, { name: params.chaincodeName }); const argsAsBytes = [stringToUint8Array(params.functionName)]; (params.args || []).forEach((arg) => { argsAsBytes.push(typeof arg === "string" ? stringToUint8Array(arg) : arg); }); const ccInput = create(ChaincodeInputSchema, { args: argsAsBytes }); const ccSpec = create(ChaincodeSpecSchema, { type: 1 /* GOLANG */, chaincodeId: ccId, input: ccInput }); const ccInvocationSpec = create(ChaincodeInvocationSpecSchema, { chaincodeSpec: ccSpec }); const ccProposalPayload = create(ChaincodeProposalPayloadSchema, { input: toBinary(ChaincodeInvocationSpecSchema, ccInvocationSpec) }); const ccHeaderExtension = create(ChaincodeHeaderExtensionSchema, { chaincodeId: ccId }); const channelHeader = create(ChannelHeaderSchema, { type: 3 /* ENDORSER_TRANSACTION */, version: 1, channelId: params.channelName, txId, epoch: protoInt64.parse(0), extension: toBinary(ChaincodeHeaderExtensionSchema, ccHeaderExtension) }); const signatureHeader = create(SignatureHeaderSchema, { creator: creatorBytes, nonce }); const header = create(HeaderSchema, { channelHeader: toBinary(ChannelHeaderSchema, channelHeader), signatureHeader: toBinary(SignatureHeaderSchema, signatureHeader) }); const proposal = create(ProposalSchema, { header: toBinary(HeaderSchema, header), payload: toBinary(ChaincodeProposalPayloadSchema, ccProposalPayload) }); return toBinary(ProposalSchema, proposal); } // src/client/fabric-client.ts var FabricClient = class { gatewayClient; constructor(config) { const transport = createGrpcWebTransport({ baseUrl: config.gatewayUrl }); this.gatewayClient = createClient(Gateway, transport); } /** * Evalúa una transacción de solo lectura. La propuesta se envía a un peer, * pero no se envía al servicio de ordenamiento. * * @param params Los detalles de la propuesta de transacción. * @param identity La identidad del cliente para firmar la propuesta. * @returns Un Result con los datos de la transacción evaluada o un error. */ async evaluateTransaction(params, identity) { return tryCatch(async () => { const { txId, nonce, creatorBytes } = await generateTransactionId( identity, params.mspId ); const proposalPayloadBytes = buildProposalPayload( params, txId, creatorBytes, nonce ); const signature = await signProposal(proposalPayloadBytes, identity); const signedProposal = create(SignedProposalSchema, { proposalBytes: proposalPayloadBytes, signature }); const evaluateRequest = create(EvaluateRequestSchema, { channelId: params.channelName, transactionId: txId, proposedTransaction: signedProposal }); const evaluateResponse = await this.gatewayClient.evaluate(evaluateRequest); const parsedResult = parseEvaluateResponse(evaluateResponse); if (!parsedResult.success) { throw parsedResult.error; } return { txId, ...parsedResult.data }; }, getGroundedError); } /** * Prepara (endorsa) una transacción para su posterior envío al orderer. * La propuesta se envía a los peers para su endoso según la política. * * @param params Los detalles de la propuesta de transacción. * @param identity La identidad del cliente para firmar la propuesta. * @returns Un Result con la transacción preparada (el envelope de la transacción) o un error. */ async prepareTransaction(params, identity) { return tryCatch(async () => { const { txId, nonce, creatorBytes } = await generateTransactionId( identity, params.mspId ); const proposalPayloadBytes = buildProposalPayload( params, txId, creatorBytes, nonce ); const signature = await signProposal(proposalPayloadBytes, identity); const signedProposal = create(SignedProposalSchema, { proposalBytes: proposalPayloadBytes, signature }); const endorseRequest = create(EndorseRequestSchema, { channelId: params.channelName, transactionId: txId, proposedTransaction: signedProposal }); const endorseResponse = await this.gatewayClient.endorse(endorseRequest); if (!endorseResponse.preparedTransaction?.payload) { throw new Error( "La respuesta del Endorse no conten\xEDa una transacci\xF3n preparada v\xE1lida." ); } return { txId, transactionEnvelope: endorseResponse.preparedTransaction.payload }; }, getGroundedError); } /** * Envía una transacción previamente preparada y firmada al orderer para su commit en el ledger. * * @param params Los detalles de la transacción a enviar, incluyendo el envelope de `prepareTransaction`. * @param identity La identidad del cliente para firmar el envelope final. * @returns Un Result confirmando el envío exitoso o un error. */ async submitSignedTransaction(params, identity) { return tryCatch(async () => { const envelopeSignature = await signEnvelope( params.preparedTransaction, identity ); const clientSignedEnvelope = create(EnvelopeSchema, { payload: params.preparedTransaction, signature: envelopeSignature }); const submitRequest = create(SubmitRequestSchema, { channelId: params.channelName, transactionId: params.txId, preparedTransaction: clientSignedEnvelope }); await this.gatewayClient.submit(submitRequest); return { txId: params.txId, status: "Transacci\xF3n enviada con \xE9xito al gateway." }; }, getGroundedError); } }; var file_ledger_rwset_rwset = /* @__PURE__ */ fileDesc("ChhsZWRnZXIvcndzZXQvcndzZXQucHJvdG8SBXJ3c2V0IoMBCg5UeFJlYWRXcml0ZVNldBIzCgpkYXRhX21vZGVsGAEgASgOMh8ucndzZXQuVHhSZWFkV3JpdGVTZXQuRGF0YU1vZGVsEicKCG5zX3J3c2V0GAIgAygLMhUucndzZXQuTnNSZWFkV3JpdGVTZXQiEwoJRGF0YU1vZGVsEgYKAktWEAAieAoOTnNSZWFkV3JpdGVTZXQSEQoJbmFtZXNwYWNlGAEgASgJEg0KBXJ3c2V0GAIgASgMEkQKF2NvbGxlY3Rpb25faGFzaGVkX3J3c2V0GAMgAygLMiMucndzZXQuQ29sbGVjdGlvbkhhc2hlZFJlYWRXcml0ZVNldCJlChxDb2xsZWN0aW9uSGFzaGVkUmVhZFdyaXRlU2V0EhcKD2NvbGxlY3Rpb25fbmFtZRgBIAEoCRIUCgxoYXNoZWRfcndzZXQYAiABKAwSFgoOcHZ0X3J3c2V0X2hhc2gYAyABKAwieAoRVHhQdnRSZWFkV3JpdGVTZXQSMwoKZGF0YV9tb2RlbBgBIAEoDjIfLnJ3c2V0LlR4UmVhZFdyaXRlU2V0LkRhdGFNb2RlbBIuCgxuc19wdnRfcndzZXQYAiADKAsyGC5yd3NldC5Oc1B2dFJlYWRXcml0ZVNldCJmChFOc1B2dFJlYWRXcml0ZVNldBIRCgluYW1lc3BhY2UYASABKAkSPgoUY29sbGVjdGlvbl9wdnRfcndzZXQYAiADKAsyIC5yd3NldC5Db2xsZWN0aW9uUHZ0UmVhZFdyaXRlU2V0IkMKGUNvbGxlY3Rpb25QdnRSZWFkV3JpdGVTZXQSFwoPY29sbGVjdGlvbl9uYW1lGAEgASgJEg0KBXJ3c2V0GAIgASgMQmIKKm9yZy5oeXBlcmxlZGdlci5mYWJyaWMucHJvdG9zLmxlZGdlci5yd3NldFo0Z2l0aHViLmNvbS9oeXBlcmxlZGdlci9mYWJyaWMtcHJvdG9zLWdvL2xlZGdlci9yd3NldGIGcHJvdG8z"); // src/generated_protos/peer/events_pb.ts var file_peer_events = /* @__PURE__ */ fileDesc("ChFwZWVyL2V2ZW50cy5wcm90bxIGcHJvdG9zIm8KDUZpbHRlcmVkQmxvY2sSEgoKY2hhbm5lbF9pZBgBIAEoCRIOCgZudW1iZXIYAiABKAQSOgoVZmlsdGVyZWRfdHJhbnNhY3Rpb25zGAQgAygLMhsucHJvdG9zLkZpbHRlcmVkVHJhbnNhY3Rpb24ixgEKE0ZpbHRlcmVkVHJhbnNhY3Rpb24SDAoEdHhpZBgBIAEoCRIgCgR0eXBlGAIgASgOMhIuY29tbW9uLkhlYWRlclR5cGUSNAoSdHhfdmFsaWRhdGlvbl9jb2RlGAMgASgOMhgucHJvdG9zLlR4VmFsaWRhdGlvbkNvZGUSQQoTdHJhbnNhY3Rpb25fYWN0aW9ucxgEIAEoCzIiLnByb3Rvcy5GaWx0ZXJlZFRyYW5zYWN0aW9uQWN0aW9uc0gAQgYKBERhdGEiWAoaRmlsdGVyZWRUcmFuc2FjdGlvbkFjdGlvbnMSOgoRY2hhaW5jb2RlX2FjdGlvbnMYASADKAsyHy5wcm90b3MuRmlsdGVyZWRDaGFpbmNvZGVBY3Rpb24iSgoXRmlsdGVyZWRDaGFpbmNvZGVBY3Rpb24SLwoPY2hhaW5jb2RlX2V2ZW50GAEgASgLMhYucHJvdG9zLkNoYWluY29kZUV2ZW50Is8BChNCbG9ja0FuZFByaXZhdGVEYXRhEhwKBWJsb2NrGAEgASgLMg0uY29tbW9uLkJsb2NrEkkKEHByaXZhdGVfZGF0YV9tYXAYAiADKAsyLy5wcm90b3MuQmxvY2tBbmRQcml2YXRlRGF0YS5Qcml2YXRlRGF0YU1hcEVudHJ5Gk8KE1ByaXZhdGVEYXRhTWFwRW50cnkSCwoDa2V5GAEgASgEEicKBXZhbHVlGAIgASgLMhgucndzZXQuVHhQdnRSZWFkV3JpdGVTZXQ6AjgBIssBCg9EZWxpdmVyUmVzcG9uc2USIAoGc3RhdHVzGAEgASgOMg4uY29tbW9uLlN0YXR1c0gAEh4KBWJsb2NrGAIgASgLMg0uY29tbW9uLkJsb2NrSAASLwoOZmlsdGVyZWRfYmxvY2sYAyABKAsyFS5wcm90b3MuRmlsdGVyZWRCbG9ja0gAEj0KFmJsb2NrX2FuZF9wcml2YXRlX2RhdGEYBCABKAsyGy5wcm90b3MuQmxvY2tBbmRQcml2YXRlRGF0YUgAQgYKBFR5cGUy1AEKB0RlbGl2ZXISOgoHRGVsaXZlchIQLmNvbW1vbi5FbnZlbG9wZRoXLnByb3Rvcy5EZWxpdmVyUmVzcG9uc2UiACgBMAESQgoPRGVsaXZlckZpbHRlcmVkEhAuY29tbW9uLkVudmVsb3BlGhcucHJvdG9zLkRlbGl2ZXJSZXNwb25zZSIAKAEwARJJChZEZWxpdmVyV2l0aFByaXZhdGVEYXRhEhAuY29tbW9uLkVudmVsb3BlGhcucHJvdG9zLkRlbGl2ZXJSZXNwb25zZSIAKAEwAUJhCiJvcmcuaHlwZXJsZWRnZXIuZmFicmljLnByb3Rvcy5wZWVyQg1FdmVudHNQYWNrYWdlWixnaXRodWIuY29tL2h5cGVybGVkZ2VyL2ZhYnJpYy1wcm90b3MtZ28vcGVlcmIGcHJvdG8z", [file_common_common, file_ledger_rwset_rwset, file_peer_chaincode_event, file_peer_transaction]); var DeliverResponseSchema = /* @__PURE__ */ messageDesc(file_peer_events, 5); async function createSignedDeliverRequest(params) { const { txId, nonce, creatorBytes } = await generateTransactionId( params.identity, params.mspId ); let startPosition; if (typeof params.startBlock === "bigint") { startPosition = create(SeekPositionSchema, { Type: { case: "specified", value: create(SeekSpecifiedSchema, { number: params.startBlock }) } }); } else { startPosition = create(SeekPositionSchema, { Type: { case: "newest", value: create(SeekNewestSchema, {}) } }); } const stopPosition = create(SeekPositionSchema, { Type: { case: "specified", value: create(SeekSpecifiedSchema, { number: protoInt64.parse(Number.MAX_SAFE_INTEGER.toString()) }) } }); const seekInfo = create(SeekInfoSchema, { start: startPosition, stop: stopPosition, behavior: 0 }); const channelHeader = create(ChannelHeaderSchema, { type: 5 /* DELIVER_SEEK_INFO */, version: 0, channelId: params.channelName, txId, epoch: protoInt64.parse(0), timestamp: create(TimestampSchema, { seconds: BigInt(Math.floor(Date.now() / 1e3)), // Segundos como BigInt nanos: Date.now() % 1e3 * 1e6 // Nanosegundos }) }); const signatureHeader = create(SignatureHeaderSchema, { creator: creatorBytes, nonce }); const payload = create(PayloadSchema, { header: create(HeaderSchema, { channelHeader: toBinary(ChannelHeaderSchema, channelHeader), signatureHeader: toBinary(SignatureHeaderSchema, signatureHeader) }), data: toBinary(SeekInfoSchema, seekInfo) }); const payloadBytes = toBinary(PayloadSchema, payload); const signature = await signEnvelope(payloadBytes, params.identity); return create(EnvelopeSchema, { payload: payloadBytes, signature }); } // src/events/event-service.ts var EventService = class { gatewayClient; wsBaseUrl; constructor(config) { const transport = createGrpcWebTransport({ baseUrl: config.gatewayUrl }); this.gatewayClient = createClient(Gateway, transport); this.wsBaseUrl = config.wsUrl; } /** * Establece una conexión para escuchar eventos emitidos por un chaincode específico. * Devuelve un Generador Asíncrono que produce respuestas a medida que llegan. * * @param params Los detalles del canal y chaincode a escuchar. * @param identity La identidad del cliente para firmar la petición de eventos. * @param signal Un AbortSignal para cancelar la suscripción y cerrar el stream. * @yields {ChaincodeEventsResponse} Un objeto de respuesta por cada bloque que contenga eventos. */ async *listenToChaincodeEvents(params, identity, signal) { try { const signedRequest = await this.createSignedChaincodeEventsRequest( params, identity ); const stream = this.gatewayClient.chaincodeEvents(signedRequest, { signal }); console.log( `[EventService] Escuchando eventos para ${params.chaincodeName} en ${params.channelName}...` ); for await (const response of stream) { if (signal.aborted) break; yield response; } } catch (error) { if (signal.aborted || error instanceof Error && error.name === "AbortError") { console.log( "[EventService] Stream de eventos de chaincode cancelado por el cliente." ); } else { console.error( "[EventService] Error en el stream de eventos de chaincode:", error ); throw error; } } finally { console.log( `[EventService] Stream para ${params.chaincodeName} finalizado.` ); } } /** * Establece una conexión WebSocket para escuchar eventos de bloque filtrados de un canal. * Devuelve un Generador Asíncrono que produce bloques a medida que son commiteados. * * @param params Los detalles del canal y peer a escuchar. * @param identity La identidad del cliente para firmar la petición de deliver. * @param signal Un AbortSignal para cancelar la suscripción y cerrar el WebSocket. * @yields {FilteredBlock} Un bloque filtrado cada vez que se commitea uno nuevo. */ async *listenToBlockEvents(params, identity, signal) { const wsUrl = new URL(this.wsBaseUrl); wsUrl.searchParams.append("target", params.targetPeer); wsUrl.searchParams.append("hostname", params.targetHostname); const signedRequestEnvelope = await createSignedDeliverRequest({ ...params, identity }); const requestBytes = toBinary(EnvelopeSchema, signedRequestEnvelope); const socket = new WebSocket(wsUrl.toString()); socket.binaryType = "arraybuffer"; try { await this.waitForSocketOpen(socket, signal); socket.send(requestBytes); console.log( `[EventService] Escuchando eventos de bloque en el canal ${params.channelName}...` ); while (!signal.aborted) { const message = await this.waitForSocketMessage(socket, signal); const deliverResponse = fromBinary( DeliverResponseSchema, new Uint8Array(message.data) ); if (deliverResponse.Type.case === "filteredBlock") { yield deliverResponse.Type.value; } else if (deliverResponse.Type.case === "status") { console.warn( "[EventService] Mensaje de estado recibido del peer:", deliverResponse.Type.value ); } } } catch (error) { if (signal.aborted || error instanceof Error && error.name === "AbortError") { console.log( "[EventService] Stream de eventos de bloque cancelado por el cliente." ); } else { console.error( "[EventService] Error en el stream de eventos de bloque:", error ); throw error; } } finally { if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) { socket.close(1e3, "Stream finished by client"); } console.log( `[EventService] Stream de bloques para ${params.channelName} finalizado.` ); } } // --- Métodos Privados de Soporte --- async createSignedChaincodeEventsRequest(params, identity) { const identityBytes = createSerializedIdentityBytes( params.mspId, identity.cert ); const eventsRequest = create(ChaincodeEventsRequestSchema, { channelId: params.channelName, chaincodeId: params.chaincodeName, identity: identityBytes }); const requestBytes = toBinary(ChaincodeEventsRequestSchema, eventsRequest); const signature = await signProposal(requestBytes, identity); return create(SignedChaincodeEventsRequestSchema, { request: requestBytes, signature }); } waitForSocketOpen(socket, signal) { return new Promise((resolve, reject) => { if (signal.aborted) return reject(new Error("AbortError")); const abortHandler = () => { socket.close(); reject(new Error("AbortError")); }; signal.addEventListener("abort", abortHandler, { once: true }); socket.onopen = () => { signal.removeEventListener("abort", abortHandler); resolve(); }; socket.onerror = () => { signal.removeEventListener("abort", abortHandler); reject(new Error("Fallo al establecer la conexi\xF3n WebSocket.")); }; }); } waitForSocketMessage(socket, signal) { return new Promise((resolve, reject) => { if (signal.aborted) return reject(new Error("AbortError")); const abortHandler = () => reject(new Error("AbortError")); signal.addEventListener("abort", abortHandler, { once: true }); socket.onmessage = (event) => { signal.removeEventListener("abort", abortHandler); resolve(event); }; socket.onclose = (event) => { signal.removeEventListener("abort", abortHandler); reject( new Error( `WebSocket cerrado inesperadamente: ${event.code} ${event.reason}` ) ); }; socket.onerror = () => { signal.removeEventListener("abort", abortHandler); reject(new Error("Error en la conexi\xF3n WebSocket.")); }; }); } }; export { EventService, FabricClient, IdentityService }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map