Skip to content

Commit

Permalink
#108 Revocation Registry: fix typos + add new endorsed method
Browse files Browse the repository at this point in the history
Signed-off-by: Guilherme Funchal da Silva <[email protected]>
  • Loading branch information
guilherme-funchal committed Jan 7, 2025
1 parent 2f724c5 commit 311847f
Show file tree
Hide file tree
Showing 11 changed files with 548 additions and 168 deletions.
40 changes: 40 additions & 0 deletions smart_contracts/contracts-ts/RevocationRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,25 @@ export class RevocationRegistry extends Contract {
return tx.wait()
}

public async createRevocationRegistryEntrySigned(
identity: string,
revRegDefId: string,
issuerId: string,
revRegEntry: RevocationRegistryEntryStruct,
signature: Signature,
) {
const tx = await this.instance.createRevocationRegistryEntrySigned(
identity,
signature.v,
signature.r,
signature.s,
keccak256(toUtf8Bytes(revRegDefId)),
issuerId,
revRegEntry,
)
return tx.wait()
}

public async resolveRevocationRegistryDefinition(id: string): Promise<RevocationRegistryDefinitionRecord> {
const record = await this.instance.resolveRevocationRegistryDefinition(keccak256(toUtf8Bytes(id)))
return {
Expand Down Expand Up @@ -114,4 +133,25 @@ export class RevocationRegistry extends Contract {
]),
)
}

public signCreateRevRegEntryEndorsementData(
identity: string,
privateKey: Uint8Array,
revRegDefId: string,
issuerId: string,
revRegEntry: RevocationRegistryEntryStruct,
) {
const revRegEntrySolidityStruct = ['tuple(bytes,bytes,uint32[],uint32[],uint64)']

return this.signEndorsementData(
privateKey,
concat([
identity,
toUtf8Bytes('createRevocationRegistryEntry'),
getBytes(keccak256(toUtf8Bytes(revRegDefId)), 'hex'),
toUtf8Bytes(issuerId),
getBytes(new AbiCoder().encode(revRegEntrySolidityStruct, [Object.values(revRegEntry)])),
]),
)
}
}
79 changes: 46 additions & 33 deletions smart_contracts/contracts/anoncreds/RevocationRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { NotRevocationRegistryDefinitionIssuer, RevocationRegistryDefinitionAlre
import { CredentialDefinitionRegistryInterface } from "./CredentialDefinitionRegistryInterface.sol";
import { RoleControlInterface } from "../auth/RoleControl.sol";
import { AnoncredsRegistry } from "./AnoncredsRegistry.sol";
import { StringUtils } from "../utils/StringUtils.sol";

import { Errors } from "../utils/Errors.sol";

Expand All @@ -26,7 +27,7 @@ contract RevocationRegistry is RevocationRegistryInterface, ControlledUpgradeabl
mapping(bytes32 id => RevocationRegistryDefinitionRecord revocationRegistryDefinitionRecord) private _revRegDefs;

/**
* Checks that the schema exist
* Checks that the Credential Definition exist
*/
modifier _credentialDefinitionExists(bytes32 id) {
_credentialDefinitionRegistry.resolveCredentialDefinition(id);
Expand All @@ -49,34 +50,21 @@ contract RevocationRegistry is RevocationRegistryInterface, ControlledUpgradeabl
_;
}

modifier _accumulatorsMatch(bytes32 _revRegDefId, RevocationRegistryEntry calldata initialRevRegEntry) {
if (
_revRegDefs[_revRegDefId].metadata.currentAccumulator.length != 0 &&
keccak256(abi.encodePacked(_revRegDefs[_revRegDefId].metadata.currentAccumulator)) !=
keccak256(abi.encodePacked(initialRevRegEntry.prevAccumulator))
) revert AccumulatorMismatch(initialRevRegEntry.prevAccumulator);
_;
}

//TODO: using as modifier caused 'Stack Too Deep' Compile Error
// modifier _isRevRegDefIssuer(bytes32 revRegDefId, string calldata issuerId) {
// if (
// keccak256(abi.encodePacked(_revRegDefs[revRegDefId].metadata.issuerId)) !=
// keccak256(abi.encodePacked(issuerId))
// ) revert NotRevocationRegistryDefinitionIssuer(issuerId);
// _;
// }

//TODO:
// Seems odd but keep in mind...
//https://github.com/hyperledger/indy-node/blob/main/design/anoncreds.md#revoc_reg_entry
// Creation of Revocation Registry (Def and Enteries):
// RevocReg Issuer may not be the same as Schema Author and CredDef issuer.
/**
* Checks wether Credential Definition Exists
* Checks that the previous accumulator specified by the new Revocation Registry Entry matches
* the current accumulator on chain (only if not the first entry)
*/
modifier _credDefExists(bytes32 credDefId) {
_credentialDefinitionRegistry.resolveCredentialDefinition(credDefId);
modifier _accumulatorsMatch(bytes32 _revRegDefId, RevocationRegistryEntry calldata _revRegEntry) {
if (_revRegDefs[_revRegDefId].metadata.currentAccumulator.length != 0) {
if (_revRegDefs[_revRegDefId].metadata.currentAccumulator.length != _revRegEntry.prevAccumulator.length) {
revert AccumulatorMismatch(_revRegEntry.prevAccumulator);
}
for (uint256 i = 0; i < _revRegEntry.prevAccumulator.length; i++) {
if (_revRegDefs[_revRegDefId].metadata.currentAccumulator[i] != _revRegEntry.prevAccumulator[i]) {
revert AccumulatorMismatch(_revRegEntry.prevAccumulator);
}
}
}
_;
}

Expand Down Expand Up @@ -137,6 +125,7 @@ contract RevocationRegistry is RevocationRegistryInterface, ControlledUpgradeabl
);
}

