tutus-chain/pkg/core/native/pons.go

1261 lines
44 KiB
Go

package native
import (
"encoding/binary"
"errors"
"math/big"
"github.com/tutus-one/tutus-chain/pkg/config"
"github.com/tutus-one/tutus-chain/pkg/core/dao"
"github.com/tutus-one/tutus-chain/pkg/core/interop"
"github.com/tutus-one/tutus-chain/pkg/core/native/nativeids"
"github.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
"github.com/tutus-one/tutus-chain/pkg/core/state"
"github.com/tutus-one/tutus-chain/pkg/smartcontract"
"github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag"
"github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest"
"github.com/tutus-one/tutus-chain/pkg/util"
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)
// Pons ("bridge" in Latin) represents the Inter-Government Bridge Protocol
// native contract. It manages:
// - Bilateral agreements between sovereign chains
// - Cross-border verification requests
// - International VTS settlement
// - Education and healthcare credential portability
type Pons struct {
interop.ContractMD
Tutus ITutus
Vita IVita
Federation *Federation
RoleRegistry *RoleRegistry
VTS *VTS
Scire *Scire
Salus *Salus
}
// Storage key prefixes for Pons.
const (
ponsPrefixConfig byte = 0x01 // -> PonsConfig
ponsPrefixAgreement byte = 0x10 // agreementID -> BilateralAgreement
ponsPrefixAgreementByChain byte = 0x11 // chainID + agreementID -> exists
ponsPrefixAgreementCounter byte = 0x1F // -> next agreementID
ponsPrefixVerification byte = 0x20 // requestID -> VerificationRequest
ponsPrefixVerifBySubject byte = 0x21 // subject + requestID -> exists
ponsPrefixVerifCounter byte = 0x2F // -> next verificationID
ponsPrefixSettlement byte = 0x30 // settlementID -> SettlementRequest
ponsPrefixSettlByChain byte = 0x31 // chainID + settlementID -> exists
ponsPrefixSettlCounter byte = 0x3F // -> next settlementID
ponsPrefixCredential byte = 0x40 // credentialID -> CredentialShare
ponsPrefixCredByOwner byte = 0x41 // owner + credentialID -> exists
ponsPrefixCredCounter byte = 0x4F // -> next credentialID
)
// Default configuration values.
const (
defaultLocalChainID uint32 = 1
defaultVerificationTimeout uint32 = 8640 // ~1 day at 10s blocks
defaultSettlementTimeout uint32 = 86400 // ~10 days
defaultMaxPendingRequests uint64 = 10000
defaultCredentialShareExpiry uint32 = 315360 // ~1 year
)
// Event names for Pons.
const (
AgreementCreatedEvent = "AgreementCreated"
AgreementUpdatedEvent = "AgreementUpdated"
AgreementTerminatedEvent = "AgreementTerminated"
VerificationRequestedEvent = "VerificationRequested"
VerificationRespondedEvent = "VerificationResponded"
SettlementRequestedEvent = "SettlementRequested"
SettlementCompletedEvent = "SettlementCompleted"
CredentialSharedEvent = "CredentialShared"
CredentialRevokedEvent = "CredentialRevoked"
)
// Various errors for Pons.
var (
ErrNoAgreement = errors.New("no active agreement with target chain")
ErrAgreementExists = errors.New("agreement already exists")
ErrAgreementNotFound = errors.New("agreement not found")
ErrInvalidAgreementType = errors.New("invalid agreement type")
ErrAgreementNotActive = errors.New("agreement not active")
ErrVerificationNotFound = errors.New("verification request not found")
ErrVerificationExpired = errors.New("verification request expired")
ErrSettlementNotFound = errors.New("settlement request not found")
ErrSettlementExpired = errors.New("settlement request expired")
ErrCredentialNotFound = errors.New("credential share not found")
ErrCredentialExpired = errors.New("credential share expired")
ErrCredentialRevoked = errors.New("credential share revoked")
ErrMaxRequestsReached = errors.New("maximum pending requests reached")
ErrNotCredentialOwner = errors.New("not credential owner")
ErrAgreementTypeNotAllowed = errors.New("agreement type not allowed for this operation")
)
var _ interop.Contract = (*Pons)(nil)
// newPons creates a new Pons native contract.
func newPons() *Pons {
p := &Pons{
ContractMD: *interop.NewContractMD(nativenames.Pons, nativeids.Pons),
}
defer p.BuildHFSpecificMD(p.ActiveIn())
// getConfig method
desc := NewDescriptor("getConfig", smartcontract.ArrayType)
md := NewMethodAndPrice(p.getConfig, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// setLocalChainID method (committee only)
desc = NewDescriptor("setLocalChainID", smartcontract.BoolType,
manifest.NewParameter("chainID", smartcontract.IntegerType))
md = NewMethodAndPrice(p.setLocalChainID, 1<<16, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// --- Agreement Management ---
// createAgreement method (committee only)
desc = NewDescriptor("createAgreement", smartcontract.IntegerType,
manifest.NewParameter("remoteChainID", smartcontract.IntegerType),
manifest.NewParameter("agreementType", smartcontract.IntegerType),
manifest.NewParameter("termsHash", smartcontract.Hash256Type),
manifest.NewParameter("expirationHeight", smartcontract.IntegerType))
md = NewMethodAndPrice(p.createAgreement, 1<<17, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// updateAgreementStatus method (committee only)
desc = NewDescriptor("updateAgreementStatus", smartcontract.BoolType,
manifest.NewParameter("agreementID", smartcontract.IntegerType),
manifest.NewParameter("newStatus", smartcontract.IntegerType))
md = NewMethodAndPrice(p.updateAgreementStatus, 1<<16, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// getAgreement method
desc = NewDescriptor("getAgreement", smartcontract.ArrayType,
manifest.NewParameter("agreementID", smartcontract.IntegerType))
md = NewMethodAndPrice(p.getAgreement, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// hasActiveAgreement method
desc = NewDescriptor("hasActiveAgreement", smartcontract.BoolType,
manifest.NewParameter("remoteChainID", smartcontract.IntegerType),
manifest.NewParameter("agreementType", smartcontract.IntegerType))
md = NewMethodAndPrice(p.hasActiveAgreement, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// --- Verification Requests ---
// requestVerification method
desc = NewDescriptor("requestVerification", smartcontract.IntegerType,
manifest.NewParameter("targetChainID", smartcontract.IntegerType),
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("verificationType", smartcontract.IntegerType),
manifest.NewParameter("dataHash", smartcontract.Hash256Type))
md = NewMethodAndPrice(p.requestVerification, 1<<17, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// respondVerification method (committee only - represents response from other chain)
desc = NewDescriptor("respondVerification", smartcontract.BoolType,
manifest.NewParameter("requestID", smartcontract.IntegerType),
manifest.NewParameter("approved", smartcontract.BoolType),
manifest.NewParameter("responseHash", smartcontract.Hash256Type))
md = NewMethodAndPrice(p.respondVerification, 1<<16, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// getVerificationRequest method
desc = NewDescriptor("getVerificationRequest", smartcontract.ArrayType,
manifest.NewParameter("requestID", smartcontract.IntegerType))
md = NewMethodAndPrice(p.getVerificationRequest, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// --- Settlement Requests ---
// requestSettlement method
desc = NewDescriptor("requestSettlement", smartcontract.IntegerType,
manifest.NewParameter("toChainID", smartcontract.IntegerType),
manifest.NewParameter("receiver", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType),
manifest.NewParameter("reference", smartcontract.StringType))
md = NewMethodAndPrice(p.requestSettlement, 1<<17, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// completeSettlement method (committee only - represents confirmation from other chain)
desc = NewDescriptor("completeSettlement", smartcontract.BoolType,
manifest.NewParameter("settlementID", smartcontract.IntegerType),
manifest.NewParameter("txHash", smartcontract.Hash256Type))
md = NewMethodAndPrice(p.completeSettlement, 1<<16, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// cancelSettlement method (sender or committee)
desc = NewDescriptor("cancelSettlement", smartcontract.BoolType,
manifest.NewParameter("settlementID", smartcontract.IntegerType))
md = NewMethodAndPrice(p.cancelSettlement, 1<<16, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// getSettlementRequest method
desc = NewDescriptor("getSettlementRequest", smartcontract.ArrayType,
manifest.NewParameter("settlementID", smartcontract.IntegerType))
md = NewMethodAndPrice(p.getSettlementRequest, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// --- Credential Sharing ---
// shareCredential method
desc = NewDescriptor("shareCredential", smartcontract.IntegerType,
manifest.NewParameter("targetChainID", smartcontract.IntegerType),
manifest.NewParameter("credentialType", smartcontract.IntegerType),
manifest.NewParameter("credentialID", smartcontract.IntegerType),
manifest.NewParameter("contentHash", smartcontract.Hash256Type),
manifest.NewParameter("validUntil", smartcontract.IntegerType))
md = NewMethodAndPrice(p.shareCredential, 1<<17, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// revokeCredentialShare method
desc = NewDescriptor("revokeCredentialShare", smartcontract.BoolType,
manifest.NewParameter("shareID", smartcontract.IntegerType))
md = NewMethodAndPrice(p.revokeCredentialShare, 1<<16, callflag.States|callflag.AllowNotify)
p.AddMethod(md, desc)
// getCredentialShare method
desc = NewDescriptor("getCredentialShare", smartcontract.ArrayType,
manifest.NewParameter("shareID", smartcontract.IntegerType))
md = NewMethodAndPrice(p.getCredentialShare, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// verifyCredentialShare method
desc = NewDescriptor("verifyCredentialShare", smartcontract.BoolType,
manifest.NewParameter("shareID", smartcontract.IntegerType))
md = NewMethodAndPrice(p.verifyCredentialShare, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// --- Counter Query Methods ---
// getAgreementCount method
desc = NewDescriptor("getAgreementCount", smartcontract.IntegerType)
md = NewMethodAndPrice(p.getAgreementCount, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// getVerificationCount method
desc = NewDescriptor("getVerificationCount", smartcontract.IntegerType)
md = NewMethodAndPrice(p.getVerificationCount, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// getSettlementCount method
desc = NewDescriptor("getSettlementCount", smartcontract.IntegerType)
md = NewMethodAndPrice(p.getSettlementCount, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// getCredentialShareCount method
desc = NewDescriptor("getCredentialShareCount", smartcontract.IntegerType)
md = NewMethodAndPrice(p.getCredentialShareCount, 1<<15, callflag.ReadStates)
p.AddMethod(md, desc)
// --- Events ---
eDesc := NewEventDescriptor(AgreementCreatedEvent,
manifest.NewParameter("agreementID", smartcontract.IntegerType),
manifest.NewParameter("remoteChainID", smartcontract.IntegerType),
manifest.NewParameter("agreementType", smartcontract.IntegerType))
p.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(AgreementUpdatedEvent,
manifest.NewParameter("agreementID", smartcontract.IntegerType),
manifest.NewParameter("oldStatus", smartcontract.IntegerType),
manifest.NewParameter("newStatus", smartcontract.IntegerType))
p.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(AgreementTerminatedEvent,
manifest.NewParameter("agreementID", smartcontract.IntegerType),
manifest.NewParameter("remoteChainID", smartcontract.IntegerType))
p.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(VerificationRequestedEvent,
manifest.NewParameter("requestID", smartcontract.IntegerType),
manifest.NewParameter("targetChainID", smartcontract.IntegerType),
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("verificationType", smartcontract.IntegerType))
p.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(VerificationRespondedEvent,
manifest.NewParameter("requestID", smartcontract.IntegerType),
manifest.NewParameter("approved", smartcontract.BoolType))
p.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(SettlementRequestedEvent,
manifest.NewParameter("settlementID", smartcontract.IntegerType),
manifest.NewParameter("toChainID", smartcontract.IntegerType),
manifest.NewParameter("receiver", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType))
p.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(SettlementCompletedEvent,
manifest.NewParameter("settlementID", smartcontract.IntegerType),
manifest.NewParameter("txHash", smartcontract.Hash256Type))
p.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(CredentialSharedEvent,
manifest.NewParameter("shareID", smartcontract.IntegerType),
manifest.NewParameter("owner", smartcontract.Hash160Type),
manifest.NewParameter("targetChainID", smartcontract.IntegerType),
manifest.NewParameter("credentialType", smartcontract.IntegerType))
p.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(CredentialRevokedEvent,
manifest.NewParameter("shareID", smartcontract.IntegerType),
manifest.NewParameter("owner", smartcontract.Hash160Type))
p.AddEvent(NewEvent(eDesc))
return p
}
// Metadata returns contract metadata.
func (p *Pons) Metadata() *interop.ContractMD {
return &p.ContractMD
}
// Initialize initializes Pons contract at the specified hardfork.
func (p *Pons) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
if hf != p.ActiveIn() {
return nil
}
// Initialize default config
cfg := state.PonsConfig{
LocalChainID: defaultLocalChainID,
VerificationTimeout: defaultVerificationTimeout,
SettlementTimeout: defaultSettlementTimeout,
MaxPendingRequests: defaultMaxPendingRequests,
CredentialShareExpiry: defaultCredentialShareExpiry,
}
p.setConfigInternal(ic.DAO, &cfg)
return nil
}
// InitializeCache fills native Pons cache from DAO on node restart.
func (p *Pons) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
return nil
}
// OnPersist implements the Contract interface.
func (p *Pons) OnPersist(ic *interop.Context) error {
return nil
}
// PostPersist implements the Contract interface.
func (p *Pons) PostPersist(ic *interop.Context) error {
return nil
}
// ActiveIn returns the hardfork this contract activates in (nil = always active).
func (p *Pons) ActiveIn() *config.Hardfork {
return nil
}
// ============================================================================
// Storage Key Helpers
// ============================================================================
func makePonsConfigKey() []byte {
return []byte{ponsPrefixConfig}
}
func makePonsAgreementKey(agreementID uint64) []byte {
key := make([]byte, 9)
key[0] = ponsPrefixAgreement
binary.BigEndian.PutUint64(key[1:], agreementID)
return key
}
func makePonsAgreementByChainKey(chainID uint32, agreementID uint64) []byte {
key := make([]byte, 13)
key[0] = ponsPrefixAgreementByChain
binary.BigEndian.PutUint32(key[1:], chainID)
binary.BigEndian.PutUint64(key[5:], agreementID)
return key
}
func makePonsAgreementCounterKey() []byte {
return []byte{ponsPrefixAgreementCounter}
}
func makePonsVerificationKey(requestID uint64) []byte {
key := make([]byte, 9)
key[0] = ponsPrefixVerification
binary.BigEndian.PutUint64(key[1:], requestID)
return key
}
func makePonsVerifBySubjectKey(subject util.Uint160, requestID uint64) []byte {
key := make([]byte, 1+util.Uint160Size+8)
key[0] = ponsPrefixVerifBySubject
copy(key[1:], subject.BytesBE())
binary.BigEndian.PutUint64(key[1+util.Uint160Size:], requestID)
return key
}
func makePonsVerificationCounterKey() []byte {
return []byte{ponsPrefixVerifCounter}
}
func makePonsSettlementKey(settlementID uint64) []byte {
key := make([]byte, 9)
key[0] = ponsPrefixSettlement
binary.BigEndian.PutUint64(key[1:], settlementID)
return key
}
func makePonsSettlByChainKey(chainID uint32, settlementID uint64) []byte {
key := make([]byte, 13)
key[0] = ponsPrefixSettlByChain
binary.BigEndian.PutUint32(key[1:], chainID)
binary.BigEndian.PutUint64(key[5:], settlementID)
return key
}
func makePonsSettlementCounterKey() []byte {
return []byte{ponsPrefixSettlCounter}
}
func makePonsCredentialKey(credentialID uint64) []byte {
key := make([]byte, 9)
key[0] = ponsPrefixCredential
binary.BigEndian.PutUint64(key[1:], credentialID)
return key
}
func makePonsCredByOwnerKey(owner util.Uint160, credentialID uint64) []byte {
key := make([]byte, 1+util.Uint160Size+8)
key[0] = ponsPrefixCredByOwner
copy(key[1:], owner.BytesBE())
binary.BigEndian.PutUint64(key[1+util.Uint160Size:], credentialID)
return key
}
func makePonsCredentialCounterKey() []byte {
return []byte{ponsPrefixCredCounter}
}
// ============================================================================
// Internal Storage Methods
// ============================================================================
func (p *Pons) getConfigInternal(d *dao.Simple) *state.PonsConfig {
si := d.GetStorageItem(p.ID, makePonsConfigKey())
if si == nil {
return &state.PonsConfig{
LocalChainID: defaultLocalChainID,
VerificationTimeout: defaultVerificationTimeout,
SettlementTimeout: defaultSettlementTimeout,
MaxPendingRequests: defaultMaxPendingRequests,
CredentialShareExpiry: defaultCredentialShareExpiry,
}
}
// Decode config: chainID(4) + verifTimeout(4) + settlTimeout(4) + maxReq(8) + credExpiry(4) = 24 bytes
if len(si) < 24 {
return &state.PonsConfig{
LocalChainID: defaultLocalChainID,
VerificationTimeout: defaultVerificationTimeout,
SettlementTimeout: defaultSettlementTimeout,
MaxPendingRequests: defaultMaxPendingRequests,
CredentialShareExpiry: defaultCredentialShareExpiry,
}
}
return &state.PonsConfig{
LocalChainID: binary.BigEndian.Uint32(si[0:4]),
VerificationTimeout: binary.BigEndian.Uint32(si[4:8]),
SettlementTimeout: binary.BigEndian.Uint32(si[8:12]),
MaxPendingRequests: binary.BigEndian.Uint64(si[12:20]),
CredentialShareExpiry: binary.BigEndian.Uint32(si[20:24]),
}
}
func (p *Pons) setConfigInternal(d *dao.Simple, cfg *state.PonsConfig) {
buf := make([]byte, 24)
binary.BigEndian.PutUint32(buf[0:4], cfg.LocalChainID)
binary.BigEndian.PutUint32(buf[4:8], cfg.VerificationTimeout)
binary.BigEndian.PutUint32(buf[8:12], cfg.SettlementTimeout)
binary.BigEndian.PutUint64(buf[12:20], cfg.MaxPendingRequests)
binary.BigEndian.PutUint32(buf[20:24], cfg.CredentialShareExpiry)
d.PutStorageItem(p.ID, makePonsConfigKey(), buf)
}
func (p *Pons) getCounterInternal(d *dao.Simple, key []byte) uint64 {
si := d.GetStorageItem(p.ID, key)
if si == nil || len(si) < 8 {
return 0
}
return binary.BigEndian.Uint64(si)
}
func (p *Pons) incrementCounterInternal(d *dao.Simple, key []byte) uint64 {
current := p.getCounterInternal(d, key)
next := current + 1
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, next)
d.PutStorageItem(p.ID, key, buf)
return next
}
// Agreement storage format:
// localChainID(4) + remoteChainID(4) + agreementType(1) + status(1) + terms(32) +
// effectiveDate(4) + expirationDate(4) + createdAt(4) + updatedAt(4) = 58 bytes
func (p *Pons) getAgreementInternal(d *dao.Simple, agreementID uint64) (*state.BilateralAgreement, bool) {
si := d.GetStorageItem(p.ID, makePonsAgreementKey(agreementID))
if si == nil || len(si) < 58 {
return nil, false
}
var terms util.Uint256
copy(terms[:], si[10:42])
return &state.BilateralAgreement{
ID: agreementID,
LocalChainID: binary.BigEndian.Uint32(si[0:4]),
RemoteChainID: binary.BigEndian.Uint32(si[4:8]),
AgreementType: state.AgreementType(si[8]),
Status: state.AgreementStatus(si[9]),
Terms: terms,
EffectiveDate: binary.BigEndian.Uint32(si[42:46]),
ExpirationDate: binary.BigEndian.Uint32(si[46:50]),
CreatedAt: binary.BigEndian.Uint32(si[50:54]),
UpdatedAt: binary.BigEndian.Uint32(si[54:58]),
}, true
}
func (p *Pons) setAgreementInternal(d *dao.Simple, agr *state.BilateralAgreement) {
buf := make([]byte, 58)
binary.BigEndian.PutUint32(buf[0:4], agr.LocalChainID)
binary.BigEndian.PutUint32(buf[4:8], agr.RemoteChainID)
buf[8] = byte(agr.AgreementType)
buf[9] = byte(agr.Status)
copy(buf[10:42], agr.Terms[:])
binary.BigEndian.PutUint32(buf[42:46], agr.EffectiveDate)
binary.BigEndian.PutUint32(buf[46:50], agr.ExpirationDate)
binary.BigEndian.PutUint32(buf[50:54], agr.CreatedAt)
binary.BigEndian.PutUint32(buf[54:58], agr.UpdatedAt)
d.PutStorageItem(p.ID, makePonsAgreementKey(agr.ID), buf)
// Index by chain
d.PutStorageItem(p.ID, makePonsAgreementByChainKey(agr.RemoteChainID, agr.ID), []byte{1})
}
// Verification storage format:
// requestingChain(4) + targetChain(4) + subject(20) + verificationType(1) + dataHash(32) +
// status(1) + responseHash(32) + requester(20) + createdAt(4) + expiresAt(4) + respondedAt(4) = 126 bytes
func (p *Pons) getVerificationInternal(d *dao.Simple, requestID uint64) (*state.VerificationRequest, bool) {
si := d.GetStorageItem(p.ID, makePonsVerificationKey(requestID))
if si == nil || len(si) < 126 {
return nil, false
}
var subject util.Uint160
copy(subject[:], si[8:28])
var dataHash util.Uint256
copy(dataHash[:], si[29:61])
var responseHash util.Uint256
copy(responseHash[:], si[62:94])
var requester util.Uint160
copy(requester[:], si[94:114])
return &state.VerificationRequest{
ID: requestID,
RequestingChain: binary.BigEndian.Uint32(si[0:4]),
TargetChain: binary.BigEndian.Uint32(si[4:8]),
Subject: subject,
VerificationType: state.VerificationType(si[28]),
DataHash: dataHash,
Status: state.VerificationStatus(si[61]),
ResponseHash: responseHash,
Requester: requester,
CreatedAt: binary.BigEndian.Uint32(si[114:118]),
ExpiresAt: binary.BigEndian.Uint32(si[118:122]),
RespondedAt: binary.BigEndian.Uint32(si[122:126]),
}, true
}
func (p *Pons) setVerificationInternal(d *dao.Simple, vr *state.VerificationRequest) {
buf := make([]byte, 126)
binary.BigEndian.PutUint32(buf[0:4], vr.RequestingChain)
binary.BigEndian.PutUint32(buf[4:8], vr.TargetChain)
copy(buf[8:28], vr.Subject[:])
buf[28] = byte(vr.VerificationType)
copy(buf[29:61], vr.DataHash[:])
buf[61] = byte(vr.Status)
copy(buf[62:94], vr.ResponseHash[:])
copy(buf[94:114], vr.Requester[:])
binary.BigEndian.PutUint32(buf[114:118], vr.CreatedAt)
binary.BigEndian.PutUint32(buf[118:122], vr.ExpiresAt)
binary.BigEndian.PutUint32(buf[122:126], vr.RespondedAt)
d.PutStorageItem(p.ID, makePonsVerificationKey(vr.ID), buf)
// Index by subject
d.PutStorageItem(p.ID, makePonsVerifBySubjectKey(vr.Subject, vr.ID), []byte{1})
}
// Settlement storage format:
// fromChain(4) + toChain(4) + sender(20) + receiver(20) + amount(8) + status(1) +
// createdAt(4) + settledAt(4) + txHash(32) + refLen(2) + reference(var) = 99 + ref bytes
func (p *Pons) getSettlementInternal(d *dao.Simple, settlementID uint64) (*state.SettlementRequest, bool) {
si := d.GetStorageItem(p.ID, makePonsSettlementKey(settlementID))
if si == nil || len(si) < 99 {
return nil, false
}
var sender util.Uint160
copy(sender[:], si[8:28])
var receiver util.Uint160
copy(receiver[:], si[28:48])
var txHash util.Uint256
copy(txHash[:], si[65:97])
refLen := binary.BigEndian.Uint16(si[97:99])
var reference string
if len(si) >= 99+int(refLen) {
reference = string(si[99 : 99+refLen])
}
return &state.SettlementRequest{
ID: settlementID,
FromChain: binary.BigEndian.Uint32(si[0:4]),
ToChain: binary.BigEndian.Uint32(si[4:8]),
Sender: sender,
Receiver: receiver,
Amount: binary.BigEndian.Uint64(si[48:56]),
Status: state.SettlementStatus(si[56]),
CreatedAt: binary.BigEndian.Uint32(si[57:61]),
SettledAt: binary.BigEndian.Uint32(si[61:65]),
TxHash: txHash,
Reference: reference,
}, true
}
func (p *Pons) setSettlementInternal(d *dao.Simple, sr *state.SettlementRequest) {
refBytes := []byte(sr.Reference)
buf := make([]byte, 99+len(refBytes))
binary.BigEndian.PutUint32(buf[0:4], sr.FromChain)
binary.BigEndian.PutUint32(buf[4:8], sr.ToChain)
copy(buf[8:28], sr.Sender[:])
copy(buf[28:48], sr.Receiver[:])
binary.BigEndian.PutUint64(buf[48:56], sr.Amount)
buf[56] = byte(sr.Status)
binary.BigEndian.PutUint32(buf[57:61], sr.CreatedAt)
binary.BigEndian.PutUint32(buf[61:65], sr.SettledAt)
copy(buf[65:97], sr.TxHash[:])
binary.BigEndian.PutUint16(buf[97:99], uint16(len(refBytes)))
copy(buf[99:], refBytes)
d.PutStorageItem(p.ID, makePonsSettlementKey(sr.ID), buf)
// Index by chain
d.PutStorageItem(p.ID, makePonsSettlByChainKey(sr.ToChain, sr.ID), []byte{1})
}
// CredentialShare storage format:
// sourceChain(4) + targetChain(4) + owner(20) + credentialType(1) + credentialID(8) +
// contentHash(32) + validUntil(4) + createdAt(4) + isRevoked(1) = 78 bytes
func (p *Pons) getCredentialShareInternal(d *dao.Simple, shareID uint64) (*state.CredentialShare, bool) {
si := d.GetStorageItem(p.ID, makePonsCredentialKey(shareID))
if si == nil || len(si) < 78 {
return nil, false
}
var owner util.Uint160
copy(owner[:], si[8:28])
var contentHash util.Uint256
copy(contentHash[:], si[37:69])
return &state.CredentialShare{
ID: shareID,
SourceChain: binary.BigEndian.Uint32(si[0:4]),
TargetChain: binary.BigEndian.Uint32(si[4:8]),
Owner: owner,
CredentialType: state.VerificationType(si[28]),
CredentialID: binary.BigEndian.Uint64(si[29:37]),
ContentHash: contentHash,
ValidUntil: binary.BigEndian.Uint32(si[69:73]),
CreatedAt: binary.BigEndian.Uint32(si[73:77]),
IsRevoked: si[77] != 0,
}, true
}
func (p *Pons) setCredentialShareInternal(d *dao.Simple, cs *state.CredentialShare) {
buf := make([]byte, 78)
binary.BigEndian.PutUint32(buf[0:4], cs.SourceChain)
binary.BigEndian.PutUint32(buf[4:8], cs.TargetChain)
copy(buf[8:28], cs.Owner[:])
buf[28] = byte(cs.CredentialType)
binary.BigEndian.PutUint64(buf[29:37], cs.CredentialID)
copy(buf[37:69], cs.ContentHash[:])
binary.BigEndian.PutUint32(buf[69:73], cs.ValidUntil)
binary.BigEndian.PutUint32(buf[73:77], cs.CreatedAt)
if cs.IsRevoked {
buf[77] = 1
}
d.PutStorageItem(p.ID, makePonsCredentialKey(cs.ID), buf)
// Index by owner
d.PutStorageItem(p.ID, makePonsCredByOwnerKey(cs.Owner, cs.ID), []byte{1})
}
// hasActiveAgreementInternal checks if there's an active agreement with the target chain
// for the specified agreement type.
func (p *Pons) hasActiveAgreementInternal(d *dao.Simple, remoteChainID uint32, agreementType state.AgreementType, blockHeight uint32) bool {
count := p.getCounterInternal(d, makePonsAgreementCounterKey())
for i := uint64(1); i <= count; i++ {
agr, exists := p.getAgreementInternal(d, i)
if !exists {
continue
}
if agr.RemoteChainID != remoteChainID {
continue
}
if agr.Status != state.AgreementActive {
continue
}
// Check expiration
if agr.ExpirationDate > 0 && agr.ExpirationDate < blockHeight {
continue
}
// Check if agreement type matches or is comprehensive
if agr.AgreementType == agreementType || agr.AgreementType == state.AgreementTypeComprehensive {
return true
}
}
return false
}
// ============================================================================
// Contract Methods
// ============================================================================
func (p *Pons) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
cfg := p.getConfigInternal(ic.DAO)
return stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(cfg.LocalChainID))),
stackitem.NewBigInteger(big.NewInt(int64(cfg.VerificationTimeout))),
stackitem.NewBigInteger(big.NewInt(int64(cfg.SettlementTimeout))),
stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MaxPendingRequests)),
stackitem.NewBigInteger(big.NewInt(int64(cfg.CredentialShareExpiry))),
})
}
func (p *Pons) setLocalChainID(ic *interop.Context, args []stackitem.Item) stackitem.Item {
chainID := uint32(toBigInt(args[0]).Int64())
if !p.Tutus.CheckCommittee(ic) {
panic("only committee can set chain ID")
}
cfg := p.getConfigInternal(ic.DAO)
cfg.LocalChainID = chainID
p.setConfigInternal(ic.DAO, cfg)
return stackitem.NewBool(true)
}
func (p *Pons) createAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item {
remoteChainID := uint32(toBigInt(args[0]).Int64())
agreementType := state.AgreementType(toBigInt(args[1]).Int64())
termsHashBytes, err := args[2].TryBytes()
if err != nil {
panic(err)
}
termsHash, err := util.Uint256DecodeBytesBE(termsHashBytes)
if err != nil {
panic(err)
}
expirationHeight := uint32(toBigInt(args[3]).Int64())
if !p.Tutus.CheckCommittee(ic) {
panic("only committee can create agreements")
}
if agreementType > state.AgreementTypeComprehensive {
panic(ErrInvalidAgreementType)
}
cfg := p.getConfigInternal(ic.DAO)
// Create agreement
agreementID := p.incrementCounterInternal(ic.DAO, makePonsAgreementCounterKey())
agr := &state.BilateralAgreement{
ID: agreementID,
LocalChainID: cfg.LocalChainID,
RemoteChainID: remoteChainID,
AgreementType: agreementType,
Status: state.AgreementPending,
Terms: termsHash,
EffectiveDate: 0, // Set when activated
ExpirationDate: expirationHeight,
CreatedAt: ic.Block.Index,
UpdatedAt: ic.Block.Index,
}
p.setAgreementInternal(ic.DAO, agr)
ic.AddNotification(p.Hash, AgreementCreatedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID)),
stackitem.NewBigInteger(big.NewInt(int64(remoteChainID))),
stackitem.NewBigInteger(big.NewInt(int64(agreementType))),
}))
return stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID))
}
func (p *Pons) updateAgreementStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item {
agreementID := toBigInt(args[0]).Uint64()
newStatus := state.AgreementStatus(toBigInt(args[1]).Int64())
if !p.Tutus.CheckCommittee(ic) {
panic("only committee can update agreement status")
}
agr, exists := p.getAgreementInternal(ic.DAO, agreementID)
if !exists {
panic(ErrAgreementNotFound)
}
oldStatus := agr.Status
agr.Status = newStatus
agr.UpdatedAt = ic.Block.Index
// Set effective date when activating
if newStatus == state.AgreementActive && oldStatus != state.AgreementActive {
agr.EffectiveDate = ic.Block.Index
}
p.setAgreementInternal(ic.DAO, agr)
ic.AddNotification(p.Hash, AgreementUpdatedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID)),
stackitem.NewBigInteger(big.NewInt(int64(oldStatus))),
stackitem.NewBigInteger(big.NewInt(int64(newStatus))),
}))
return stackitem.NewBool(true)
}
func (p *Pons) getAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item {
agreementID := toBigInt(args[0]).Uint64()
agr, exists := p.getAgreementInternal(ic.DAO, agreementID)
if !exists {
return stackitem.Null{}
}
return stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(agr.ID)),
stackitem.NewBigInteger(big.NewInt(int64(agr.LocalChainID))),
stackitem.NewBigInteger(big.NewInt(int64(agr.RemoteChainID))),
stackitem.NewBigInteger(big.NewInt(int64(agr.AgreementType))),
stackitem.NewBigInteger(big.NewInt(int64(agr.Status))),
stackitem.NewByteArray(agr.Terms.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(agr.EffectiveDate))),
stackitem.NewBigInteger(big.NewInt(int64(agr.ExpirationDate))),
stackitem.NewBigInteger(big.NewInt(int64(agr.CreatedAt))),
stackitem.NewBigInteger(big.NewInt(int64(agr.UpdatedAt))),
})
}
func (p *Pons) hasActiveAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item {
remoteChainID := uint32(toBigInt(args[0]).Int64())
agreementType := state.AgreementType(toBigInt(args[1]).Int64())
has := p.hasActiveAgreementInternal(ic.DAO, remoteChainID, agreementType, ic.Block.Index)
return stackitem.NewBool(has)
}
func (p *Pons) requestVerification(ic *interop.Context, args []stackitem.Item) stackitem.Item {
targetChainID := uint32(toBigInt(args[0]).Int64())
subject := toUint160(args[1])
verificationType := state.VerificationType(toBigInt(args[2]).Int64())
dataHashBytes, err := args[3].TryBytes()
if err != nil {
panic(err)
}
dataHash, err := util.Uint256DecodeBytesBE(dataHashBytes)
if err != nil {
panic(err)
}
cfg := p.getConfigInternal(ic.DAO)
// Check for active agreement
agreementType := state.AgreementTypeIdentity
switch verificationType {
case state.VerificationTypeCredential, state.VerificationTypeCertificate:
agreementType = state.AgreementTypeEducation
case state.VerificationTypeHealth:
agreementType = state.AgreementTypeHealthcare
}
if !p.hasActiveAgreementInternal(ic.DAO, targetChainID, agreementType, ic.Block.Index) {
panic(ErrNoAgreement)
}
// Get requester from caller
requester := ic.VM.GetCallingScriptHash()
// Create verification request
requestID := p.incrementCounterInternal(ic.DAO, makePonsVerificationCounterKey())
vr := &state.VerificationRequest{
ID: requestID,
RequestingChain: cfg.LocalChainID,
TargetChain: targetChainID,
Subject: subject,
VerificationType: verificationType,
DataHash: dataHash,
Status: state.VerificationPending,
Requester: requester,
CreatedAt: ic.Block.Index,
ExpiresAt: ic.Block.Index + cfg.VerificationTimeout,
}
p.setVerificationInternal(ic.DAO, vr)
ic.AddNotification(p.Hash, VerificationRequestedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)),
stackitem.NewBigInteger(big.NewInt(int64(targetChainID))),
stackitem.NewByteArray(subject.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(verificationType))),
}))
return stackitem.NewBigInteger(new(big.Int).SetUint64(requestID))
}
func (p *Pons) respondVerification(ic *interop.Context, args []stackitem.Item) stackitem.Item {
requestID := toBigInt(args[0]).Uint64()
approved := toBool(args[1])
responseHashBytes, err := args[2].TryBytes()
if err != nil {
panic(err)
}
responseHash, err := util.Uint256DecodeBytesBE(responseHashBytes)
if err != nil {
panic(err)
}
if !p.Tutus.CheckCommittee(ic) {
panic("only committee can respond to verification requests")
}
vr, exists := p.getVerificationInternal(ic.DAO, requestID)
if !exists {
panic(ErrVerificationNotFound)
}
if vr.Status != state.VerificationPending {
panic("verification request already processed")
}
if ic.Block.Index > vr.ExpiresAt {
vr.Status = state.VerificationExpired
p.setVerificationInternal(ic.DAO, vr)
panic(ErrVerificationExpired)
}
if approved {
vr.Status = state.VerificationApproved
} else {
vr.Status = state.VerificationRejected
}
vr.ResponseHash = responseHash
vr.RespondedAt = ic.Block.Index
p.setVerificationInternal(ic.DAO, vr)
ic.AddNotification(p.Hash, VerificationRespondedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)),
stackitem.NewBool(approved),
}))
return stackitem.NewBool(true)
}
func (p *Pons) getVerificationRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item {
requestID := toBigInt(args[0]).Uint64()
vr, exists := p.getVerificationInternal(ic.DAO, requestID)
if !exists {
return stackitem.Null{}
}
return stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(vr.ID)),
stackitem.NewBigInteger(big.NewInt(int64(vr.RequestingChain))),
stackitem.NewBigInteger(big.NewInt(int64(vr.TargetChain))),
stackitem.NewByteArray(vr.Subject.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(vr.VerificationType))),
stackitem.NewByteArray(vr.DataHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(vr.Status))),
stackitem.NewByteArray(vr.ResponseHash.BytesBE()),
stackitem.NewByteArray(vr.Requester.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(vr.CreatedAt))),
stackitem.NewBigInteger(big.NewInt(int64(vr.ExpiresAt))),
stackitem.NewBigInteger(big.NewInt(int64(vr.RespondedAt))),
})
}
func (p *Pons) requestSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item {
toChainID := uint32(toBigInt(args[0]).Int64())
receiver := toUint160(args[1])
amount := toBigInt(args[2]).Uint64()
reference := toString(args[3])
cfg := p.getConfigInternal(ic.DAO)
// Check for settlement agreement
if !p.hasActiveAgreementInternal(ic.DAO, toChainID, state.AgreementTypeSettlement, ic.Block.Index) {
panic(ErrNoAgreement)
}
sender := ic.VM.GetCallingScriptHash()
// Create settlement request
settlementID := p.incrementCounterInternal(ic.DAO, makePonsSettlementCounterKey())
sr := &state.SettlementRequest{
ID: settlementID,
FromChain: cfg.LocalChainID,
ToChain: toChainID,
Sender: sender,
Receiver: receiver,
Amount: amount,
Status: state.SettlementPending,
Reference: reference,
CreatedAt: ic.Block.Index,
}
p.setSettlementInternal(ic.DAO, sr)
ic.AddNotification(p.Hash, SettlementRequestedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID)),
stackitem.NewBigInteger(big.NewInt(int64(toChainID))),
stackitem.NewByteArray(receiver.BytesBE()),
stackitem.NewBigInteger(new(big.Int).SetUint64(amount)),
}))
return stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID))
}
func (p *Pons) completeSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item {
settlementID := toBigInt(args[0]).Uint64()
txHashBytes, err := args[1].TryBytes()
if err != nil {
panic(err)
}
txHash, err := util.Uint256DecodeBytesBE(txHashBytes)
if err != nil {
panic(err)
}
if !p.Tutus.CheckCommittee(ic) {
panic("only committee can complete settlements")
}
sr, exists := p.getSettlementInternal(ic.DAO, settlementID)
if !exists {
panic(ErrSettlementNotFound)
}
if sr.Status != state.SettlementPending {
panic("settlement already processed")
}
sr.Status = state.SettlementCompleted
sr.SettledAt = ic.Block.Index
sr.TxHash = txHash
p.setSettlementInternal(ic.DAO, sr)
ic.AddNotification(p.Hash, SettlementCompletedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID)),
stackitem.NewByteArray(txHash.BytesBE()),
}))
return stackitem.NewBool(true)
}
func (p *Pons) cancelSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item {
settlementID := toBigInt(args[0]).Uint64()
sr, exists := p.getSettlementInternal(ic.DAO, settlementID)
if !exists {
panic(ErrSettlementNotFound)
}
if sr.Status != state.SettlementPending {
panic("settlement already processed")
}
// Allow sender or committee to cancel
caller := ic.VM.GetCallingScriptHash()
if !caller.Equals(sr.Sender) && !p.Tutus.CheckCommittee(ic) {
panic("only sender or committee can cancel settlement")
}
sr.Status = state.SettlementCancelled
p.setSettlementInternal(ic.DAO, sr)
return stackitem.NewBool(true)
}
func (p *Pons) getSettlementRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item {
settlementID := toBigInt(args[0]).Uint64()
sr, exists := p.getSettlementInternal(ic.DAO, settlementID)
if !exists {
return stackitem.Null{}
}
return stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(sr.ID)),
stackitem.NewBigInteger(big.NewInt(int64(sr.FromChain))),
stackitem.NewBigInteger(big.NewInt(int64(sr.ToChain))),
stackitem.NewByteArray(sr.Sender.BytesBE()),
stackitem.NewByteArray(sr.Receiver.BytesBE()),
stackitem.NewBigInteger(new(big.Int).SetUint64(sr.Amount)),
stackitem.NewBigInteger(big.NewInt(int64(sr.Status))),
stackitem.NewByteArray(sr.TxHash.BytesBE()),
stackitem.NewByteArray([]byte(sr.Reference)),
stackitem.NewBigInteger(big.NewInt(int64(sr.CreatedAt))),
stackitem.NewBigInteger(big.NewInt(int64(sr.SettledAt))),
})
}
func (p *Pons) shareCredential(ic *interop.Context, args []stackitem.Item) stackitem.Item {
targetChainID := uint32(toBigInt(args[0]).Int64())
credentialType := state.VerificationType(toBigInt(args[1]).Int64())
credentialID := toBigInt(args[2]).Uint64()
contentHashBytes, err := args[3].TryBytes()
if err != nil {
panic(err)
}
contentHash, err := util.Uint256DecodeBytesBE(contentHashBytes)
if err != nil {
panic(err)
}
validUntil := uint32(toBigInt(args[4]).Int64())
cfg := p.getConfigInternal(ic.DAO)
// Determine agreement type needed
agreementType := state.AgreementTypeEducation
if credentialType == state.VerificationTypeHealth {
agreementType = state.AgreementTypeHealthcare
}
if !p.hasActiveAgreementInternal(ic.DAO, targetChainID, agreementType, ic.Block.Index) {
panic(ErrNoAgreement)
}
owner := ic.VM.GetCallingScriptHash()
// Set default validity if not provided
if validUntil == 0 {
validUntil = ic.Block.Index + cfg.CredentialShareExpiry
}
// Create credential share
shareID := p.incrementCounterInternal(ic.DAO, makePonsCredentialCounterKey())
cs := &state.CredentialShare{
ID: shareID,
SourceChain: cfg.LocalChainID,
TargetChain: targetChainID,
Owner: owner,
CredentialType: credentialType,
CredentialID: credentialID,
ContentHash: contentHash,
ValidUntil: validUntil,
CreatedAt: ic.Block.Index,
IsRevoked: false,
}
p.setCredentialShareInternal(ic.DAO, cs)
ic.AddNotification(p.Hash, CredentialSharedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(shareID)),
stackitem.NewByteArray(owner.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(targetChainID))),
stackitem.NewBigInteger(big.NewInt(int64(credentialType))),
}))
return stackitem.NewBigInteger(new(big.Int).SetUint64(shareID))
}
func (p *Pons) revokeCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item {
shareID := toBigInt(args[0]).Uint64()
cs, exists := p.getCredentialShareInternal(ic.DAO, shareID)
if !exists {
panic(ErrCredentialNotFound)
}
// Allow owner or committee to revoke
caller := ic.VM.GetCallingScriptHash()
if !caller.Equals(cs.Owner) && !p.Tutus.CheckCommittee(ic) {
panic(ErrNotCredentialOwner)
}
cs.IsRevoked = true
p.setCredentialShareInternal(ic.DAO, cs)
ic.AddNotification(p.Hash, CredentialRevokedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(shareID)),
stackitem.NewByteArray(cs.Owner.BytesBE()),
}))
return stackitem.NewBool(true)
}
func (p *Pons) getCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item {
shareID := toBigInt(args[0]).Uint64()
cs, exists := p.getCredentialShareInternal(ic.DAO, shareID)
if !exists {
return stackitem.Null{}
}
return stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(new(big.Int).SetUint64(cs.ID)),
stackitem.NewBigInteger(big.NewInt(int64(cs.SourceChain))),
stackitem.NewBigInteger(big.NewInt(int64(cs.TargetChain))),
stackitem.NewByteArray(cs.Owner.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(cs.CredentialType))),
stackitem.NewBigInteger(new(big.Int).SetUint64(cs.CredentialID)),
stackitem.NewByteArray(cs.ContentHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(cs.ValidUntil))),
stackitem.NewBigInteger(big.NewInt(int64(cs.CreatedAt))),
stackitem.NewBool(cs.IsRevoked),
})
}
func (p *Pons) verifyCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item {
shareID := toBigInt(args[0]).Uint64()
cs, exists := p.getCredentialShareInternal(ic.DAO, shareID)
if !exists {
return stackitem.NewBool(false)
}
if cs.IsRevoked {
return stackitem.NewBool(false)
}
if ic.Block.Index > cs.ValidUntil {
return stackitem.NewBool(false)
}
return stackitem.NewBool(true)
}
func (p *Pons) getAgreementCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
count := p.getCounterInternal(ic.DAO, makePonsAgreementCounterKey())
return stackitem.NewBigInteger(new(big.Int).SetUint64(count))
}
func (p *Pons) getVerificationCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
count := p.getCounterInternal(ic.DAO, makePonsVerificationCounterKey())
return stackitem.NewBigInteger(new(big.Int).SetUint64(count))
}
func (p *Pons) getSettlementCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
count := p.getCounterInternal(ic.DAO, makePonsSettlementCounterKey())
return stackitem.NewBigInteger(new(big.Int).SetUint64(count))
}
func (p *Pons) getCredentialShareCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
count := p.getCounterInternal(ic.DAO, makePonsCredentialCounterKey())
return stackitem.NewBigInteger(new(big.Int).SetUint64(count))
}