Skip to content

Commit

Permalink
feat: implemented updateCredential
Browse files Browse the repository at this point in the history
  • Loading branch information
GuilaneDen committed Dec 10, 2024
1 parent 17dac31 commit de39666
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 13 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
- name: Install node
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: "^16.10.0"
node-version: "22.x"
- name: Install dependencies
run: yarn install
- name: Run test
Expand Down
167 changes: 159 additions & 8 deletions src/Concordium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import {
serializeRegisterData,
serializeTransferToPublic,
serializeDeployModule,
serializeInitContract
serializeInitContract,
serializeUpdateContract,
serializeTransactionPayloads,
serializeUpdateCredentials
} from "./serialization";
import BigNumber from "bignumber.js";
import { encodeInt32 } from "./utils";
Expand Down Expand Up @@ -51,6 +54,25 @@ const P2_LAST = 0x00;
const P1_INITIAL_PACKET = 0x00;
const P1_SCHEDULED_TRANSFER_PAIRS = 0x01;

// Update Credentials
const P2_CREDENTIAL_INITIAL = 0x00;
const P2_CREDENTIAL_CREDENTIAL_INDEX = 0x01;
const P2_CREDENTIAL_CREDENTIAL = 0x02;
const P2_CREDENTIAL_ID_COUNT = 0x03;
const P2_CREDENTIAL_ID = 0x04;
const P2_THRESHOLD = 0x05;

//Deploy Credential
const P1_VERIFICATION_KEY_LENGTH = 0x0A;
const P1_VERIFICATION_KEY = 0x01;
const P1_SIGNATURE_THRESHOLD = 0x02;
const P1_AR_IDENTITY = 0x03;
const P1_CREDENTIAL_DATES = 0x04;
const P1_ATTRIBUTE_TAG = 0x05;
const P1_ATTRIBUTE_VALUE = 0x06;
const P1_LENGTH_OF_PROOFS = 0x07;
const P1_PROOFS = 0x08;
const P1_NEW_OR_EXISTING = 0x09

