@ -7,6 +7,7 @@
import Foundation
import AlphaWalletAddress
import AlphaWalletAttestation
import AlphaWalletCore
import Kanna
import PromiseKit
@ -113,8 +114,7 @@ public enum TokenLevelTokenScriptDisplayStatus {
public class PrivateXMLHandler {
enum Target {
case token ( AlphaWallet . Address )
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t t o i m p l e m e n t . I s t h e k e y t h e s c r i p t ' s U R L ?
case attestation ( URL )
case attestation
var isFifaTicketContract : Bool {
switch self {
@ -139,6 +139,7 @@ public class PrivateXMLHandler {
fileprivate static let tokenScriptNamespace = TokenScript . supportedTokenScriptNamespace
private let features : TokenScriptFeatures
fileprivate let assetAttributeResolver : AssetAttributeResolver
private var xml : XMLDocument
private let signatureNamespacePrefix = " ds: "
private let xhtmlNamespacePrefix = " xhtml: "
@ -167,7 +168,7 @@ public class PrivateXMLHandler {
return holdingContractElement ? [ " interface " ] . flatMap { TokenInterfaceType ( rawValue : $0 ) }
} ( )
fileprivate lazy var tokenType : TokenInterfaceType ? = {
lazy var tokenType : TokenInterfaceType ? = {
var tokenType : TokenInterfaceType ?
threadSafe . performSync {
tokenType = self . _tokenType
@ -187,6 +188,9 @@ public class PrivateXMLHandler {
return fields
}
// S e e u s a g e f o r w h y i t h a s t o b e p u b l i c
public lazy var _attestationFields : [ AttestationAttribute ] = extractFieldsForAttestation ( )
lazy var introductionHtmlString : String = {
var introductionHtmlString : String = " "
// T O D O f a l l b a c k t o f i r s t i f n o t f o u n d
@ -262,7 +266,7 @@ public class PrivateXMLHandler {
case . token ( let contractAddress ) :
results . append ( . init ( type : . tokenScript ( contract : contractAddress , title : name , viewHtml : ( html : html , style : style ) , attributes : attributes , transactionFunction : functionOrigin , selection : selection ) ) )
case . attestation :
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t t o i m p l e m e n t s u p p o r t f o r ` a c t i o n s
// T O D O a t t e s t a t i o n s + T o k e n S c r i p t t o i m p l e m e n t s u p p o r t f o r ` a c t i o n s
break
}
}
@ -310,7 +314,7 @@ public class PrivateXMLHandler {
case . token ( let contractAddress ) :
optionalContract = contractAddress
case . attestation :
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t t o i m p l e m e n t s u p p o r t f o r ` a c t i v i t y C a r d s `
// T O D O a t t e s t a t i o n s + T o k e n S c r i p t t o i m p l e m e n t s u p p o r t f o r ` a c t i o n s
optionalContract = nil
}
}
@ -412,25 +416,36 @@ public class PrivateXMLHandler {
return " en "
}
// T O D O m a y b e t h i s s h o u l d b e r e m o v e d . W e s h o u l d n o t u s e A s s e t D e f i n i t i o n S t o r e h e r e b e c a u s e i t ' s e a s y t o c r e a t e c y c l i c a l r e f e r e n c e s a n d i n f i n i t e l o o p s s i n c e t h e y r e f e r t o e a c h o t h e r
convenience init ( contract : AlphaWallet . Address , assetDefinitionStore : AssetDefinitionStoreProtocol ) {
let xmlString = assetDefinitionStore [ contract ]
let isOfficial = assetDefinitionStore . isOfficial ( contract : contract )
let isCanonicalized = assetDefinitionStore . isCanonicalized ( contract : contract )
self . init ( contract : contract , xmlString : xmlString , baseTokenType : nil , isOfficial : isOfficial , isCanonicalized : isCanonicalized , assetDefinitionStore : assetDefinitionStore )
}
private lazy var attestationName : String ? = {
let attestationElement = XMLHandler . getAttestationElement ( fromRoot : xml , xmlContext : xmlContext )
return XMLHandler . getAttestationNameElement ( fromAttestationElement : attestationElement , xmlContext : xmlContext ) ? . text
} ( )
convenience init ( contract : AlphaWallet . Address , baseXml : String , baseTokenType : TokenType , assetDefinitionStore : AssetDefinitionStoreProtocol ) {
self . init ( contract : contract , xmlString : baseXml , baseTokenType : baseTokenType , isOfficial : true , isCanonicalized : true , assetDefinitionStore : assetDefinitionStore )
}
private lazy var attestationDescription : String ? = {
let attestationElement = XMLHandler . getAttestationElement ( fromRoot : xml , xmlContext : xmlContext )
return XMLHandler . getAttestationDescriptionElement ( fromAttestationElement : attestationElement , xmlContext : xmlContext ) ? . text
} ( )
lazy var attestationIssuerKey : String ? = {
return functional . getAttestationIssuerKey ( xml : xml , xmlContext : xmlContext )
} ( )
lazy var attestationCollectionId : String ? = {
return functional . computeAttestationCollectionId ( xml : xml , xmlContext : xmlContext )
} ( )
lazy var attestationSchemaUid : Attestation . SchemaUid ? = {
return functional . getAttestationSchemaUid ( xml : xml , xmlContext : xmlContext )
} ( )
private init ( contract : AlphaWallet . Address , xmlString : String ? , baseTokenType : TokenType ? , isOfficial : Bool , isCanonicalized : Bool , assetDefinitionStore : AssetDefinitionStoreProtocol ) {
init ( contract : AlphaWallet . Address , xmlString : String ? , baseTokenType : TokenType ? , isOfficial : Bool , isCanonicalized : Bool , resolver : TokenScriptResolver , tokenScriptStatusResolver : TokenScriptStatusResolver , assetAttributeResolver : AssetAttributeResolver , features : TokenScriptFeatures ) {
let xmlString = xmlString ? ? " "
self . target = Target . token ( contract )
self . isOfficial = isOfficial
self . isCanonicalized = isCanonicalized
self . baseTokenType = baseTokenType
self . features = assetDefinitionStore . features
self . features = features
self . assetAttributeResolver = assetAttributeResolver
var _xml : XMLDocument !
var _tokenScriptStatus : Promise < TokenLevelTokenScriptDisplayStatus > !
@ -438,14 +453,14 @@ public class PrivateXMLHandler {
var _server : RPCServerOrAny ?
let _xmlContext = xmlContext
let _isBase = baseTokenType != nil
let features = self . fe atures
let shouldLoadTokenScriptWithFailedSignatures = features . shouldLoadTokenScriptWithFailedSign atures
threadSafe . performSync {
// W e s t i l l c o m p u t e t h e T o k e n S c r i p t s t a t u s e v e n i f x m l S t r i n g i s e m p t y b e c a u s e i t m i g h t b e c o n s i d e r e d e m p t y b e c a u s e t h e r e ' s a c o n f l i c t
let tokenScriptStatusPromise = assetDefinitionStore . computeTokenScriptStatus ( forContract : contract , xmlString : xmlString , isOfficial : isOfficial )
let tokenScriptStatusPromise = tokenScriptStatusResolver . computeTokenScriptStatus ( forContract : contract , xmlString : xmlString , isOfficial : isOfficial )
_tokenScriptStatus = tokenScriptStatusPromise
if let tokenScriptStatus = tokenScriptStatusPromise . value {
let ( xml , hasValidTokenScriptFile ) = PrivateXMLHandler . storeXmlAccordingToTokenScriptStatus ( xmlString : xmlString , tokenScriptStatus : tokenScriptStatus , features : fe atures)
let ( xml , hasValidTokenScriptFile ) = PrivateXMLHandler . storeXmlAccordingToTokenScriptStatus ( xmlString : xmlString , tokenScriptStatus : tokenScriptStatus , shouldLoadTokenScriptWithFailedSignatures : shouldLoadTokenScriptWithFailedSign atures)
_xml = xml
_hasValidTokenScriptFile = hasValidTokenScriptFile
if _isBase {
@ -463,18 +478,18 @@ public class PrivateXMLHandler {
_server = PrivateXMLHandler . extractServer ( fromXML : _xml , xmlContext : _xmlContext , matchingContract : contract ) . flatMap { . server ( $0 ) }
}
tokenScriptStatusPromise . done { tokenScriptStatus in
let ( xml , hasValidTokenScriptFile ) = PrivateXMLHandler . storeXmlAccordingToTokenScriptStatus ( xmlString : xmlString , tokenScriptStatus : tokenScriptStatus , features : fe atures)
_xml = xml
_hasValidTokenScriptFile = hasValidTokenScriptFile
if isBase {
_server = . any
} else {
_server = PrivateXMLHandler . extractServer ( fromXML : xml , xmlContext : _xmlContext , matchingContract : contract ) . flatMap { . server ( $0 ) }
}
if ! isBase {
assetDefinitionStore . invalidateSignatureStatus ( forContract : contract )
}
} . cauterize ( )
let ( xml , hasValidTokenScriptFile ) = PrivateXMLHandler . storeXmlAccordingToTokenScriptStatus ( xmlString : xmlString , tokenScriptStatus : tokenScriptStatus , shouldLoadTokenScriptWithFailedSignatures : shouldLoadTokenScriptWithFailedSign atures)
_xml = xml
_hasValidTokenScriptFile = hasValidTokenScriptFile
if isBase {
_server = . any
} else {
_server = PrivateXMLHandler . extractServer ( fromXML : xml , xmlContext : _xmlContext , matchingContract : contract ) . flatMap { . server ( $0 ) }
}
if ! isBase {
resolver . invalidateSignatureStatus ( forContract : contract )
}
} . cauterize ( )
}
}
@ -484,13 +499,14 @@ public class PrivateXMLHandler {
self . server = _server
}
private init ( forAttestationURL url : URL , xmlString : String ? , assetDefinitionStore : AssetDefinitionStoreProtocol ) {
let xmlString = xmlString ? ? " "
self . target = Target . attestation ( url )
// W h i l e w e p a s s i n t h e a t t e s t a t i o n ( w e n e e d i t b e c a u s e w e d o n ' t k n o w t h e a t t e s t a t i o n ' s c o l l e c t i o n I d w i t h o u t p a s s i n g i t i n f o r c o m p u t a t i o n ) , w e d o n ' t s t o r e t h e a t t e s t a t i o n
init ( forAttestation attestation : Attestation , xmlString : String , tokenScriptStatusResolver : TokenScriptStatusResolver , assetAttributeResolver : AssetAttributeResolver , features : TokenScriptFeatures ) {
self . target = Target . attestation
self . isOfficial = false
self . isCanonicalized = false
self . baseTokenType = nil
self . features = assetDefinitionStore . features
self . features = features
self . assetAttributeResolver = assetAttributeResolver
var _xml : XMLDocument !
var _tokenScriptStatus : Promise < TokenLevelTokenScriptDisplayStatus > !
@ -501,26 +517,23 @@ public class PrivateXMLHandler {
threadSafe . performSync {
// W e s t i l l c o m p u t e t h e T o k e n S c r i p t s t a t u s e v e n i f x m l S t r i n g i s e m p t y b e c a u s e i t m i g h t b e c o n s i d e r e d e m p t y b e c a u s e t h e r e ' s a c o n f l i c t
let tokenScriptStatusPromise = assetDefinitionStore . computeTokenScriptStatus ( forAttestationURL : url , xmlString : xmlString )
let tokenScriptStatusPromise = tokenScriptStatusResolver . computeTokenScriptStatus ( forAttestation : attestation , xmlString : xmlString )
_tokenScriptStatus = tokenScriptStatusPromise
if let tokenScriptStatus = tokenScriptStatusPromise . value {
let ( xml , hasValidTokenScriptFile ) = PrivateXMLHandler . storeXmlAccordingToTokenScriptStatus ( xmlString : xmlString , tokenScriptStatus : tokenScriptStatus , fe atures: features )
let ( xml , hasValidTokenScriptFile ) = PrivateXMLHandler . storeXmlAccordingToTokenScriptStatus ( xmlString : xmlString , tokenScriptStatus : tokenScriptStatus , shouldLoadTokenScriptWithFailedSign atures: features . shouldLoadTokenScriptWithFailedSign atures )
_xml = xml
_hasValidTokenScriptFile = hasValidTokenScriptFile
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t n o s p e c i f i c s e r v e r i n T o k e n S c r i p t f o r a t t e s t a t i o n r i g h t ?
_server = . any
} else {
_xml = ( try ? Kanna . XML ( xml : xmlString , encoding : . utf8 ) ) ? ? PrivateXMLHandler . emptyXML
_hasValidTokenScriptFile = true
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t i s t h e r e a s p e c i f i c s e r v e r f o r a t t e s t a t i o n ' s T o k e n S c r i p t ?
_server = . any
tokenScriptStatusPromise . done { tokenScriptStatus in
let ( xml , hasValidTokenScriptFile ) = PrivateXMLHandler . storeXmlAccordingToTokenScriptStatus ( xmlString : xmlString , tokenScriptStatus : tokenScriptStatus , fe atures: features )
let ( xml , hasValidTokenScriptFile ) = PrivateXMLHandler . storeXmlAccordingToTokenScriptStatus ( xmlString : xmlString , tokenScriptStatus : tokenScriptStatus , shouldLoadTokenScriptWithFailedSign atures: features . shouldLoadTokenScriptWithFailedSign atures )
_xml = xml
_hasValidTokenScriptFile = hasValidTokenScriptFile
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t n o s p e c i f i c s e r v e r i n T o k e n S c r i p t f o r a t t e s t a t i o n r i g h t ?
_server = . any
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t i s t h e r e a n e e d t o i n v a l i d a t e t h e s i g n a t u r e s t a t u s h e r e ?
// T O D O a t t e s t a t i o n s + T o k e n S c r i p t t o i m p l e m e n t c o m p u t e T o k e n S c r i p t S t a t u s . N o t e t h a t t h i s i s a b o u t t h e T o k e n S c r i p t f i l e . N o t t h e a t t e s t a t i o n i s s u e r i s t h e r e a n e e d t o i n v a l i d a t e t h e s i g n a t u r e s t a t u s h e r e ?
} . cauterize ( )
}
}
@ -531,9 +544,22 @@ public class PrivateXMLHandler {
self . server = _server
}
convenience init ( forAttestationURL url : URL , assetDefinitionStore : AssetDefinitionStoreProtocol ) {
let xmlString = assetDefinitionStore [ url ]
self . init ( forAttestationURL : url , xmlString : xmlString , assetDefinitionStore : assetDefinitionStore )
func computeCollectionIdFieldNames ( forAttestation attestation : Attestation ) -> [ String ] {
guard let tokensElement = XMLHandler . getTokenElement ( fromRoot : xml , xmlContext : xmlContext ) else { return [ ] }
let collectionFieldElements = XMLHandler . getAttestationCollectionFieldElements ( fromAttributeElement : tokensElement , xmlContext : xmlContext )
return collectionFieldElements . compactMap { $0 [ " name " ] }
}
func computeAttestationCollectionId ( forAttestation attestation : Attestation ) -> String {
let collectionIdFieldNames = computeCollectionIdFieldNames ( forAttestation : attestation )
let collectionIdFields : [ AttestationAttribute ] = collectionIdFieldNames . map { AttestationAttribute ( label : $0 , path : $0 ) }
return Attestation . computeAttestationCollectionId ( forAttestation : attestation , collectionIdFields : collectionIdFields )
}
func computeAttestationIdFieldNames ( forAttestation attestation : Attestation ) -> [ String ] {
guard let tokensElement = XMLHandler . getTokenElement ( fromRoot : xml , xmlContext : xmlContext ) else { return [ ] }
let fieldElements = XMLHandler . getAttestationIdFieldElements ( fromAttributeElement : tokensElement , xmlContext : xmlContext )
return fieldElements . compactMap { $0 [ " name " ] }
}
private func extractHtml ( fromViewElement element : XMLElement ) -> ( html : String , style : String ) {
@ -557,7 +583,7 @@ public class PrivateXMLHandler {
}
}
private static func storeXmlAccordingToTokenScriptStatus ( xmlString : String , tokenScriptStatus : TokenLevelTokenScriptDisplayStatus , features : TokenScriptFeatures ) -> ( xml : XMLDocument , hasValidTokenScriptFile : Bool ) {
private static func storeXmlAccordingToTokenScriptStatus ( xmlString : String , tokenScriptStatus : TokenLevelTokenScriptDisplayStatus , shouldLoadTokenScriptWithFailedSignatures : Bool ) -> ( xml : XMLDocument , hasValidTokenScriptFile : Bool ) {
let xml : XMLDocument
let hasValidTokenScriptFile : Bool
switch tokenScriptStatus {
@ -578,7 +604,7 @@ public class PrivateXMLHandler {
hasValidTokenScriptFile = false
}
} else {
if features . shouldLoadTokenScriptWithFailedSignatures {
if shouldLoadTokenScriptWithFailedSignatures {
xml = ( try ? Kanna . XML ( xml : xmlString , encoding : . utf8 ) ) ? ? PrivateXMLHandler . emptyXML
hasValidTokenScriptFile = true
} else {
@ -590,18 +616,26 @@ public class PrivateXMLHandler {
return ( xml : xml , hasValidTokenScriptFile : hasValidTokenScriptFile )
}
func getToken ( name : String , symbol : String , fromTokenIdOrEvent tokenIdOrEvent : TokenIdOrEvent , index : UInt16 , inWallet account : AlphaWallet . Address , server : RPCServer , tokenType : TokenType , assetDefinitionStore : AssetDefinitionStoreProtocol ) -> TokenScript . Token {
func getToken ( name : String , symbol : String , fromTokenIdOrEvent tokenIdOrEvent : TokenIdOrEvent , index : UInt16 , inWallet account : AlphaWallet . Address , server : RPCServer , tokenType : TokenType ) -> TokenScript . Token {
guard tokenIdOrEvent . tokenId != 0 else { return . empty }
let values : [ AttributeId : AssetAttributeSyntaxValue ]
if areFieldsEmpty {
values = . init ( )
} else {
// T O D O r e a d f r o m c a c h e a g a i n , p e r h a p s b a s e d o n a t i m e o u t / T T L f o r e a c h a t t r i b u t e . T h e r e w a s a b u g w i t h r e a d i n g f r o m c a c h e s o m e t i m e s . e . g . c a c h e a t o k e n w i t h 8 t o k e n o r i g i n a t t r i b u t e s a n d 1 f u n c t i o n o r i g i n a t t r i b u t e a n d w h e n d i s p l a y i n g i t a n d r e a d i n g f r o m t h e c a c h e , s o m e t i m e s i t ' l l o n l y r e t u r n t h e 1 f u n c t i o n o r i g i n a t t r i b u t e i n t h e c a c h e
values = resolveAttributesBypassingCache ( withTokenIdOrEvent : tokenIdOrEvent , server : server , account : account , assetDefinitionStore : assetDefinitionStore )
values = resolveAttributesBypassingCache ( withTokenIdOrEvent : tokenIdOrEvent , server : server , account : account , assetAttributeResolver : assetAttributeResolver )
}
return TokenScript . Token ( tokenIdOrEvent : tokenIdOrEvent , tokenType : tokenType , index : index , name : name , symbol : symbol , status : . available , values : values )
}
func getAttestationName ( ) -> String ? {
return attestationName
}
func getAttestationDescription ( ) -> String ? {
return attestationDescription
}
private var areFieldsEmpty : Bool {
var areFieldsEmpty : Bool = true
threadSafe . performSync {
@ -611,18 +645,10 @@ public class PrivateXMLHandler {
return areFieldsEmpty
}
func resolveAttributesBypassingCache ( withTokenIdOrEvent tokenIdOrEvent : TokenIdOrEvent , server : RPCServer , account : AlphaWallet . Address , assetDefinitionStore : AssetDefinitionStoreProtocol ) -> [ AttributeId : AssetAttributeSyntaxValue ] {
fileprivate func resolveAttributesBypassingCache ( withTokenIdOrEvent tokenIdOrEvent : TokenIdOrEvent , server : RPCServer , account : AlphaWallet . Address , assetAttributeResolver : AssetAttributeResolver ) -> [ AttributeId : AssetAttributeSyntaxValue ] {
var attributes : [ AttributeId : AssetAttributeSyntaxValue ] = [ : ]
threadSafe . performSync {
attributes = assetDefinitionStore
. assetAttributeResolver
. resolve ( withTokenIdOrEvent : tokenIdOrEvent ,
userEntryValues : . init ( ) ,
server : server ,
account : account ,
additionalValues : . init ( ) ,
localRefs : . init ( ) ,
attributes : _fields )
attributes = assetAttributeResolver . resolve ( withTokenIdOrEvent : tokenIdOrEvent , userEntryValues : . init ( ) , server : server , account : account , additionalValues : . init ( ) , localRefs : . init ( ) , attributes : _fields )
}
return attributes
}
@ -684,7 +710,7 @@ public class PrivateXMLHandler {
private func createFunctionOriginFrom ( ethereumFunctionElement : XMLElement ) -> FunctionOrigin ? {
if let contract = ethereumFunctionElement [ " contract " ] . nilIfEmpty {
guard let server = server else { return nil }
return XMLHandler . functional . getNonTokenHoldingContract ( byName : contract , server : server , fromContractNamesAndAddresses : self . contractNamesAndAddresses )
return XMLHandler . getNonTokenHoldingContract ( byName : contract , server : server , fromContractNamesAndAddresses : contractNamesAndAddresses )
. flatMap { FunctionOrigin ( forEthereumFunctionTransactionElement : ethereumFunctionElement , root : xml , originContract : $0 , xmlContext : xmlContext , bitmask : nil , bitShift : 0 ) }
} else {
return XMLHandler . getRecipientAddress ( fromEthereumFunctionElement : ethereumFunctionElement , xmlContext : xmlContext )
@ -713,6 +739,14 @@ public class PrivateXMLHandler {
}
}
private func extractFieldsForAttestation ( ) -> [ AttestationAttribute ] {
if let tokensElement = XMLHandler . getTokenElement ( fromRoot : xml , xmlContext : xmlContext ) {
return extractFieldsForAttestation ( fromElementContainingAttributes : tokensElement )
} else {
return . init ( )
}
}
private func extractSelectionsForToken ( ) -> [ TokenScriptSelection ] {
XMLHandler . getSelectionElements ( fromRoot : xml , xmlContext : xmlContext ) . compactMap { each in
guard let id = each [ " name " ] , let filter = each [ " filter " ] else { return nil }
@ -741,11 +775,24 @@ public class PrivateXMLHandler {
}
return fields
case . attestation :
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t t o i m p l e m e n t s u p p o r t f o r e x t r a c t F i e l d s
var fields = [ AttributeId : AssetAttribute ] ( )
for each in XMLHandler . getAttributeElements ( fromAttributeElement : element , xmlContext : xmlContext ) {
guard let name = each [ " name " ] else { continue }
}
// T O D O a t t r i b u t e s f o r t o k e n a n d a t t e s t a t i o n s a r e s e p a r a t e f o r n o w u n t i l i t ' s n e c e s s a r y t o c o m b i n e t h e m
return [ : ]
}
}
private func extractFieldsForAttestation ( fromElementContainingAttributes element : XMLElement ) -> [ AttestationAttribute ] {
switch target {
case . token :
// T O D O a t t r i b u t e s f o r t o k e n a n d a t t e s t a t i o n s a r e s e p a r a t e f o r n o w u n t i l i t ' s n e c e s s a r y t o c o m b i n e t h e m
return [ ]
case . attestation :
var fields : [ AttestationAttribute ] = XMLHandler
. getAttestationAttributeElements ( fromAttributeElement : element , xmlContext : xmlContext )
. compactMap {
guard let path = $0 [ " name " ] else { return nil }
guard let label = $0 . text else { return nil }
return AttestationAttribute ( label : label , path : path )
}
return fields
}
}
@ -787,8 +834,8 @@ public class PrivateXMLHandler {
return . init ( namespacePrefix : rootNamespacePrefix , namespaces : namespaces , lang : lang )
}
private static let regex = try ? NSRegularExpression ( pattern : " < \\ !ENTITY \\ s+(.*) \\ s+SYSTEM \\ s+ \" (.*) \" > " , options : [ ] )
fileprivate static func getEntities ( inXml xml : String ) -> [ TokenScriptFileIndices . Entity ] {
var entities = [ TokenScriptFileIndices . Entity ] ( )
fileprivate static func getEntities ( inXml xml : String ) -> [ XMLHandler . Entity ] {
var entities = [ XMLHandler . Entity ] ( )
if let regex = Self . regex {
regex . enumerateMatches ( in : xml , options : [ ] , range : . init ( xml . startIndex . . < xml . endIndex , in : xml ) ) { match , _ , _ in
@ -796,15 +843,72 @@ public class PrivateXMLHandler {
guard match . numberOfRanges = = 3 else { return }
guard let entityRange = Range ( match . range ( at : 1 ) , in : xml ) , let fileNameRange = Range ( match . range ( at : 2 ) , in : xml ) else { return }
let entityName = String ( xml [ entityRange ] )
let fileName = String ( xml [ fileNameRange ] )
entities . append ( . init ( name : entityName , fileName : fileName ) )
let fileName = Filename ( value : String ( xml [ fileNameRange ] ) )
entities . append ( XMLHandler . Entity ( name : entityName , fileName : fileName ) )
}
}
return entities
}
static func getAttestationSchemaUid ( xmlString : String ) -> Attestation . SchemaUid ? {
guard let xml = try ? Kanna . XML ( xml : xmlString , encoding : . utf8 ) else { return nil }
let xmlContext = PrivateXMLHandler . createXmlContext ( withLang : PrivateXMLHandler . lang )
return functional . getAttestationSchemaUid ( xml : xml , xmlContext : xmlContext )
}
static func getAttestationCollectionId ( xmlString : String ) -> String ? {
guard let xml = try ? Kanna . XML ( xml : xmlString , encoding : . utf8 ) else { return nil }
let xmlContext = PrivateXMLHandler . createXmlContext ( withLang : PrivateXMLHandler . lang )
return functional . computeAttestationCollectionId ( xml : xml , xmlContext : xmlContext )
}
fileprivate func resolveAttestationAttributes ( forAttestation attestation : Attestation ) -> [ Attestation . TypeValuePair ] {
return Attestation . resolveAttestationAttributes ( forAttestation : attestation , withAttestationFields : _attestationFields )
}
}
// s w i f t l i n t : e n a b l e t y p e _ b o d y _ l e n g t h
fileprivate extension PrivateXMLHandler {
enum functional { }
}
fileprivate extension PrivateXMLHandler . functional {
static func getAttestationIssuerKey ( xml : XMLDocument , xmlContext : XmlContext ) -> String ? {
if let tokensElement = XMLHandler . getTokenElement ( fromRoot : xml , xmlContext : xmlContext ) {
return XMLHandler . getAttestationIssuerKey ( fromAttributeElement : tokensElement , xmlContext : xmlContext )
} else {
return nil
}
}
static func computeAttestationCollectionId ( xml : XMLDocument , xmlContext : XmlContext ) -> String ? {
guard let tokensElement = XMLHandler . getTokenElement ( fromRoot : xml , xmlContext : xmlContext ) else { return " " }
let attestationIssuerKey : String ? = getAttestationIssuerKey ( xml : xml , xmlContext : xmlContext )
var results : [ String ] = [
Attestation . convertSignerAddressToFormatForComputingCollectionId ( signer : attestationIssuerKey . flatMap { deriveAddressFromPublicKey ( $0 ) } )
]
let collectionFieldElements = XMLHandler . getAttestationCollectionFieldElements ( fromAttributeElement : tokensElement , xmlContext : xmlContext )
for each in collectionFieldElements {
if let eachText = each . text {
results . append ( eachText )
}
}
let collectionId = results . joined ( )
if collectionId . isEmpty {
return nil
} else {
let hash = collectionId . sha3 ( . keccak256 )
return hash
}
}
static func getAttestationSchemaUid ( xml : XMLDocument , xmlContext : XmlContext ) -> Attestation . SchemaUid ? {
guard let tokensElement = XMLHandler . getTokenElement ( fromRoot : xml , xmlContext : xmlContext ) else { return nil }
let schemaUID = XMLHandler . getAttestationSchemaUid ( fromAttributeElement : tokensElement , xmlContext : xmlContext )
return schemaUID
}
}
final class ThreadSafe {
private let queue : DispatchQueue
@ -826,9 +930,19 @@ final class ThreadSafe {
// / T h i s c l a s s d e l e g a t e s a l l t h e f u n c t i o n a l i t y t o a s i n g l e t o n o f t h e a c t u a l X M L p a r s e r . 1 f o r e a c h c o n t r a c t . S o w e j u s t p a r s e t h e X M L f i l e 1 t i m e o n l y f o r e a c h c o n t r a c t
public struct XMLHandler {
struct Entity : Codable {
let name : String
let fileName : Filename
}
public var _attestationFields : [ AttestationAttribute ] {
privateXMLHandler . _attestationFields
}
public static let fileExtension = " tsml "
private let privateXMLHandler : PrivateXMLHandler
// p u b l i c b e c a u s e o f c y c l i c d e p e n d e n c y
public let privateXMLHandler : PrivateXMLHandler
private let baseXMLHandler : PrivateXMLHandler ?
public var hasAssetDefinition : Bool {
@ -864,17 +978,11 @@ public struct XMLHandler {
}
public var tokenScriptStatus : Promise < TokenLevelTokenScriptDisplayStatus > {
var tokenScriptStatus : Promise < TokenLevelTokenScriptDisplayStatus > !
tokenScriptStatus = privateXMLHandler . tokenScriptStatus
return tokenScriptStatus
return privateXMLHandler . tokenScriptStatus
}
public var introductionHtmlString : String {
var introductionHtmlString : String = " "
introductionHtmlString = privateXMLHandler . introductionHtmlString
return introductionHtmlString
return privateXMLHandler . introductionHtmlString
}
public var tokenViewIconifiedHtml : ( html : String , style : String ) {
@ -1013,113 +1121,23 @@ public struct XMLHandler {
" " "
}
public init ( contract : AlphaWallet . Address , tokenType : TokenType , assetDefinitionStore : AssetDefinitionStoreProtocol ) {
self . init ( contract : contract , optionalTokenType : tokenType , assetDefinitionStore : assetDefinitionStore )
var attestationCollectionId : String ? {
privateXMLHandler . attestationCollectionId
}
public init ( forAttestationURL url : URL , assetDefinitionStore : AssetDefinitionStoreProtocol ) {
let features = assetDefinitionStore . features
var privateXMLHandler : PrivateXMLHandler
var baseXMLHandler : PrivateXMLHandler ?
if let handler = assetDefinitionStore . getXmlHandler ( forAttestationAtURL : url ) {
privateXMLHandler = handler
} else {
privateXMLHandler = PrivateXMLHandler ( forAttestationURL : url , assetDefinitionStore : assetDefinitionStore )
assetDefinitionStore . set ( xmlHandler : privateXMLHandler , forAttestationAtURL : url )
}
// TODO: a t t e s t a t i o n s + T o k e n S c r i p t t o k e n T y p e n o t u s e d ? R e l e v a n t ?
let tokenType : TokenType ? = nil
if features . isActivityEnabled , let tokenType = tokenType {
// l e t t o k e n T y p e F o r B a s e X m l : T o k e n T y p e
// i f p r i v a t e X M L H a n d l e r . h a s V a l i d T o k e n S c r i p t F i l e , l e t t o k e n T y p e I n X m l = p r i v a t e X M L H a n d l e r . t o k e n T y p e . f l a t M a p ( { T o k e n T y p e ( t o k e n I n t e r f a c e T y p e : $ 0 ) } ) {
// t o k e n T y p e F o r B a s e X m l = t o k e n T y p e I n X m l
// } e l s e {
// t o k e n T y p e F o r B a s e X m l = t o k e n T y p e
// }
// / / K e y c a n n o t b e j u s t ` c o n t r a c t ` , b e c a u s e t h e t y p e c a n c h a n g e ( f r o m t h e o v e r r i d i n g T o k e n S c r i p t f i l e )
// l e t k e y = " \ ( c o n t r a c t . e i p 5 5 S t r i n g ) - \ ( t o k e n T y p e F o r B a s e X m l . r a w V a l u e ) "
// i f l e t h a n d l e r = a s s e t D e f i n i t i o n S t o r e . g e t B a s e X m l H a n d l e r ( f o r : k e y ) {
// b a s e X M L H a n d l e r = h a n d l e r
// } e l s e {
// i f l e t x m l = a s s e t D e f i n i t i o n S t o r e . b a s e T o k e n S c r i p t F i l e ( f o r : t o k e n T y p e F o r B a s e X m l ) {
// b a s e X M L H a n d l e r = P r i v a t e X M L H a n d l e r ( c o n t r a c t : c o n t r a c t , b a s e X m l : x m l , b a s e T o k e n T y p e : t o k e n T y p e F o r B a s e X m l , a s s e t D e f i n i t i o n S t o r e : a s s e t D e f i n i t i o n S t o r e )
// a s s e t D e f i n i t i o n S t o r e . s e t B a s e X m l H a n d l e r ( f o r : k e y , b a s e X m l H a n d l e r : b a s e X M L H a n d l e r )
// } e l s e {
// b a s e X M L H a n d l e r = n i l
// }
// }
} else {
baseXMLHandler = nil
}
self . baseXMLHandler = baseXMLHandler
self . privateXMLHandler = privateXMLHandler
var attestationSchemaUid : Attestation . SchemaUid ? {
privateXMLHandler . attestationSchemaUid
}
// p r i v a t e b e c a u s e w e d o n ' t w a n t c l i e n t c o d e c r e a t i n g X M L H a n d l e r ( s ) t o b e a b l e t o a c c i d e n t a l l y p a s s i n a n i l T o k e n T y p e
private init ( contract : AlphaWallet . Address , optionalTokenType tokenType : TokenType ? , assetDefinitionStore : AssetDefinitionStoreProtocol ) {
let features = assetDefinitionStore . features
var privateXMLHandler : PrivateXMLHandler
var baseXMLHandler : PrivateXMLHandler ?
if let handler = assetDefinitionStore . getXmlHandler ( for : contract ) {
privateXMLHandler = handler
} else {
privateXMLHandler = PrivateXMLHandler ( contract : contract , assetDefinitionStore : assetDefinitionStore )
assetDefinitionStore . set ( xmlHandler : privateXMLHandler , for : contract )
}
if features . isActivityEnabled , let tokenType = tokenType {
let tokenTypeForBaseXml : TokenType
if privateXMLHandler . hasValidTokenScriptFile , let tokenTypeInXml = privateXMLHandler . tokenType . flatMap ( { TokenType ( tokenInterfaceType : $0 ) } ) {
tokenTypeForBaseXml = tokenTypeInXml
} else {
tokenTypeForBaseXml = tokenType
}
// K e y c a n n o t b e j u s t ` c o n t r a c t ` , b e c a u s e t h e t y p e c a n c h a n g e ( f r o m t h e o v e r r i d i n g T o k e n S c r i p t f i l e )
let key = " \( contract . eip55String ) - \( tokenTypeForBaseXml . rawValue ) "
if let handler = assetDefinitionStore . getBaseXmlHandler ( for : key ) {
baseXMLHandler = handler
} else {
if let xml = assetDefinitionStore . baseTokenScriptFile ( for : tokenTypeForBaseXml ) {
baseXMLHandler = PrivateXMLHandler ( contract : contract , baseXml : xml , baseTokenType : tokenTypeForBaseXml , assetDefinitionStore : assetDefinitionStore )
assetDefinitionStore . setBaseXmlHandler ( for : key , baseXmlHandler : baseXMLHandler )
} else {
baseXMLHandler = nil
}
}
} else {
baseXMLHandler = nil
}
init ( baseXMLHandler : PrivateXMLHandler ? , privateXMLHandler : PrivateXMLHandler ) {
self . baseXMLHandler = baseXMLHandler
self . privateXMLHandler = privateXMLHandler
}
public func getToken ( name : String , symbol : String , fromTokenIdOrEvent tokenIdOrEvent : TokenIdOrEvent , index : UInt16 , inWallet account : AlphaWallet . Address , server : RPCServer , tokenType : TokenType , assetDefinitionStore : AssetDefinitionStoreProtocol ) -> TokenScript . Token {
let overriden = privateXMLHandler . getToken (
name : name ,
symbol : symbol ,
fromTokenIdOrEvent : tokenIdOrEvent ,
index : index ,
inWallet : account ,
server : server ,
tokenType : tokenType ,
assetDefinitionStore : assetDefinitionStore )
public func getToken ( name : String , symbol : String , fromTokenIdOrEvent tokenIdOrEvent : TokenIdOrEvent , index : UInt16 , inWallet account : AlphaWallet . Address , server : RPCServer , tokenType : TokenType ) -> TokenScript . Token {
let overriden = privateXMLHandler . getToken ( name : name , symbol : symbol , fromTokenIdOrEvent : tokenIdOrEvent , index : index , inWallet : account , server : server , tokenType : tokenType )
if let baseXMLHandler = baseXMLHandler {
let base = baseXMLHandler . getToken (
name : name ,
symbol : symbol ,
fromTokenIdOrEvent : tokenIdOrEvent ,
index : index ,
inWallet : account ,
server : server ,
tokenType : tokenType ,
assetDefinitionStore : assetDefinitionStore )
let base = baseXMLHandler . getToken ( name : name , symbol : symbol , fromTokenIdOrEvent : tokenIdOrEvent , index : index , inWallet : account , server : server , tokenType : tokenType )
let baseValues = base . values
let overriddenValues = overriden . values
@ -1160,22 +1178,20 @@ public struct XMLHandler {
return nameInPluralForm
}
public func resolveAttributesBypassingCache ( withTokenIdOrEvent tokenIdOrEvent : TokenIdOrEvent , server : RPCServer , account : AlphaWallet . Address , assetDefinitionStore : AssetDefinitionStoreProtocol ) -> [ AttributeId : AssetAttributeSyntaxValue ] {
var attributes : [ AttributeId : AssetAttributeSyntaxValue ] = [ : ]
let overrides = privateXMLHandler . resolveAttributesBypassingCache (
withTokenIdOrEvent : tokenIdOrEvent ,
server : server ,
account : account ,
assetDefinitionStore : assetDefinitionStore )
public func getAttestationName ( ) -> String ? {
privateXMLHandler . getAttestationName ( )
}
public func getAttestationDescription ( ) -> String ? {
privateXMLHandler . getAttestationDescription ( )
}
public func resolveAttributesBypassingCache ( withTokenIdOrEvent tokenIdOrEvent : TokenIdOrEvent , server : RPCServer , account : AlphaWallet . Address ) -> [ AttributeId : AssetAttributeSyntaxValue ] {
var attributes : [ AttributeId : AssetAttributeSyntaxValue ] = [ : ]
let overrides = privateXMLHandler . resolveAttributesBypassingCache ( withTokenIdOrEvent : tokenIdOrEvent , server : server , account : account , assetAttributeResolver : privateXMLHandler . assetAttributeResolver )
if let baseXMLHandler = baseXMLHandler {
// T O D O T h i s i s i n e f f i c i e n t b e c a u s e o v e r r i d d e n a t t r i b u t e s g e t r e s o l v e d t o o
let base = baseXMLHandler . resolveAttributesBypassingCache (
withTokenIdOrEvent : tokenIdOrEvent ,
server : server ,
account : account ,
assetDefinitionStore : assetDefinitionStore )
let base = baseXMLHandler . resolveAttributesBypassingCache ( withTokenIdOrEvent : tokenIdOrEvent , server : server , account : account , assetAttributeResolver : privateXMLHandler . assetAttributeResolver )
attributes = base . merging ( overrides ) { _ , new in new }
} else {
attributes = overrides
@ -1183,28 +1199,41 @@ public struct XMLHandler {
return attributes
}
}
extension XMLHandler {
public enum functional { }
}
public func computeAttestationIdentifyingFieldNames ( forAttestation attestation : Attestation ) -> [ String ] {
return privateXMLHandler . computeAttestationIdFieldNames ( forAttestation : attestation )
}
extension XMLHandler . functional {
public func computeCollectionIdFieldNames ( forAttestation attestation : Attestation ) -> [ String ] {
return privateXMLHandler . computeCollectionIdFieldNames ( forAttestation : attestation )
}
public static func tokenScriptStatus ( forContract contract : AlphaWallet . Address , assetDefinitionStore : AssetDefinitionStoreProtocol ) -> Promise < TokenLevelTokenScriptDisplayStatus > {
XMLHandler ( contract : contract , optionalTokenType : nil , assetDefinitionStore : assetDefinitionStore ) . tokenScriptStatus
public static func getAttestationSchemaUid ( xmlString : String ) -> Attestation . SchemaUid ? {
return PrivateXMLHandler . getAttestationSchemaUid ( xmlString : xmlString )
}
public static func getNonTokenHoldingContract ( byName name : String , server : RPCServerOrAny , fromContractNamesAndAddresses contractNamesAndAddresses : [ String : [ ( AlphaWallet . Address , RPCServer ) ] ] ) -> AlphaWallet . Address ? {
guard let addressesAndServers = contractNamesAndAddresses [ name ] else { return nil }
switch server {
case . any :
// T O D O r e t u r n i n g t h e f i r s t s e e m s a r b i t r a r y , b u t I d o n ' t t h i n k T o k e n S c r i p t d e s i g n h a s e x p l o r e d t h i s a r e a y e t
guard let ( contract , _ ) = addressesAndServers . first else { return nil }
return contract
case . server ( let server ) :
guard let ( contract , _ ) = addressesAndServers . first ( where : { $0 . 1 = = server } ) else { return nil }
return contract
public static func getAttestationCollectionId ( xmlString : String ) -> String ? {
return PrivateXMLHandler . getAttestationCollectionId ( xmlString : xmlString )
}
public func resolveAttestationAttributes ( forAttestation attestation : Attestation ) -> [ Attestation . TypeValuePair ] {
return privateXMLHandler . resolveAttestationAttributes ( forAttestation : attestation )
}
static func getEntities ( forTokenScript xml : String ) -> [ Entity ] {
return PrivateXMLHandler . getEntities ( inXml : xml )
}
static func isTokenScriptSupportedSchemaVersion ( _ url : URL ) -> Bool {
switch checkTokenScriptSchema ( forPath : url ) {
case . supportedTokenScriptVersion :
return true
case . unsupportedTokenScriptVersion :
return false
case . unknownXml :
return false
case . others :
return false
}
}
@ -1213,7 +1242,7 @@ extension XMLHandler.functional {
// L a n g d o e s n ' t m a t t e r
let xmlContext = PrivateXMLHandler . createXmlContext ( withLang : " en " )
switch XMLHandler . functional . checkTokenScriptSchema ( xmlString ) {
switch checkTokenScriptSchema ( xmlString ) {
case . supportedTokenScriptVersion :
if let xml = try ? Kanna . XML ( xml : xmlString , encoding : . utf8 ) {
return PrivateXMLHandler . getHoldingContracts ( xml : xml , xmlContext : xmlContext )
@ -1223,11 +1252,19 @@ extension XMLHandler.functional {
case . unsupportedTokenScriptVersion , . unknownXml , . others :
return nil
}
}
public static func getEntities ( forTokenScript xml : String ) -> [ TokenScriptFileIndices . Entity ] {
return PrivateXMLHandler . getEntities ( inXml : xml )
public static func getNonTokenHoldingContract ( byName name : String , server : RPCServerOrAny , fromContractNamesAndAddresses contractNamesAndAddresses : [ String : [ ( AlphaWallet . Address , RPCServer ) ] ] ) -> AlphaWallet . Address ? {
guard let addressesAndServers = contractNamesAndAddresses [ name ] else { return nil }
switch server {
case . any :
// T O D O r e t u r n i n g t h e f i r s t s e e m s a r b i t r a r y , b u t I d o n ' t t h i n k T o k e n S c r i p t d e s i g n h a s e x p l o r e d t h i s a r e a y e t
guard let ( contract , _ ) = addressesAndServers . first else { return nil }
return contract
case . server ( let server ) :
guard let ( contract , _ ) = addressesAndServers . first ( where : { $0 . 1 = = server } ) else { return nil }
return contract
}
}
public static func checkTokenScriptSchema ( forPath path : URL ) -> TokenScriptSchema {
@ -1244,19 +1281,6 @@ extension XMLHandler.functional {
}
}
public static func isTokenScriptSupportedSchemaVersion ( _ url : URL ) -> Bool {
switch XMLHandler . functional . checkTokenScriptSchema ( forPath : url ) {
case . supportedTokenScriptVersion :
return true
case . unsupportedTokenScriptVersion :
return false
case . unknownXml :
return false
case . others :
return false
}
}
public static func checkTokenScriptSchema ( _ contents : String ) -> TokenScriptSchema {
// I t ' s f i n e t o h a v e a f i l e t h a t i s e m p t y . A C S S f i l e f o r e x a m p l e . B u t w e s h o u l d e x p e c t t h e i n p u t t o b e X M L
if let xml = try ? Kanna . XML ( xml : contents , encoding : . utf8 ) {
@ -1282,5 +1306,16 @@ extension XMLHandler.functional {
return . unknownXml
}
}
static func hasValidTokenScriptFileExtension ( url : URL ) -> Bool {
return url . pathExtension = = XMLHandler . fileExtension || url . pathExtension = = " xml "
}
}
extension XMLHandler {
public enum functional { }
}
fileprivate extension XMLHandler . functional {
}
// s w i f t l i n t : e n a b l e f i l e _ l e n g t h