/// @inheritdoc RevocationRegistryInterface
function resolveRevocationRegistryDefinition(
bytes32 id
)
Expand All @@ -149,6 +138,7 @@ contract RevocationRegistry is RevocationRegistryInterface, ControlledUpgradeabl
return _revRegDefs[id];
}

/// @inheritdoc RevocationRegistryInterface
function createRevocationRegistryEntry(
address identity,
bytes32 revRegDefId,
Expand All @@ -158,6 +148,31 @@ contract RevocationRegistry is RevocationRegistryInterface, ControlledUpgradeabl
_createRevocationRegistryEntry(identity, msg.sender, revRegDefId, issuerId, revRegEntry);
}

/// @inheritdoc RevocationRegistryInterface
function createRevocationRegistryEntrySigned(
address identity,
uint8 sigV,
bytes32 sigR,
bytes32 sigS,
bytes32 revRegDefId,
string calldata issuerId,
RevocationRegistryEntry calldata revRegEntry
) public virtual {
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0x19),
bytes1(0),
address(this),
identity,
"createRevocationRegistryEntry",
revRegDefId,
issuerId,
abi.encode(revRegEntry)
)
);
_createRevocationRegistryEntry(identity, ecrecover(hash, sigV, sigR, sigS), revRegDefId, issuerId, revRegEntry);
}

