Bulk approve address book items
POST/api/v1/addressbooks/bulk-approve
Request payload example
{
"vaultId": "bJGB3KUS5h348SyteRSQsf",
"keyId": "fEoEZNvVv4fBVokqgAUzyL",
"signature": "613048c1fef17369daaaccc5ea1547405c0b111cb2b787283cfa1ead61fff47",
"proofs": [
{
"id": "dhH5Qq1bDNaJoDbYrJZpm2",
"proof": [0, 1]
},
{
"id": "cMUqaQrD6vj7dU2s1wBJew",
"proof": [2, 1]
},
{
"id": "mku15vtmjemAZGbrMXsiw6",
"proof": [3]
}
]
}
Payload generation example
const crypto = require('crypto');
const { MerkleTree } = require('merkletreejs');
const LEAF_PREFIX = Buffer.from([0x00]);
const NODE_PREFIX = Buffer.from([0x01]);
function sha256(data) {
return crypto.createHash('sha256').update(data).digest();
}
function hashLeaf(data) {
return sha256(Buffer.concat([LEAF_PREFIX, Buffer.from(data)]));
}
function hashNode(data) {
return sha256(Buffer.concat([NODE_PREFIX, data]));
}
const addressBookItems = [
{ id: 'dhH5Qq1bDNaJoDbYrJZpm2', approvalString: '...'},
{ id: 'cMUqaQrD6vj7dU2s1wBJew', approvalString: '...'},
{ id: 'mku15vtmjemAZGbrMXsiw6', approvalString: '...'},
];
const leaves = addressBookItems.map(item => hashLeaf(item.approvalString));
const tree = new MerkleTree(leaves, hashNode, { sortPairs: true });
const root = tree.getRoot();
// fields order is important
const rootApprovalString = JSON.stringify({
merkleRoot: root.toString('hex'),
vaultId: 'bJGB3KUS5h348SyteRSQsf',
});
const signature = // sign root approval string using private key or web3 signature
const hashIndex = new Map();
const hashes = [];
function getIndex(hex) {
if (!hashIndex.has(hex)) {
hashIndex.set(hex, hashes.length);
hashes.push(hex);
}
return hashIndex.get(hex);
}
const proofs = addressBookItems.map(item => {
const proof = tree.getProof(hashLeaf(item.approvalString)).map((p) => getIndex(p.data.toString('hex')));
return { id: item.id, proof };
});
const payload = {
vaultId: 'bJGB3KUS5h348SyteRSQsf',
keyId: 'fEoEZNvVv4fBVokqgAUzyL',
approval: signature.toString('hex'),
proofs,
};
Specific item verification example
const { createHash } = require('crypto');
const vaultId = '6oRvQaCGheepv62HAvBtJo';
const item = {
id: 'uT3zgWYN1SdVBzoupmWxb3',
approvalString: '{...}',
approval: {
type: 'addressbook-bulk',
keyId: 'gpMTyQfqp5jS9fpjv6xFVr',
approval: {
merkleRoot: '24a16e42a394f6804a63ad721c725ee564e81c719ae50424dde900f1b575e733',
approval:
'5b0c76ca ... 6ce251c21976',
proof: [
'e1b3c8d9a3ec91dba0f26378f428f629c0c1680049134ac518a23afad465d3c1',
'a3a8cd29e1506b9aeb17265bc5a42098828c4474cbad1090092c83d427f3b837',
'77e24107b9be394cc16510a025628bde6e24d921aa3a6028aaa4fc9773e1d61a',
],
},
},
};
const LEAF_PREFIX = Buffer.from([0x00]);
const NODE_PREFIX = Buffer.from([0x01]);
function sha256(data) {
return createHash('sha256').update(data).digest();
}
function hashLeaf(data) {
return sha256(Buffer.concat([LEAF_PREFIX, Buffer.from(data)]));
}
function hashNode(data) {
return sha256(Buffer.concat([NODE_PREFIX, data]));
}
function verifyLevel1(item) {
const { approvalString, approval: { approval } } = item;
let computed = hashLeaf(approvalString);
for (const p of approval.proof) {
const proofHash = Buffer.from(p, 'hex');
computed = hashNode(Buffer.concat([computed, proofHash].sort(Buffer.compare)));
}
return computed.toString('hex') === approval.merkleRoot;
}
function verifyLevel2(item) {
const { approval: { keyId, approval: { merkleRoot, approval } } } = item;
const rootApprovalString = JSON.stringify({
merkleRoot,
vaultId,
});
//
// verify root approval (signature) using key with id keyId depending on type of key
//
return verifyLevel1(item);
}
Request
Responses
- 200
- 400
Address book items bulk approved successfully
Invalid request parameters