github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/manifest/download.go (about) 1 // Copyright 2021 The TrueBlocks Authors. All rights reserved. 2 // Use of this source code is governed by a license that can 3 // be found in the LICENSE file. 4 5 package manifest 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "net/http" 11 "net/url" 12 "os" 13 "path" 14 "strings" 15 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/abi" 17 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/articulate" 18 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 19 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/call" 20 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 21 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/debug" 22 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 23 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 24 ethAbi "github.com/ethereum/go-ethereum/accounts/abi" 25 ) 26 27 // ReadUnchainedIndex calls UnchainedIndex smart contract to get the current manifest IPFS CID as 28 // published by the given publisher 29 func ReadUnchainedIndex(chain string, publisher base.Address, database string) (string, error) { 30 cid := os.Getenv("TB_OVERRIDE_CID") 31 if cid != "" { 32 return cid, nil 33 } 34 35 callAddress, abiMap, err := getUnchainedAbi() 36 if err != nil { 37 return "", err 38 } 39 40 unchainedChain := "mainnet" // the unchained index is on mainnet 41 conn := rpc.TempConnection(unchainedChain) 42 // if conn.LatestBlockTimestamp < 1_705_173_443 { // block 19_000_000 43 // provider := config.GetChain(unchainedChain).RpcProvider 44 // logger.Fatal(usage.Usage(unchainedWarning, provider)) 45 // } 46 47 theCall := fmt.Sprintf("manifestHashMap(%s, \"%s\")", publisher, database) 48 if contractCall, _, err := call.NewContractCallWithAbi(conn, callAddress, theCall, abiMap); err != nil { 49 wrapped := fmt.Errorf("the --call value provided (%s) was not found: %s", theCall, err) 50 return "", wrapped 51 } else { 52 contractCall.BlockNumber = conn.GetLatestBlockNumber() 53 artFunc := func(str string, function *types.Function) error { 54 return articulate.ArticulateFunction(function, "", str[2:]) 55 } 56 if result, err := contractCall.Call(artFunc); err != nil { 57 return "", err 58 } else { 59 return result.Values["hash"], nil 60 } 61 } 62 } 63 64 // downloadManifest downloads manifest from the given gateway and parses it into 65 // Manifest struct. Both JSON and TSV formats are supported, but the server has 66 // to set the correct Content-Type header. 67 func downloadManifest(chain, gatewayUrl, cid string) (*Manifest, error) { 68 _ = chain // linter 69 url, err := url.Parse(gatewayUrl) 70 if err != nil { 71 return nil, err 72 } 73 url.Path = path.Join(url.Path, cid) 74 75 debug.DebugCurlStr(url.String()) 76 resp, err := http.Get(url.String()) 77 if err != nil { 78 return nil, err 79 } 80 81 if resp.StatusCode != http.StatusOK { 82 return nil, fmt.Errorf("fetch to pinning service (%s) failed: %s", url.String(), resp.Status) 83 } 84 85 switch resp.Header.Get("Content-Type") { 86 case "application/json": 87 m := &Manifest{} 88 err := json.NewDecoder(resp.Body).Decode(m) 89 return m, err 90 default: 91 return nil, fmt.Errorf("fetch to %s return unrecognized content type: %s", url.String(), resp.Header.Get("Content-Type")) 92 } 93 } 94 95 func getUnchainedAbi() (base.Address, *abi.SelectorSyncMap, error) { 96 abiMap := &abi.SelectorSyncMap{} 97 callAddress := base.HexToAddress(config.GetUnchained().SmartContract) 98 99 var unchainedAbiJson = `[ 100 { 101 "name": "manifestHashMap", 102 "type": "function", 103 "signature": "manifestHashMap(address,string)", 104 "encoding": "0x7087e4bd", 105 "inputs": [ 106 { 107 "type": "address", 108 "name": "publisher", 109 "internalType": "address" 110 }, 111 { 112 "type": "string", 113 "name": "database", 114 "internalType": "string" 115 } 116 ], 117 "outputs": [ 118 { 119 "type": "string", 120 "name": "hash", 121 "internalType": "string" 122 } 123 ] 124 } 125 ]` 126 127 if abi, err := ethAbi.JSON(strings.NewReader(unchainedAbiJson)); err != nil { 128 return base.Address{}, abiMap, err 129 } else { 130 method := abi.Methods["manifestHashMap"] 131 function := types.FunctionFromAbiMethod(&method) 132 abiMap.SetValue(function.Encoding, function) 133 } 134 135 return callAddress, abiMap, nil 136 } 137 138 // var unchainedWarning string = ` 139 // The Unchained Index requires your mainnet RPC to be synced (at least to block 0x1304073 or 19000000). 140 // Check the progress with the following curl command and try again later. 141 142 // curl -X POST -H "Content-Type: application/json" --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' {0} 143 // `