Skip to content

Commit

Permalink
feat(issuer): add batch issuing facade
Browse files Browse the repository at this point in the history
  • Loading branch information
josipbagaric committed Jun 16, 2021
1 parent dd0bdd5 commit 4e7f321
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 137 deletions.
14 changes: 8 additions & 6 deletions packages/traceability/issuer/contracts/Issuer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ contract Issuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {
function requestCertificationForBatch(bytes[] memory _data, address[] memory _owners) public returns (uint256[] memory) {
uint256[] memory requestIds = new uint256[](_data.length);

for (uint i = 1; i <= _data.length; i++) {
uint256 id = i + _latestCertificationRequestId;
for (uint256 i = 0; i < _data.length; i++) {
uint256 id = i + _latestCertificationRequestId + 1;

_certificationRequests[id] = CertificationRequest({
owner: _owners[i],
Expand All @@ -95,6 +95,8 @@ contract Issuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {

emit CertificationRequestedBatch(_owners, requestIds);

_latestCertificationRequestId = requestIds[requestIds.length - 1];

return requestIds;
}

Expand All @@ -104,7 +106,7 @@ contract Issuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {

function isRequestValid(uint256 _requestId) external view returns (bool) {
CertificationRequest memory request = _certificationRequests[_requestId];
uint certificateId = requestToCertificate[_requestId];
uint256 certificateId = requestToCertificate[_requestId];

return _requestId <= _latestCertificationRequestId
&& request.approved
Expand Down Expand Up @@ -162,15 +164,15 @@ contract Issuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {
) public returns (uint256[] memory) {
require(_msgSender() == owner() || _msgSender() == privateIssuer, "Issuer::approveCertificationRequestBatch: caller is not the owner or private issuer contract");

for (uint i = 0; i < _requestIds.length; i++) {
for (uint256 i = 0; i < _requestIds.length; i++) {
require(_requestNotApprovedOrRevoked(_requestIds[i]), "Issuer::approveCertificationRequestBatch: request already approved or revoked");
}

address[] memory owners = new address[](_requestIds.length);
bytes[] memory data = new bytes[](_requestIds.length);
bytes[] memory validityData = new bytes[](_requestIds.length);

for (uint i = 0; i < _requestIds.length; i++) {
for (uint256 i = 0; i < _requestIds.length; i++) {
CertificationRequest storage request = _certificationRequests[_requestIds[i]];
request.approved = true;

Expand All @@ -187,7 +189,7 @@ contract Issuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {
validityData
);

for (uint i = 0; i < _requestIds.length; i++) {
for (uint256 i = 0; i < _requestIds.length; i++) {
requestToCertificate[_requestIds[i]] = certificateIds[i];
}

Expand Down
16 changes: 8 additions & 8 deletions packages/traceability/issuer/contracts/PrivateIssuer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ contract PrivateIssuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {
Certification requests
*/

function getCertificateCommitment(uint certificateId) public view returns (bytes32) {
function getCertificateCommitment(uint256 certificateId) public view returns (bytes32) {
return _commitments[certificateId];
}

Expand Down Expand Up @@ -135,18 +135,18 @@ contract PrivateIssuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {
return _requestMigrateToPublicFor(_certificateId, _ownerAddressLeafHash, _msgSender());
}

function getPrivateTransferRequest(uint _certificateId) external view onlyOwner returns (PrivateTransferRequest memory) {
function getPrivateTransferRequest(uint256 _certificateId) external view onlyOwner returns (PrivateTransferRequest memory) {
return _requestPrivateTransferStorage[_certificateId];
}

function getMigrationRequest(uint _requestId) external view onlyOwner returns (RequestStateChange memory) {
function getMigrationRequest(uint256 _requestId) external view onlyOwner returns (RequestStateChange memory) {
return _requestMigrateToPublicStorage[_requestId];
}

function getMigrationRequestId(uint _certificateId) external view onlyOwner returns (uint256 _migrationRequestId) {
function getMigrationRequestId(uint256 _certificateId) external view onlyOwner returns (uint256 _migrationRequestId) {
bool found = false;

for (uint i = 1; i <= _requestMigrateToPublicNonce; i++) {
for (uint256 i = 1; i <= _requestMigrateToPublicNonce; i++) {
if (_requestMigrateToPublicStorage[i].certificateId == _certificateId) {
found = true;
return i;
Expand Down Expand Up @@ -185,7 +185,7 @@ contract PrivateIssuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {

function validateOwnershipProof(
address _ownerAddress,
uint _volume,
uint256 _volume,
string memory _salt,
bytes32 _rootHash,
Proof[] memory _proof
Expand Down Expand Up @@ -222,10 +222,10 @@ contract PrivateIssuer is Initializable, OwnableUpgradeable, UUPSUpgradeable {
emit CommitmentUpdated(_msgSender(), _id, _commitment);
}

function _migrationRequestExists(uint _certificateId) private view returns (bool) {
function _migrationRequestExists(uint256 _certificateId) private view returns (bool) {
bool exists = false;

for (uint i = 1; i <= _requestMigrateToPublicNonce; i++) {
for (uint256 i = 1; i <= _requestMigrateToPublicNonce; i++) {
if (_requestMigrateToPublicStorage[i].certificateId == _certificateId) {
exists = true;
return exists;
Expand Down
55 changes: 21 additions & 34 deletions packages/traceability/issuer/contracts/Registry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,21 @@ contract Registry is ERC1155, ERC1888 {
}

function batchIssue(address _to, bytes memory _issuanceData, uint256 _topic, uint256[] memory _values, bytes[] memory _validityCalls) external override returns (uint256[] memory) {
require(_issuanceData.length == _values.length, "Registry::batchIssueMultiple: _issuanceData and _values arrays have to be the same length");
require(_values.length == _validityCalls.length, "Registry::batchIssueMultiple: _values and _validityCalls arrays have to be the same length");

uint256[] memory _ids = new uint256[](_values.length);

address operator = _msgSender();

for (uint256 i = 1; i <= _values.length; ++i) {
_ids[i] = i + _latestCertificateId;
_validate(operator, _validityCalls[_ids[i]]);
for (uint256 i = 0; i <= _values.length; i++) {
_ids[i] = i + _latestCertificateId + 1;
_validate(operator, _validityCalls[i]);
}

ERC1155._mintBatch(_to, _ids, _values, _issuanceData);

for (uint256 i = 0; i <= _ids.length; ++i) {
for (uint256 i = 0; i < _ids.length; i++) {
certificateStorage[_ids[i]] = Certificate({
topic: _topic,
issuer: operator,
Expand All @@ -58,16 +61,20 @@ contract Registry is ERC1155, ERC1888 {
}

function batchIssueMultiple(address[] memory _to, bytes[] memory _issuanceData, uint256 _topic, uint256[] memory _values, bytes[] memory _validityCalls) external returns (uint256[] memory) {
require(_to.length == _issuanceData.length, "Registry::batchIssueMultiple: _to and _issuanceData arrays have to be the same length");
require(_issuanceData.length == _values.length, "Registry::batchIssueMultiple: _issuanceData and _values arrays have to be the same length");
require(_values.length == _validityCalls.length, "Registry::batchIssueMultiple: _values and _validityCalls arrays have to be the same length");

uint256[] memory _ids = new uint256[](_values.length);

address operator = _msgSender();

for (uint256 i = 0; i < _values.length; ++i) {
_ids[i] = i + _latestCertificateId;
_validate(operator, _validityCalls[_ids[i]]);
for (uint256 i = 0; i < _values.length; i++) {
_ids[i] = i + _latestCertificateId + 1;
_validate(operator, _validityCalls[i]);
}

for (uint256 i = 0; i <= _ids.length; ++i) {
for (uint256 i = 0; i < _ids.length; i++) {
ERC1155._mint(_to[i], _ids[i], _values[i], _issuanceData[i]);

certificateStorage[_ids[i]] = Certificate({
Expand All @@ -78,6 +85,8 @@ contract Registry is ERC1155, ERC1888 {
});
}

_latestCertificateId = _ids[_ids.length - 1];

emit IssuanceBatch(operator, _topic, _ids, _values);

return _ids;
Expand Down Expand Up @@ -125,7 +134,7 @@ contract Registry is ERC1155, ERC1888 {
bytes calldata _data,
bytes[] calldata _claimData
) external override {
uint numberOfClaims = _ids.length;
uint256 numberOfClaims = _ids.length;

require(_to != address(0x0), "Registry::safeBatchTransferAndClaimFrom: _to address must be non-zero.");
require(_ids.length == _values.length, "Registry::safeBatchTransferAndClaimFrom: _ids and _values array length must match.");
Expand All @@ -139,7 +148,7 @@ contract Registry is ERC1155, ERC1888 {

uint256[] memory topics = new uint256[](numberOfClaims);

for (uint256 i = 0; i < numberOfClaims; ++i) {
for (uint256 i = 0; i < numberOfClaims; i++) {
Certificate memory cert = certificateStorage[_ids[i]];
_validate(cert.issuer, cert.validityData);
topics[i] = cert.topic;
Expand All @@ -149,7 +158,7 @@ contract Registry is ERC1155, ERC1888 {
safeBatchTransferFrom(_from, _to, _ids, _values, _data);
}

for (uint256 i = 0; i < numberOfClaims; ++i) {
for (uint256 i = 0; i < numberOfClaims; i++) {
_burn(_to, _ids[i], _values[i]);
}

Expand All @@ -172,35 +181,13 @@ contract Registry is ERC1155, ERC1888 {

uint256[] memory batchClaimBalances = new uint256[](_owners.length);

for (uint256 i = 0; i < _owners.length; ++i) {
for (uint256 i = 0; i < _owners.length; i++) {
batchClaimBalances[i] = this.claimedBalanceOf(_owners[i], _ids[i]);
}

return batchClaimBalances;
}

/**
* Modification to the OpenZeppelin ERC-1155 to support to[] addresses
*/
// function _mintBatch(address[] to, uint256[] memory ids, uint256[] memory amounts, bytes[] memory data, bytes[] memory validityData) internal override {
// require(to.length == ids.length, "ERC1155: to and ids length mismatch");
// require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

// address operator = _msgSender();

// for (uint i = 0; i < ids.length; i++) {
// require(_to != address(0), "ERC1155: mint to the zero address");
// _validate(operator, _validityData[i]);
// }

// for (uint i = 0; i < ids.length; i++) {
// _balances[ids[i]][to[i]] += amounts[i];

// emit TransferSingle(operator, address(0), to[i], ids[i], amounts[i]);
// _doSafeTransferAcceptanceCheck(operator, address(0), to[i], ids[i], amounts[i], data);
// }
// }

function _burn(address _from, uint256 _id, uint256 _value) internal override {
ERC1155._burn(_from, _id, _value);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export interface IClaimData {

export interface IData {
deviceId: string;
generationStartTime: number;
generationEndTime: number;
generationStartTime: Timestamp;
generationEndTime: Timestamp;
metadata: string;
}

Expand All @@ -50,7 +50,7 @@ export interface ICertificate extends IData {
id: number;
issuer: string;
certificationRequestId: number;
creationTime: number;
creationTime: Timestamp;
creationBlockHash: string;
owners: IShareInCertificate;
claimers: IShareInCertificate;
Expand All @@ -59,13 +59,13 @@ export interface ICertificate extends IData {
export class Certificate implements ICertificate {
public deviceId: string;

public generationStartTime: number;
public generationStartTime: Timestamp;

public generationEndTime: number;
public generationEndTime: Timestamp;

public issuer: string;

public creationTime: number;
public creationTime: Timestamp;

public creationBlockHash: string;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { BigNumber, ContractTransaction, utils } from 'ethers';

import { IBlockchainProperties } from './BlockchainProperties';
import { Certificate, IClaimData, IData } from './Certificate';
import { encodeClaimData, encodeData } from './CertificateUtils';

interface CertificateInfoInBatch extends IData {
to: string;
amount: BigNumber;
}

export async function issueCertificates(
certificateInfo: CertificateInfoInBatch[],
blockchainProperties: IBlockchainProperties
): Promise<Certificate['id'][]> {
const { issuer, registry, activeUser } = blockchainProperties;
const issuerWithSigner = issuer.connect(activeUser);

const data = certificateInfo.map((info) =>
encodeData({
generationStartTime: info.generationStartTime,
generationEndTime: info.generationEndTime,
deviceId: info.deviceId,
metadata: info.metadata
})
);

const batchIssueTx = await issuerWithSigner.issueBatch(
certificateInfo.map((info) => info.to),
certificateInfo.map((info) => info.amount),
data
);

const { events } = await batchIssueTx.wait();

let issuanceEvent: utils.LogDescription;

for (const event of events) {
try {
issuanceEvent = issuer.interface.parseLog(event);
} catch (e) {
issuanceEvent = registry.interface.parseLog(event);
}

if (issuanceEvent.name === 'IssuanceBatch') {
break;
}
}

return issuanceEvent.args[2].map((id: BigNumber) => id.toNumber());
}

export async function transferCertificates(
certificateIds: number[],
to: string,
blockchainProperties: IBlockchainProperties,
from?: string
): Promise<ContractTransaction> {
const certificatesPromises = certificateIds.map((certId) =>
new Certificate(certId, blockchainProperties).sync()
);

const { registry, activeUser } = blockchainProperties;
const registryWithSigner = registry.connect(activeUser);

const activeUserAddress = await activeUser.getAddress();
const fromAddress = from ?? activeUserAddress;

const certificates = await Promise.all(certificatesPromises);

const values = certificates.map((cert) => BigNumber.from(cert.owners[fromAddress] ?? 0));

const transferTx = await registryWithSigner.safeBatchTransferFrom(
fromAddress,
to,
certificateIds,
values,
utils.randomBytes(32) // TO-DO: replace with proper data
);

await transferTx.wait();

return transferTx;
}

export async function claimCertificates(
certificateIds: number[],
claimData: IClaimData,
blockchainProperties: IBlockchainProperties,
forAddress?: string
): Promise<ContractTransaction> {
const certificatesPromises = certificateIds.map((certId) =>
new Certificate(certId, blockchainProperties).sync()
);
const certificates = await Promise.all(certificatesPromises);

const { activeUser, registry } = blockchainProperties;
const claimer = forAddress ?? (await activeUser.getAddress());

const values = certificates.map((cert) => BigNumber.from(cert.owners[claimer] ?? 0));

const encodedClaimData = encodeClaimData(claimData);
const data = utils.randomBytes(32);

const registryWithSigner = registry.connect(activeUser);

const claimTx = await registryWithSigner.safeBatchTransferAndClaimFrom(
claimer,
claimer,
certificateIds,
values,
data,
certificates.map(() => encodedClaimData)
);

await claimTx.wait();

return claimTx;
}
Loading

0 comments on commit 4e7f321

Please sign in to comment.