package native import ( "encoding/binary" "errors" "math/big" "git.marketally.com/tutus-one/tutus-chain/pkg/config" "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" "git.marketally.com/tutus-one/tutus-chain/pkg/util" "git.marketally.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)) }