import moment from 'moment'; import { BlockSchema, DIDNodeSchema, DocumentNodeSchema, EventType, LogNodeSchema } from '../storage'; import { CatBienes } from './models/CatBienes'; import { DescBienes } from './models/DescBienes'; import { Destino, DGen, EntregaCafe, EnvioContenedorFE, FormaPago, FormularioCafe, Item, RucType, TasaITBMS, TiempoPago, TipoAmbiente, TipoDocumento, TipoEmision, TipoGeneracion, TipoNaturalezaOperacion, TipoOperacion, TipoReceptor, TipoRuc, TipoTransaccionVenta, Totales } from './models'; import { DIDDocumentBuilder, DIDMethodXDV } from '../did'; import { eddsa } from 'elliptic'; import { expect } from 'chai'; import { FEBuilder, Plantillas } from './FEBuilder'; import { IpldClient } from '../ipld'; import { KeyConvert, Wallet, X509Info, XmlDsig } from '../crypto'; import { LDCryptoTypes, X509 } from '../crypto'; import { Paises } from './models/Paises'; import { Ubicaciones } from './models/Ubicaciones'; import { Unidades } from './models/Unidades'; const ipld = new IpldClient(); const xdvMethod = new DIDMethodXDV(ipld); const localStorage = {}; const testMatch = `1.00FE01200000000000029-29-29-56763220181015259827406393001267295805482012020-10-09T00:00:00-05:0010125982740639302020-10-09T00:00:00-05:00012113115629-29-2921-1-2112+8.9892,-79.5201Calle 501-1-2667311385629-29-29213-1-3131301PACalle 50667311385629-29-2921Servicios profesionales Abril Mayo 2020 relacionado a desarrollo web1500500500000No reembolsableActividad1Investigacion de algoritmo para firmar una factura electronica2500500500000Probablemente posible43238021.000.001000.000.001000.001000.001.000.00` describe("FEBuilder", function () { let latestFEDocument; let feDoocument; let feSigned; beforeEach(function () { }); it("should be able to create a FE with id and version", async function () { // Generales const gDGen = { ...Plantillas.Pruebas, ...Plantillas.PruebasFechas(new Date(2020, 9, 9)), ...Plantillas.TodoElectronicoLocal, dNroDF: '2598274063', dPtoFacDF: '930', dSeg: '672958054', iNatOp: TipoNaturalezaOperacion.Venta, iTipoOp: TipoOperacion.Compra, iTipoTranVenta: TipoTransaccionVenta.Giro, gEmis: { gRucEmi: { dTipoRuc: TipoRuc.Juridico, dRuc: '29-29-29', dDV: '56' }, dNombEm: '', dSucEm: '7632', dCoordEm: '+8.9892,-79.5201', gUbiEm: { dCodUbi: Ubicaciones['BOCAS DEL TORO-BOCAS DEL TORO-BASTIMENTOS'], }, dDirecEm: 'Calle 50', dTfnEm: ['66731138'], }, gDatRec: { iTipoRec: TipoReceptor.Contribuyente, gRucRec: { dTipoRuc: TipoRuc.Juridico, dRuc: '29-29-29', dDV: '56' }, cPaisRec: Paises.PANAMA, dNombRec: '', gUbiRec: { dCodUbi: Ubicaciones['PANAMA OESTE-ARRAIJAN-CERRO SILVESTRE'], }, dDirecRec: 'Calle 50', dTfnRec: ['66731138'], }, gAutXML: [{ gRucAutXML: { dTipoRuc: TipoRuc.Juridico, dRuc: '29-29-29', dDV: '56' }, }] }; const gItem: Item[] = [ { dSecItem: 1, dDescProd: 'Servicios profesionales Abril Mayo 2020 relacionado a desarrollo web', cCantCodInt: 1, cUnidad: Unidades['Actividad: una unidad de trabajo o acción'], dInfEmFE: 'No reembolsable', gPrecios: { dPrItem: 500, dPrUnit: 500, dValTotItem: 500 }, gITBMSItem: { dTasaITBMS: TasaITBMS.TasaExonerado, dValITBMS: 0 } }, { dSecItem: 2, dDescProd: 'Investigacion de algoritmo para firmar una factura electronica', cCantCodInt: 1, dCodCPBSabr: CatBienes['Servicios de Gestión, Servicios Profesionales de Empresa y Servicios Administrativos'], dCodCPBScmp: DescBienes.Software, dInfEmFE: 'Probablemente posible', gPrecios: { dPrItem: 500, dPrUnit: 500, dValTotItem: 500 }, gITBMSItem: { dTasaITBMS: TasaITBMS.TasaExonerado, dValITBMS: 0 } } ]; const gTot: Totales = { dNroItems: 1, dTotGravado: 1000, dTotITBMS: 0, dTotNeto: 1000, dTotRec: 0, dVTot: 1000, dVTotItems: 1, dVuelto: 0, iPzPag: TiempoPago.Plazo, gFormaPago: [{ iFormaPago: FormaPago.Otro, dVlrCuota: 1 }] }; const rfe = FEBuilder .create() .rFE({ dId: 'FE01200000000000029-29-29-5676322018101525982740639300126729580548', dVerForm: 1.00, gDGen, gItem, gTot, }); const res = await rfe.toXml(); latestFEDocument = res; feDoocument = rfe; expect(res).equal(testMatch); }); it("should be able to sign with RSA Key pair", async function () { const issuer: X509Info = { stateOrProvinceName: 'PA', organizationName: 'RM', organizationalUnitName: 'Engineering', commonName: 'Rogelio Morrell', countryName: 'Panama', localityName: 'Panama' }; const rsaKey = await Wallet.getRSA256Standalone(); const rsaKeyExports = await KeyConvert.getX509RSA(rsaKey); const selfSignedCert = X509.createSelfSignedCertificateFromRSA( rsaKeyExports.pemAsPrivate, rsaKeyExports.pemAsPublic, issuer); try { const signedDocuments = await XmlDsig.signFEDocument(rsaKeyExports.pemAsPrivate, selfSignedCert, latestFEDocument); expect(!!signedDocuments.json).equals(true) expect(!!signedDocuments.xml).equals(true) } catch (e) { console.log(e); } }); it("should be able to sign with RSA Key pair and stored in IPLD as DID tree", async function () { await ipld.initialize(); const issuer: X509Info = { stateOrProvinceName: 'PA', organizationName: 'RM', organizationalUnitName: 'Engineering', commonName: 'Rogelio Morrell', countryName: 'Panama', localityName: 'Panama' }; const rsaKey = await Wallet.getRSA256Standalone(); const rsaKeyExports = await KeyConvert.getX509RSA(rsaKey); const selfSignedCert = X509.createSelfSignedCertificateFromRSA( rsaKeyExports.pemAsPrivate, rsaKeyExports.pemAsPublic, issuer); try { const signedDocuments = await XmlDsig.signFEDocument(rsaKeyExports.pemAsPrivate, selfSignedCert, latestFEDocument); expect(!!signedDocuments.json).equals(true) expect(!!signedDocuments.xml).equals(true) // Use another different wallet for creating did const mnemonic = Wallet.generateMnemonic(); const opts = { mnemonic, password: '123password' }; const keystore = await Wallet.createHDWallet(opts); expect(JSON.parse(keystore).version).equal(3); const wallet = await Wallet.unlock(keystore, opts.password); const kp = wallet.getEd25519(); const kpJwk = await KeyConvert.getEd25519(kp); // Create IPFS key storage lock const session = await xdvMethod.createIpldSession(kpJwk.pem); const ldCrypto = await KeyConvert.createLinkedDataJsonFormat( LDCryptoTypes.Ed25519, kp, false); // Create DID document with an did-ipid based issuer const did = await DIDDocumentBuilder .createDID({ issuer: session.key, verificationKeys: [ldCrypto.toPublicKey()], authenticationKeys: [ldCrypto.toAuthorizationKey()] }); ipld.setSigner((payload) => { return Promise.resolve(kp.sign(payload).toHex()); }); // DID let didNode = await ipld.createDidNode(did, 'my-ed25519-key'); const published = await ipld.ipfsClient.name.publish(didNode.cid); console.log(published) // append json let documentNode = await ipld.appendDocumentNode(didNode.cid, signedDocuments.json, 'mydoc'); const blockCid = await ipld.patchBlock(null, [{ cid: didNode.cid, tag: 'mykey' }], [{ cid: documentNode.cid, tag: 'mydoc' }], [{ cids: [didNode.cidLog, documentNode.cidLog], timestamp: moment().unix() }] ); const node = await ipld.getNode(blockCid, '/'); expect(node.value.$block).equals(0); localStorage['blockCid'] = blockCid; } catch (e) { console.log(e); } }); it("should be able to get the IPLD logs by cid", async function () { // await ipld.initialize(); try { const block = localStorage['blockCid']; // get all documents const listDocuments = await ipld.getNode(block, '/document'); console.log(listDocuments) expect(!!listDocuments.value).equals(true); // get did from document ref const did = await ipld.getNode(listDocuments.value['mydoc'], '/'); expect(!!did).equals(true); // update blockchain const newnode = await ipld.createNode({ testing: true }); const blockCid = await ipld.patchBlock(block, [{ cid: newnode as string, tag: 'newnode' }], ); const node = await ipld.getNode(blockCid, '/'); console.log(node) expect(node.value.$ref.toString()).equals(block.toString()); } catch (e) { throw e; } }); });