github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/manifest/manifest_read.go (about) 1 package manifest 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 9 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 10 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 11 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" 12 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 13 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 14 ) 15 16 // LoadManifest reads the manifest from a file or from the Unchained Index smart contract. 17 // 18 // It first checks if the manifest file exists. If it does, it reads the manifest from the file. 19 // If the caller requests the contract or the cached manifest does not exist, it reads the 20 // manifest from the contract. It then checks if the new manifest has more chunks than the existing 21 // one. If it does (or if the file didn't exist), it saves the new manifest to the file. Finally, it 22 // creates a map of chunks for easy lookup and sets the specification if it is not already set. 23 func LoadManifest(chain string, publisher base.Address, source Source) (man *Manifest, err error) { 24 manifestFn := config.PathToManifest(chain) 25 exists := file.FileExists(manifestFn) 26 man = &Manifest{} 27 28 if exists { 29 // We will either return this or use it to compare with the downlaoded manifest 30 // to see if we need to update the file. 31 man, err = readManifestFile(manifestFn) 32 if err != nil { 33 return nil, err 34 } 35 if man.Chain != chain { 36 msg := fmt.Sprintf("The remote manifest's chain (%s) does not match the cached manifest's chain (%s).", man.Chain, chain) 37 return man, errors.New(msg) 38 } 39 } 40 41 if source == TempContract || source == FromContract || !exists { 42 database := chain 43 cid, err := ReadUnchainedIndex(chain, publisher, database) 44 if err != nil { 45 return nil, err 46 } else if len(cid) == 0 { 47 return nil, fmt.Errorf("no record found in the Unchained Index for database %s from publisher %s", database, publisher.Hex()) 48 } 49 gatewayUrl := config.GetChain(chain).IpfsGateway 50 logger.InfoTable("Chain:", chain) 51 logger.InfoTable("Database:", database) 52 logger.InfoTable("Publisher:", publisher) 53 logger.InfoTable("Gateway:", gatewayUrl) 54 logger.InfoTable("CID:", cid) 55 56 newManifest, err := downloadManifest(chain, gatewayUrl, cid) 57 if err != nil { 58 return nil, err 59 } 60 if newManifest.Chain != chain { 61 msg := fmt.Sprintf("The remote manifest's chain (%s) does not match the cached manifest's chain (%s).", newManifest.Chain, chain) 62 return newManifest, errors.New(msg) 63 } 64 if source != TempContract { 65 err = newManifest.SaveManifest(chain, manifestFn) 66 if err != nil { 67 return nil, err 68 } 69 } 70 71 man = newManifest 72 } 73 74 man.ChunkMap = make(map[string]*types.ChunkRecord) 75 for i := range man.Chunks { 76 man.ChunkMap[man.Chunks[i].Range] = &man.Chunks[i] 77 } 78 79 return man, nil 80 } 81 82 var specification = "QmUyyU8wKW57c3CuwphhMdZb2QA5bsjt9vVfTE6LcBKmE9" 83 84 func Specification() string { 85 return specification 86 } 87 88 // readManifestFile reads the manifest from a file. 89 // 90 // It first reads the contents of the file into a string. It then decodes the string into the manifest. 91 func readManifestFile(path string) (*Manifest, error) { 92 contents := file.AsciiFileToString(path) 93 if len(contents) == 0 { 94 return nil, ErrManifestNotFound 95 } 96 97 man := &Manifest{} 98 reader := bytes.NewReader([]byte(contents)) 99 if err := json.NewDecoder(reader).Decode(man); err != nil { 100 return man, err 101 } 102 103 return man, nil 104 }