squid-js/src/keeper/contracts/templates/AgreementTemplateBase.ts

317 lines
9.7 KiB
TypeScript

import {
TemplateStoreManager,
AgreementStoreManager,
ConditionStoreManager
} from '../managers'
import DIDRegistry from '../DIDRegistry'
import { LockRewardCondition } from '../conditions/LockRewardCondition'
import { AccessSecretStoreCondition } from '../conditions/AccessSecretStoreCondition'
import { EscrowReward } from '../conditions/EscrowReward'
import { DDO } from '../../../ddo/DDO'
import { generateId, LoggerInstance, Logger, zeroX } from '../../../utils'
import {
ComputeExecutionCondition,
Condition,
ConditionState,
conditionStateNames
} from '../conditions'
import { ServiceType } from '../../../ddo/Service'
export interface Conditions {
lockRewardCondition: LockRewardCondition
accessSecretStoreCondition?: AccessSecretStoreCondition
computeExecutionCondition?: ComputeExecutionCondition
escrowReward: EscrowReward
}
export interface AgreementConditionsStatus {
[condition: string]: {
condition: string
contractName: string
state: ConditionState
blocked: boolean
blockedBy: string[]
}
}
export class AgreementTemplateBase {
public templateName: string
public templateManager: TemplateStoreManager
public agreementStoreManager: AgreementStoreManager
public didRegistry: DIDRegistry
public conditions: Conditions
private logger: Logger
public constructor(
templateManager: TemplateStoreManager,
agreementStoreManager: AgreementStoreManager,
didRegistry: DIDRegistry,
conditions: Conditions
) {
this.templateManager = templateManager
this.agreementStoreManager = agreementStoreManager
this.didRegistry = didRegistry
this.conditions = conditions
this.logger = LoggerInstance
this.templateName = 'invalid'
}
public async createAgreementFromDDO(
agreementId: string,
ddo: DDO,
serviceType: ServiceType,
consumer: string,
provider: string,
from?: string
) {
return !!(await this.createFullAgreement(
ddo.shortId(),
ddo.findServiceByType(serviceType).attributes.main.price,
consumer,
provider,
from,
agreementId
))
}
public async getConditionIdsFromDDO(
agreementId: string,
ddo: DDO,
consumer: string,
from?: string
) {
return this.createFullAgreementData(
agreementId,
ddo.shortId(),
ddo.findServiceByType('metadata').attributes.main.price,
consumer
)
}
public getName() {
return this.templateName
}
public getId() {
return this.templateManager.generateId(this.getName())
}
/**
* Create a agreement using EscrowAccessSecretStoreTemplate using only the most important information.
* @param {string} did Asset DID.
* @param {number} amount Asset price.
* @param {string} consumer ethereum address of consumer.
* @param {string} provider ethereum address of service provider (brizo address)
* @param {string} from Consumer address.
* @param {string} agreementId bytes32 agreement id.
*
* @return {Promise<string>} Agreement ID.
*/
public async createFullAgreement(
did: string,
amount: number | string,
consumer: string,
provider: string,
from?: string,
agreementId: string = generateId()
): Promise<string> {
const conditionIds = await this.createFullAgreementData(
agreementId,
did,
amount,
consumer
)
const timeouts = [0, 0, 0]
const timelocks = [0, 0, 0]
await this.agreementStoreManager.createAgreement(
agreementId,
did,
this.getId(),
conditionIds,
timelocks,
timeouts,
[consumer, provider],
from
)
return zeroX(agreementId)
}
protected async createFullAgreementData(
agreementId: string,
did: string,
amount: number | string,
consumer: string
): Promise<string[]> {
return null
}
/**
* Conditions address list.
* @return {Promise<string[]>} Conditions address.
*/
public async getConditionTypes(): Promise<string[]> {
return this.templateManager.getConditionTypes(this.getId())
}
/**
* List of condition contracts.
* @return {Promise<Condition[]>} Conditions contracts.
*/
public async getConditions(): Promise<Condition[]> {
return this.templateManager.getConditions(await this.getConditionTypes())
}
public async getServiceAgreementTemplate() {
return null
}
public async getServiceAgreementTemplateConditions() {
const serviceAgreementTemplate = await this.getServiceAgreementTemplate()
return serviceAgreementTemplate.conditions
}
public async getServiceAgreementTemplateConditionByRef(ref: string) {
const name = (await this.getServiceAgreementTemplateConditions()).find(
({ name: conditionRef }) => conditionRef === ref
).contractName
return (await this.getConditions()).find(
(condition) => condition.contractName === name
)
}
public async getServiceAgreementTemplateDependencies() {
const serviceAgreementTemplate = await this.getServiceAgreementTemplate()
return serviceAgreementTemplate.conditionDependency
}
/**
* Returns the status of the conditions.
* @param {string} agreementId Agreement ID.
* @param {ConditionStoreManager} conditionStoreManager
* @return {Promise} Conditions status.
*/
public async getAgreementStatus(
agreementId: string,
conditionStoreManager: ConditionStoreManager
): Promise<AgreementConditionsStatus | false> {
const dependencies = await this.getServiceAgreementTemplateDependencies()
const { conditionIds } = await this.agreementStoreManager.getAgreement(
agreementId
)
if (!conditionIds.length) {
// this.logger.error(`Agreement not creeated yet: "${agreementId}"`)
return false
}
const conditionIdByCondition = (await this.getConditions()).reduce(
(acc, { contractName }, i) => ({
...acc,
[contractName]: conditionIds[i]
}),
{}
)
const statesPromises = Object.keys(dependencies).map(async (ref, i) => {
const { contractName } = await this.getServiceAgreementTemplateConditionByRef(
ref
)
return {
ref,
contractName,
state: (
await conditionStoreManager.getCondition(
conditionIdByCondition[contractName]
)
).state
}
})
const states = await Promise.all(statesPromises)
return states.reduce((acc, { contractName, ref, state }) => {
const blockers = dependencies[ref]
.map((dependency) => states.find((_) => _.ref === dependency))
.filter((condition) => condition.state !== ConditionState.Fulfilled)
return {
...acc,
[ref]: {
condition: ref,
contractName,
state,
blocked: !!blockers.length,
blockedBy: blockers.map((_) => _.ref)
}
}
}, {})
}
/**
* Prints the agreement status.
* @param {string} agreementId Agreement ID.
* @param {ConditionStoreManager} conditionStoreManager
*/
public async printAgreementStatus(
agreementId: string,
conditionStoreManager: ConditionStoreManager
) {
const status = await this.getAgreementStatus(agreementId, conditionStoreManager)
this.logger.bypass('-'.repeat(80))
this.logger.bypass('Template:', this.templateName)
this.logger.bypass('Agreement ID:', agreementId)
this.logger.bypass('-'.repeat(40))
if (!status) {
this.logger.bypass('Agreement not created yet!')
}
Object.values(status || []).forEach(
({ condition, contractName, state, blocked, blockedBy }, i) => {
if (i) {
this.logger.bypass('-'.repeat(20))
}
this.logger.bypass(`${condition} (${contractName})`)
this.logger.bypass(' Status:', state, `(${conditionStateNames[state]})`)
if (blocked) {
this.logger.bypass(' Blocked by:', blockedBy)
}
}
)
this.logger.bypass('-'.repeat(80))
}
/**
* Generates and returns the agreement creation event.
* @param {string} agreementId Agreement ID.
* @return {Event} Agreement created event.
*/
public getAgreementCreatedEvent(agreementId: string) {
return this.agreementStoreManager.getAgreementCreatedEvent(agreementId)
}
/**
* Return actor type ids for this template (specified by subclass)
*
*/
public async getActorTypeIds() {
const { actorTypeIds } = await this.templateManager.getTemplate(this.getId())
return actorTypeIds
}
/**
* Return actor types (strings) for this template (specified by subclass)
*
*/
public async getActorTypes() {
const actorTypeIds = await this.getActorTypeIds()
return actorTypeIds.map((typeId) =>
this.templateManager.getActorTypeValue(typeId)
)
}
}