function _createRevocationRegistryDefinition(
address identity,
address actor,
Expand All @@ -170,7 +185,7 @@ contract RevocationRegistry is RevocationRegistryInterface, ControlledUpgradeabl
_senderIsTrusteeOrEndorserOrSteward
_uniqueRevRegDefId(id)
_validIssuer(issuerId, identity, actor)
_credDefExists(credDefId)
_credentialDefinitionExists(credDefId)
{
_revRegDefs[id].revRegDef = revRegDef;
_revRegDefs[id].metadata.created = block.timestamp;
Expand All @@ -192,11 +207,9 @@ contract RevocationRegistry is RevocationRegistryInterface, ControlledUpgradeabl
_validIssuer(issuerId, identity, actor)
_accumulatorsMatch(revRegDefId, revRegEntry)
{
//TODO: using as modifier caused 'Stack too deep' compile error
if (
keccak256(abi.encodePacked(_revRegDefs[revRegDefId].metadata.issuerId)) !=
keccak256(abi.encodePacked(issuerId))
) revert NotRevocationRegistryDefinitionIssuer(issuerId);
if (!StringUtils.equals(_revRegDefs[revRegDefId].metadata.issuerId, issuerId)) {
revert NotRevocationRegistryDefinitionIssuer(issuerId);
}

_revRegDefs[revRegDefId].metadata.currentAccumulator = revRegEntry.currentAccumulator;
emit RevocationRegistryEntryCreated(revRegDefId, revRegEntry.timestamp, revRegEntry);
Expand Down
138 changes: 125 additions & 13 deletions smart_contracts/contracts/anoncreds/RevocationRegistryInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,42 @@ interface RevocationRegistryInterface {
*/
event RevocationRegistryDefinitionCreated(bytes32 revocationRegistryDefinitionId, address identity);

/**
* @dev Event that is sent when a Revocation Registry Entry (Delta) is created
*
* @param revocationRegistryDefinitionId Keccak hash of created revocation registry definition id
* @param timestamp Timestamp of the created Revocation Registry Entry
* @param revRegEntry Struct containing new accumulator and list of newly issued/revoked credentials
*/
event RevocationRegistryEntryCreated(
bytes32 indexed revocationRegistryDefinitionId,
uint64 indexed timestamp,
RevocationRegistryEntry revRegEntry
);

/**
* @dev Creates a new Revocation Registry Definition.
*
* Once the Revocation Registry Definition is created, this function emits a `RevocationRegistryDefinitionCreated` event
* with the new Revocation Registry Definition's ID and issuer address.
*
* Restrictions:
* - Only senders with either TRUSTEE or ENDORSER or STEWARD role are permitted to create new object;
*
* This function can revert with following errors:
* - `RevocationRegistryDefinitionAlreadyExist`: Raised if Revocation Registry Definition with provided ID already exist.
* - `CredentialDefinitionNotFound`: Raised if the associated Credential Definition doesn't exist.
* - `IssuerNotFound`: Raised if the associated issuer doesn't exist.
* - `InvalidIssuerId`: Raised if the provided issuer DID is invalid.
* - `IssuerHasBeenDeactivated`: Raised if the associated issuer is not active.
* - `NotIdentityOwner`: Raised when specified issuer DID is not owned by sender.
*
* @param identity Account address of Revocation Registry Definition issuer.
* @param id Keccak hash of Revocation Registry Id to be created.
* @param credDefId Keccak hash of Credential Definition Id.
* @param issuerId DID of Revocation Registry Definition issuer.
* @param revRegDef AnonCreds Revocation Registry Definition JSON as bytes.
*/
function createRevocationRegistryDefinition(
address identity,
bytes32 id,
Expand All @@ -26,6 +56,32 @@ interface RevocationRegistryInterface {
bytes calldata revRegDef
) external;

/**
* @dev Endorse a new Revocation Registry Definition (off-chain author signature).
*
* Once the Revocation Registry Definition is created, this function emits a `RevocationRegistryDefinitionCreated` event
* with the new Revocation Registry Definition's ID and issuer address.
*
* Restrictions:
* - Only senders with either TRUSTEE or ENDORSER or STEWARD role are permitted to create new object;
*
* This function can revert with following errors:
* - `RevocationRegistryDefinitionAlreadyExist`: Raised if Revocation Registry Definition with provided ID already exist.
* - `CredentialDefinitionNotFound`: Raised if the associated Credential Definition doesn't exist.
* - `IssuerNotFound`: Raised if the associated issuer doesn't exist.
* - `InvalidIssuerId`: Raised if the provided issuer DID is invalid.
* - `IssuerHasBeenDeactivated`: Raised if the associated issuer is not active.
* - `NotIdentityOwner`: Raised when specified issuer DID is not owned by sender.
*
* @param identity Account address of credential definition issuer.
* @param sigR Part of EcDSA signature.
* @param sigV Part of EcDSA signature.
* @param sigS Part of EcDSA signature.
* @param id Keccak hash of Credential Definition id to be created.
* @param issuerId DID of Revocation Registry Definition issuer.
* @param credDefId Keccak hash of Credential Definition id.
* @param revRegDef AnonCreds Revocation Registry Definition JSON as bytes.
*/
function createRevocationRegistryDefinitionSigned(
address identity,
uint8 sigV,
Expand All @@ -37,28 +93,84 @@ interface RevocationRegistryInterface {
bytes calldata revRegDef
) external;

// /**
// * @dev Resolve the Revocation Registry Definition associated with the given ID.
// *
// * If no matching Revocation Registry Definition is found, the function revert with `RevocationRegistryDefinitionNotFound` error
// *
// * @param id Keccak hash of the Revocation Registry Definition to be resolved.
// *
// * @return revocationRegistryDefinitionRecord Returns the Revocation Registry Definition with metadata.
// */
function resolveRevocationRegistryDefinition(
bytes32 id
) external returns (RevocationRegistryDefinitionRecord memory revocationRegistryDefinitionRecord);

/**
* @dev Creates a new Revocation Registry Entry (Delta).
*
* Once the Revocation Registry Entry is created, this function emits a `RevocationRegistryEntryCreated` event
* with the Revocation Registry Definition's ID, timestamp and a struct containing the new accumulator and issued/revoked
* credentials
*
* Restrictions:
* - Only senders with either TRUSTEE or ENDORSER or STEWARD role are permitted to create new object;
* - Only the issuer of the associated Revocation Registry Definition is permitted to create new object;
*
* This function can revert with following errors:
* - `RevocationRegistryNotFound`: Raised if the associated Revocation Registry Definition doesn't exist.
* - `IssuerNotFound`: Raised if the associated issuer doesn't exist.
* - `InvalidIssuerId`: Raised if the provided issuer DID is invalid.
* - `IssuerHasBeenDeactivated`: Raised if the associated issuer is not active.
* - `NotIdentityOwner`: Raised when specified issuer DID is not owned by sender.
* - `NotRevocationRegistryDefinitionIssuer`: Raised when trying to create object while not being issuer of associated Revocation Registry Definition.
*
* @param identity Account address of Revocation Registry Definition issuer.
* @param revRegDefId Keccak hash of the associated Revocation Registry Id.
* @param issuerId DID of Revocation Registry Definition issuer.
* @param revRegEntry Struct with new and previous accumulators, list of issued/revoked credentials and timestamp.
*/
function createRevocationRegistryEntry(
address identity,
bytes32 revRegDefId,
string calldata issuerId,
RevocationRegistryEntry calldata revRegEntry
) external;

//TODO:
// /**
// * @dev Resolve the Revocation Registry Definition associated with the given ID.
// *
// * If no matching Revocation Registry Definition is found, the function revert with `RevocationRegistryDefinitionNotFound` error
// *
// * @param id Keccak hash of the Revocation Registry Definition to be resolved.
// *
// * @return revocationRegistryDefinitionRecord Returns the credential definition with metadata.
// */
// function resolveRevocationRegistryDefinition(
// bytes32 id
// ) external returns (RevocationRegistryDefinitionRecord memory revocationRegistryDefinitionRecord);
/**
* @dev Endorse a new Revocation Registry Entry (off-chain author signature).
*
* Once the Revocation Registry Entry is created, this function emits a `RevocationRegistryEntryCreated` event
* with the Revocation Registry Definition's ID, timestamp and a struct containing the new accumulator and issued/revoked
* credentials
*
* Restrictions:
* - Only senders with either TRUSTEE or ENDORSER or STEWARD role are permitted to create new object;
* - Only the issuer of the associated Revocation Registry Definition is permitted to create new object;
*
* This function can revert with following errors:
* - `RevocationRegistryNotFound`: Raised if the associated Revocation Registry Definition doesn't exist.
* - `IssuerNotFound`: Raised if the associated issuer doesn't exist.
* - `InvalidIssuerId`: Raised if the provided issuer DID is invalid.
* - `IssuerHasBeenDeactivated`: Raised if the associated issuer is not active.
* - `NotIdentityOwner`: Raised when specified issuer DID is not owned by sender.
* - `NotRevocationRegistryDefinitionIssuer`: Raised when trying to create object while not being issuer of associated Revocation Registry Definition.
*
* @param identity Account address of credential definition issuer.
* @param sigR Part of EcDSA signature.
* @param sigV Part of EcDSA signature.
* @param sigS Part of EcDSA signature.
* @param revRegDefId Keccak hash of the associated Revocation Registry Id.
* @param issuerId DID of Revocation Registry Definition issuer.
* @param revRegEntry Struct with new and previous accumulators, list of issued/revoked credentials and timestamp.
*/
function createRevocationRegistryEntrySigned(
address identity,
uint8 sigV,
bytes32 sigR,
bytes32 sigS,
bytes32 revRegDefId,
string calldata issuerId,
RevocationRegistryEntry calldata revRegEntry
) external;
}
11 changes: 10 additions & 1 deletion smart_contracts/contracts/anoncreds/RevocationRegistryTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,18 @@ struct RevocationRegistryDefinitionMetadata {
uint256 created;
string issuerId;
bytes currentAccumulator;
//TODO: Add timestamp for on chain control as well?
}

/**
* @title RevocationRegistryEntry
* @dev This struct holds the data of a new revocation registry entry (delta).
*
* @param currentAccumulator - New accumulator to be saved on-chain.
* @param prevAccumulator - Previous accumulator for comparison.
* @param issued - list of newly issued credential indexes.
* @param revoked - list of newly revoked credential indexes.
* @param timestamp - timestamp of revocation registry entry (delta).
*/
struct RevocationRegistryEntry {
bytes currentAccumulator;
bytes prevAccumulator;
Expand Down
6 changes: 6 additions & 0 deletions smart_contracts/demos/flow-with-did-ethr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ async function demo() {
)

console.log(`Revocation Registry Entry created for Revocation Registry Definition id ${revocationRegistryId}. Receipt: ${JSON.stringify(receipt)}`)

console.log("12. Faber fetches Revocation Registry Entries associated with a Test Revocation Registry Definition")
const revRegEntries = await faber.revocationRegistry.fetchAllRevocationEntries(revocationRegistryId);

console.log(`All Revocation Registry Entries found for Revocation Registry Definition id ${revocationRegistryId}: `)
console.log(revRegEntries)
}

if (require.main === module) {
Expand Down
Loading

0 comments on commit 311847f

Please sign in to comment.