const INS = {
// GET_VERSION: 0x03,
Expand All @@ -62,11 +84,13 @@ const INS = {
SIGN_CONFIGURE_DELEGATION: 0x17,
SIGN_CONFIGURE_BAKER: 0x18,
GET_APP_NAME: 0x21,
SIGN_UPDATE_CREDENTIALS: 0x31,
SIGN_TRANSFER_MEMO: 0x32,
SIGN_TRANSFER_SCHEDULE_AND_MEMO: 0x34,
SIGN_REGISTER_DATA: 0x35,
SIGN_DEPLOY_MODULE: 0x06,
SIGN_INIT_CONTRACT: 0x06,
SIGN_UPDATE_CONTRACT: 0x06,
};

/**
Expand Down Expand Up @@ -474,7 +498,7 @@ export default class Concordium {
};
}

async signDeployModule(txn, path: string): Promise<{ signature: string[]; transaction }> {
async signDeployModule(txn, path: string): Promise<{ signature: string[] }> {

const { payloads } = serializeDeployModule(txn, path);

Expand All @@ -492,15 +516,12 @@ export default class Concordium {

if (response.length === 1) throw new Error("User has declined.");

const transaction = payloads.slice(1);

return {
signature: response.toString("hex"),
transaction: Buffer.concat(transaction).toString("hex"),
};
}

async signInitContract(txn, path: string): Promise<{ signature: string[]; transaction }> {
async signInitContract(txn, path: string): Promise<{ signature: string[] }> {

const { payloads } = serializeInitContract(txn, path);

Expand All @@ -518,11 +539,141 @@ export default class Concordium {

if (response.length === 1) throw new Error("User has declined.");

const transaction = payloads.slice(1);
return {
signature: response.toString("hex"),
};
}

async signUpdateContract(txn, path: string): Promise<{ signature: string[] }> {

const { payloads } = serializeUpdateContract(txn, path);

let response;

for (let i = 0; i < payloads.length; i++) {
const lastChunk = i === payloads.length - 1;
response = await this.sendToDevice(
INS.SIGN_UPDATE_CONTRACT,
P1_FIRST_CHUNK + i,
lastChunk ? P2_LAST : P2_MORE,
payloads[i]
);
}

if (response.length === 1) throw new Error("User has declined.");

return {
signature: response.toString("hex"),
};
}

async signUpdateCredentials(txn, path: string): Promise<{ signature: string[] }> {

const { payloadHeaderKindAndIndexLength, credentialIndex, numberOfVerificationKeys, keyIndexAndSchemeAndVerificationKey, thresholdAndRegIdAndIPIdentity, encIdCredPubShareAndKey, validToAndCreatedAtAndAttributesLength, tag, valueLength, value, proofLength, proofs, credentialIdCount, credentialIds, threshold } = serializeUpdateCredentials(txn, path);

let response;
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
NONE,
P2_CREDENTIAL_INITIAL,
payloadHeaderKindAndIndexLength[0]
);

for (let i = 0; i < txn.payload.newCredentials.length; i++) {
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
NONE,
P2_CREDENTIAL_CREDENTIAL_INDEX,
credentialIndex[i]
);
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_VERIFICATION_KEY_LENGTH,
P2_CREDENTIAL_CREDENTIAL,
numberOfVerificationKeys[i]
);
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_VERIFICATION_KEY,
P2_CREDENTIAL_CREDENTIAL,
keyIndexAndSchemeAndVerificationKey[i]
);
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_SIGNATURE_THRESHOLD,
P2_CREDENTIAL_CREDENTIAL,
thresholdAndRegIdAndIPIdentity[i]
);
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_AR_IDENTITY,
P2_CREDENTIAL_CREDENTIAL,
encIdCredPubShareAndKey[i]
);
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_CREDENTIAL_DATES,
P2_CREDENTIAL_CREDENTIAL,
validToAndCreatedAtAndAttributesLength[i]
);
for (let j = 0; j < Object.keys(txn.payload.newCredentials[i].cdi.policy.revealedAttributes).length; j++) {
const tagAndValueLength = Buffer.concat([tag[i][j], valueLength[i][j]])
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_ATTRIBUTE_TAG,
P2_CREDENTIAL_CREDENTIAL,
tagAndValueLength
);
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_ATTRIBUTE_VALUE,
P2_CREDENTIAL_CREDENTIAL,
value[i][j]
);
}
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_LENGTH_OF_PROOFS,
P2_CREDENTIAL_CREDENTIAL,
proofLength[i]
);

const proofPayload = serializeTransactionPayloads(proofs[i]);
for (let j = 0; j < proofPayload.length; j++) {
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
P1_PROOFS,
P2_CREDENTIAL_CREDENTIAL,
proofPayload[j]
);
}
}

response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
NONE,
P2_CREDENTIAL_ID_COUNT,
credentialIdCount
);
for (let i = 0; i < txn.payload.removeCredentialIds.length; i++) {
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
NONE,
P2_CREDENTIAL_ID,
credentialIds[i]
);
}
response = await this.sendToDevice(
INS.SIGN_UPDATE_CREDENTIALS,
NONE,
P2_THRESHOLD,
threshold
);

if (response.length === 1) throw new Error("User has declined.");

return {
signature: response.toString("hex"),
transaction: Buffer.concat(transaction).toString("hex"),
};
}

Expand Down
87 changes: 85 additions & 2 deletions src/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const MAX_CHUNK_SIZE = 255;
const MAX_SCHEDULE_CHUNK_SIZE = 15;
const HEADER_LENGTH = 60;
const TRANSACTION_KIND_LENGTH = 1;
const INDEX_LENGTH = 1;
const ONE_OCTET_LENGTH = 1;
const BITMAP_LENGTH = 2;
const STAKING_PAYLOAD_LENGTH = 8;
const RESTAKE_EARNINGS_PAYLOAD_LENGTH = 1;
Expand All @@ -18,7 +20,20 @@ const KEYS_PAYLOAD_LENGTH = KEYS_ELECTION_AND_SIGNATURE_LENGTH + KEYS_AGGREGATIO
const METADATA_URL_LENGTH = 2;
const TRANSACTION_FEE_COMMISSION_LENGTH = 4;
const BAKING_REWARD_COMMISSION_LENGTH = 4;
const REVOCATION_THRESHOLD_LENGTH = 4;
const FINALIZATION_REWARD_COMMISSION_LENGTH = 4;
const KEY_LENGTH = 32;
const REG_ID_LENGTH = 48;
const IP_IDENTITY_LENGTH = 4;
const AR_DATA_LENGTH = 2;
const ID_CRED_PUB_SHARE_LENGTH = 96;
const VALID_TO_LENGTH = 3;
const CREATED_AT_LENGTH = 3;
const ATTRIBUTES_LENGTH = 2;
const TAG_LENGTH = 1;
const VALUE_LENGTH = 1;
const PROOF_LENGTH_LENGTH = 4;
const CREDENTIAL_ID_LENGTH = 48;

const serializePath = (path: number[]): Buffer => {
const buf = Buffer.alloc(1 + path.length * 4);
Expand Down Expand Up @@ -86,7 +101,7 @@ const serializeTransactionPayloadsWithDerivationPath = (path: string, rawTx: Buf
};


const serializeTransactionPayloads = (rawTx: Buffer): Buffer[] => {
export const serializeTransactionPayloads = (rawTx: Buffer): Buffer[] => {
let offset = 0;
const payloads: Buffer[] = [];
while (offset !== rawTx.length) {
Expand Down Expand Up @@ -323,4 +338,72 @@ export const serializeDeployModule = (txn: any, path: string): { payloads: Buffe

export const serializeInitContract = (txn: any, path: string): { payloads: Buffer[] } => {
return serializeTransaction(txn, path);
};
};

export const serializeUpdateContract = (txn: any, path: string): { payloads: Buffer[] } => {
return serializeTransaction(txn, path);
};

export const serializeUpdateCredentials = (txn: any, path: string): { payloadHeaderKindAndIndexLength: Buffer[], credentialIndex: Buffer[], numberOfVerificationKeys: Buffer[], keyIndexAndSchemeAndVerificationKey: Buffer[], thresholdAndRegIdAndIPIdentity: Buffer[], encIdCredPubShareAndKey: Buffer[], validToAndCreatedAtAndAttributesLength: Buffer[], attributesLength: Buffer[], tag: Buffer[][], valueLength: Buffer[][], value: Buffer[][], proofLength: Buffer[], proofs: Buffer[], credentialIdCount: Buffer, credentialIds: Buffer[], threshold: Buffer } => {
let offset = 0;
const txSerialized = serializeAccountTransaction(txn);
const headerKindAndIndexLength = txSerialized.subarray(offset, offset + HEADER_LENGTH + TRANSACTION_KIND_LENGTH + INDEX_LENGTH);
const payloadHeaderKindAndIndexLength = serializeTransactionPayloadsWithDerivationPath(path, headerKindAndIndexLength);
offset += HEADER_LENGTH + TRANSACTION_KIND_LENGTH + INDEX_LENGTH;

let credentialIndex: Buffer[] = [];
let numberOfVerificationKeys: Buffer[] = [];
let keyIndexAndSchemeAndVerificationKey: Buffer[] = [];
let thresholdAndRegIdAndIPIdentity: Buffer[] = [];
let encIdCredPubShareAndKey: Buffer[] = [];
let validToAndCreatedAtAndAttributesLength: Buffer[] = [];
let attributesLength: Buffer[] = [];
let tag: Buffer[][] = [[]];
let valueLength: Buffer[][] = [[]];
let value: Buffer[][] = [[]];
let proofLength: Buffer[] = [];
let proofs: Buffer[] = [];

for (let i = 0; i < txn.payload.newCredentials.length; i++) {
credentialIndex[i] = txSerialized.subarray(offset, offset + INDEX_LENGTH);
offset += INDEX_LENGTH;
numberOfVerificationKeys[i] = txSerialized.subarray(offset, offset + INDEX_LENGTH);
offset += INDEX_LENGTH;
keyIndexAndSchemeAndVerificationKey[i] = txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH + KEY_LENGTH);
offset += 2 * ONE_OCTET_LENGTH + KEY_LENGTH;
thresholdAndRegIdAndIPIdentity[i] = txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH + REG_ID_LENGTH + IP_IDENTITY_LENGTH + AR_DATA_LENGTH);
offset += 2 * ONE_OCTET_LENGTH + REG_ID_LENGTH + IP_IDENTITY_LENGTH + AR_DATA_LENGTH;
encIdCredPubShareAndKey[i] = txSerialized.subarray(offset, offset + 4 * ONE_OCTET_LENGTH + ID_CRED_PUB_SHARE_LENGTH);
offset += 4 * ONE_OCTET_LENGTH + ID_CRED_PUB_SHARE_LENGTH;
validToAndCreatedAtAndAttributesLength[i] = txSerialized.subarray(offset, offset + ATTRIBUTES_LENGTH + VALID_TO_LENGTH + CREATED_AT_LENGTH);
offset += ATTRIBUTES_LENGTH + VALID_TO_LENGTH + CREATED_AT_LENGTH;
attributesLength[i] = validToAndCreatedAtAndAttributesLength[i].subarray(-ATTRIBUTES_LENGTH);
tag[i] = [];
valueLength[i] = [];
value[i] = [];
for (let j = 0; j < attributesLength[i].readUInt16BE(0); j++) {
tag[i].push(txSerialized.subarray(offset, offset + TAG_LENGTH));
offset += TAG_LENGTH;
valueLength[i].push(txSerialized.subarray(offset, offset + VALUE_LENGTH));
offset += VALUE_LENGTH;
value[i].push(txSerialized.subarray(offset, offset + valueLength[i][j].readUInt8(0)));
offset += valueLength[i][j].readUInt8(0);
}

proofLength[i] = txSerialized.subarray(offset, offset + PROOF_LENGTH_LENGTH);
offset += PROOF_LENGTH_LENGTH;
proofs[i] = txSerialized.subarray(offset, offset + proofLength[i].readUInt32BE(0));
offset += proofLength[i].readUInt32BE(0);
}
const credentialIdCount = txSerialized.subarray(offset, offset + ONE_OCTET_LENGTH);
offset += ONE_OCTET_LENGTH;

const credentialIds: Buffer[] = [];
for (let i = 0; i < credentialIdCount.readUInt8(0); i++) {
credentialIds.push(txSerialized.subarray(offset, offset + CREDENTIAL_ID_LENGTH));
offset += CREDENTIAL_ID_LENGTH;
}
const threshold = txSerialized.subarray(offset, offset + ONE_OCTET_LENGTH);
offset += ONE_OCTET_LENGTH;
return { payloadHeaderKindAndIndexLength, credentialIndex, numberOfVerificationKeys, keyIndexAndSchemeAndVerificationKey, thresholdAndRegIdAndIPIdentity, encIdCredPubShareAndKey, validToAndCreatedAtAndAttributesLength, attributesLength, tag, valueLength, value, proofLength, proofs, credentialIdCount, credentialIds, threshold };
};

0 comments on commit de39666

Please sign in to comment.