gitlab.com/flarenetwork/coreth@v0.1.1/core/state_connector.go (about)

     1  // (c) 2021, Flare Networks Limited. All rights reserved.
     2  // Please see the file LICENSE for licensing terms.
     3  
     4  package core
     5  
     6  import (
     7  	"bytes"
     8  	"crypto/sha256"
     9  	"encoding/binary"
    10  	"encoding/hex"
    11  	"encoding/json"
    12  	"fmt"
    13  	"io/ioutil"
    14  	"math"
    15  	"math/big"
    16  	"net/http"
    17  	"os"
    18  	"strconv"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/common/hexutil"
    24  	"github.com/ethereum/go-ethereum/crypto"
    25  )
    26  
    27  var (
    28  	testingChainID               = new(big.Int).SetUint64(16)
    29  	stateConnectorActivationTime = new(big.Int).SetUint64(1636070400)
    30  	tr                           = &http.Transport{
    31  		MaxIdleConns:        100,
    32  		MaxConnsPerHost:     100,
    33  		MaxIdleConnsPerHost: 100,
    34  		IdleConnTimeout:     60 * time.Second,
    35  		DisableCompression:  true,
    36  	}
    37  	client = &http.Client{
    38  		Transport: tr,
    39  		Timeout:   5 * time.Second,
    40  	}
    41  	apiRetries    = 3
    42  	apiRetryDelay = 1 * time.Second
    43  )
    44  
    45  func GetStateConnectorActivated(chainID *big.Int, blockTime *big.Int) bool {
    46  	// Return true if chainID is 16 or if block.timestamp is greater than the state connector activation time on any chain
    47  	return chainID.Cmp(testingChainID) == 0 || blockTime.Cmp(stateConnectorActivationTime) > 0
    48  }
    49  
    50  func GetStateConnectorGasDivisor(blockTime *big.Int) uint64 {
    51  	switch {
    52  	default:
    53  		return 3
    54  	}
    55  }
    56  
    57  func GetMaxAllowedChains(blockTime *big.Int) uint32 {
    58  	switch {
    59  	default:
    60  		return 5
    61  	}
    62  }
    63  
    64  func GetStateConnectorContractAddr(blockTime *big.Int) string {
    65  	switch {
    66  	default:
    67  		return "0x1000000000000000000000000000000000000001"
    68  	}
    69  }
    70  
    71  func GetProveDataAvailabilityPeriodFinalitySelector(blockTime *big.Int) []byte {
    72  	switch {
    73  	default:
    74  		return []byte{0xc5, 0xd6, 0x4c, 0xd1}
    75  	}
    76  }
    77  
    78  func GetProvePaymentFinalitySelector(blockTime *big.Int) []byte {
    79  	switch {
    80  	default:
    81  		return []byte{0x38, 0x84, 0x92, 0xdd}
    82  	}
    83  }
    84  
    85  func GetDisprovePaymentFinalitySelector(blockTime *big.Int) []byte {
    86  	switch {
    87  	default:
    88  		return []byte{0x7f, 0x58, 0x24, 0x32}
    89  	}
    90  }
    91  
    92  // =======================================================
    93  // Proof of Work Common
    94  // =======================================================
    95  
    96  type GetPoWRequestPayload struct {
    97  	Method string   `json:"method"`
    98  	Params []string `json:"params"`
    99  }
   100  type GetPoWBlockCountResp struct {
   101  	Result uint64      `json:"result"`
   102  	Error  interface{} `json:"error"`
   103  }
   104  
   105  func GetPoWBlockCount(chainURL string, username string, password string) (uint64, bool) {
   106  	data := GetPoWRequestPayload{
   107  		Method: "getblockcount",
   108  		Params: []string{},
   109  	}
   110  	payloadBytes, err := json.Marshal(data)
   111  	if err != nil {
   112  		return 0, true
   113  	}
   114  	body := bytes.NewReader(payloadBytes)
   115  	req, err := http.NewRequest("POST", chainURL, body)
   116  	if err != nil {
   117  		return 0, true
   118  	}
   119  	req.Header.Set("Content-Type", "application/json")
   120  	if username != "" && password != "" {
   121  		req.SetBasicAuth(username, password)
   122  	}
   123  	resp, err := client.Do(req)
   124  	if err != nil {
   125  		return 0, true
   126  	}
   127  	defer resp.Body.Close()
   128  	if resp.StatusCode != 200 {
   129  		return 0, true
   130  	}
   131  	respBody, err := ioutil.ReadAll(resp.Body)
   132  	if err != nil {
   133  		return 0, true
   134  	}
   135  	var jsonResp GetPoWBlockCountResp
   136  	err = json.Unmarshal(respBody, &jsonResp)
   137  	if err != nil {
   138  		return 0, true
   139  	}
   140  	if jsonResp.Error != nil {
   141  		return 0, true
   142  	}
   143  	return jsonResp.Result, false
   144  }
   145  
   146  type GetPoWBlockHeaderResult struct {
   147  	Hash          string `json:"hash"`
   148  	Confirmations uint64 `json:"confirmations"`
   149  	Height        uint64 `json:"height"`
   150  }
   151  type GetPoWBlockHeaderResp struct {
   152  	Result GetPoWBlockHeaderResult `json:"result"`
   153  	Error  interface{}             `json:"error"`
   154  }
   155  
   156  func GetPoWBlockHeader(ledgerHash string, requiredConfirmations uint64, chainURL string, username string, password string) (uint64, bool) {
   157  	data := GetPoWRequestPayload{
   158  		Method: "getblockheader",
   159  		Params: []string{
   160  			ledgerHash,
   161  		},
   162  	}
   163  	payloadBytes, err := json.Marshal(data)
   164  	if err != nil {
   165  		return 0, true
   166  	}
   167  	body := bytes.NewReader(payloadBytes)
   168  	req, err := http.NewRequest("POST", chainURL, body)
   169  	if err != nil {
   170  		return 0, true
   171  	}
   172  	req.Header.Set("Content-Type", "application/json")
   173  	if username != "" && password != "" {
   174  		req.SetBasicAuth(username, password)
   175  	}
   176  	resp, err := client.Do(req)
   177  	if err != nil {
   178  		return 0, true
   179  	}
   180  	defer resp.Body.Close()
   181  	if resp.StatusCode != 200 {
   182  		return 0, true
   183  	}
   184  	respBody, err := ioutil.ReadAll(resp.Body)
   185  	if err != nil {
   186  		return 0, true
   187  	}
   188  	var jsonResp GetPoWBlockHeaderResp
   189  	err = json.Unmarshal(respBody, &jsonResp)
   190  	if err != nil {
   191  		return 0, true
   192  	}
   193  	if jsonResp.Error != nil {
   194  		return 0, false
   195  	} else if jsonResp.Result.Confirmations < requiredConfirmations {
   196  		return 0, false
   197  	}
   198  	return jsonResp.Result.Height, false
   199  }
   200  
   201  func ProveDataAvailabilityPeriodFinalityPoW(checkRet []byte, chainURL string, username string, password string) (bool, bool) {
   202  	blockCount, err := GetPoWBlockCount(chainURL, username, password)
   203  	if err {
   204  		return false, true
   205  	}
   206  	ledger := binary.BigEndian.Uint64(checkRet[56:64])
   207  	requiredConfirmations := binary.BigEndian.Uint64(checkRet[88:96])
   208  	if blockCount < ledger+requiredConfirmations {
   209  		return false, true
   210  	}
   211  	ledgerResp, err := GetPoWBlockHeader(hex.EncodeToString(checkRet[96:128]), requiredConfirmations, chainURL, username, password)
   212  	if err {
   213  		return false, true
   214  	} else if ledgerResp > 0 && ledgerResp == ledger {
   215  		return true, false
   216  	} else {
   217  		return false, false
   218  	}
   219  }
   220  
   221  type GetPoWTxRequestParams struct {
   222  	TxID    string `json:"txid"`
   223  	Verbose bool   `json:"verbose"`
   224  }
   225  type GetPoWTxRequestPayload struct {
   226  	Method string                `json:"method"`
   227  	Params GetPoWTxRequestParams `json:"params"`
   228  }
   229  type GetPoWTxResult struct {
   230  	TxID          string `json:"txid"`
   231  	BlockHash     string `json:"blockhash"`
   232  	Confirmations uint64 `json:"confirmations"`
   233  	Vout          []struct {
   234  		Value        float64 `json:"value"`
   235  		N            uint64  `json:"n"`
   236  		ScriptPubKey struct {
   237  			Type      string   `json:"type"`
   238  			Addresses []string `json:"addresses"`
   239  		} `json:"scriptPubKey"`
   240  	} `json:"vout"`
   241  }
   242  type GetPoWTxResp struct {
   243  	Result GetPoWTxResult `json:"result"`
   244  	Error  interface{}    `json:"error"`
   245  }
   246  
   247  func GetPoWTx(txHash string, voutN uint64, latestAvailableBlock uint64, currencyCode string, chainURL string, username string, password string) ([]byte, uint64, bool) {
   248  	data := GetPoWTxRequestPayload{
   249  		Method: "getrawtransaction",
   250  		Params: GetPoWTxRequestParams{
   251  			TxID:    txHash[1:],
   252  			Verbose: true,
   253  		},
   254  	}
   255  	payloadBytes, err := json.Marshal(data)
   256  	if err != nil {
   257  		return []byte{}, 0, true
   258  	}
   259  	body := bytes.NewReader(payloadBytes)
   260  	req, err := http.NewRequest("POST", chainURL, body)
   261  	if err != nil {
   262  		return []byte{}, 0, true
   263  	}
   264  	req.Header.Set("Content-Type", "application/json")
   265  	if username != "" && password != "" {
   266  		req.SetBasicAuth(username, password)
   267  	}
   268  	resp, err := client.Do(req)
   269  	if err != nil {
   270  		return []byte{}, 0, true
   271  	}
   272  	defer resp.Body.Close()
   273  	if resp.StatusCode != 200 {
   274  		return []byte{}, 0, true
   275  	}
   276  	respBody, err := ioutil.ReadAll(resp.Body)
   277  	if err != nil {
   278  		return []byte{}, 0, true
   279  	}
   280  	var jsonResp GetPoWTxResp
   281  	err = json.Unmarshal(respBody, &jsonResp)
   282  	if err != nil {
   283  		return []byte{}, 0, true
   284  	}
   285  	if jsonResp.Error != nil {
   286  		return []byte{}, 0, true
   287  	}
   288  	if uint64(len(jsonResp.Result.Vout)) <= voutN {
   289  		return []byte{}, 0, false
   290  	}
   291  	if jsonResp.Result.Vout[voutN].ScriptPubKey.Type != "pubkeyhash" || len(jsonResp.Result.Vout[voutN].ScriptPubKey.Addresses) != 1 {
   292  		return []byte{}, 0, false
   293  	}
   294  	inBlock, getBlockErr := GetPoWBlockHeader(jsonResp.Result.BlockHash, jsonResp.Result.Confirmations, chainURL, username, password)
   295  	if getBlockErr {
   296  		return []byte{}, 0, true
   297  	}
   298  	if inBlock == 0 || inBlock >= latestAvailableBlock {
   299  		return []byte{}, 0, false
   300  	}
   301  	txIdHash := crypto.Keccak256([]byte(txHash))
   302  	destinationHash := crypto.Keccak256([]byte(jsonResp.Result.Vout[voutN].ScriptPubKey.Addresses[0]))
   303  	amountHash := crypto.Keccak256(common.LeftPadBytes(common.FromHex(hexutil.EncodeUint64(uint64(jsonResp.Result.Vout[voutN].Value*math.Pow(10, 8)))), 32))
   304  	currencyHash := crypto.Keccak256([]byte(currencyCode))
   305  	return crypto.Keccak256(txIdHash, destinationHash, amountHash, currencyHash), inBlock, false
   306  }
   307  
   308  func ProvePaymentFinalityPoW(checkRet []byte, isDisprove bool, currencyCode string, chainURL string, username string, password string) (bool, bool) {
   309  	if len(checkRet) < 257 {
   310  		return false, false
   311  	}
   312  	voutN, err := strconv.ParseUint(string(checkRet[192:193]), 16, 64)
   313  	if err != nil {
   314  		return false, false
   315  	}
   316  	paymentHash, inBlock, getPoWTxErr := GetPoWTx(string(checkRet[192:257]), voutN, binary.BigEndian.Uint64(checkRet[88:96]), currencyCode, chainURL, username, password)
   317  	if getPoWTxErr {
   318  		return false, true
   319  	}
   320  	if !isDisprove {
   321  		if len(paymentHash) > 0 && bytes.Equal(paymentHash, checkRet[96:128]) && inBlock == binary.BigEndian.Uint64(checkRet[56:64]) {
   322  			return true, false
   323  		}
   324  	} else {
   325  		if len(paymentHash) > 0 && bytes.Equal(paymentHash, checkRet[96:128]) && inBlock > binary.BigEndian.Uint64(checkRet[56:64]) {
   326  			return true, false
   327  		} else if len(paymentHash) == 0 {
   328  			return true, false
   329  		}
   330  	}
   331  	return false, false
   332  }
   333  
   334  func ProvePoW(sender common.Address, blockTime *big.Int, functionSelector []byte, checkRet []byte, currencyCode string, chainURL string) (bool, bool) {
   335  	var username, password string
   336  	chainURLhash := sha256.Sum256([]byte(chainURL))
   337  	chainURLchecksum := hex.EncodeToString(chainURLhash[0:4])
   338  	switch currencyCode {
   339  	case "btc":
   340  		username = os.Getenv("BTC_U_" + chainURLchecksum)
   341  		password = os.Getenv("BTC_P_" + chainURLchecksum)
   342  	case "ltc":
   343  		username = os.Getenv("LTC_U_" + chainURLchecksum)
   344  		password = os.Getenv("LTC_P_" + chainURLchecksum)
   345  	case "dog":
   346  		username = os.Getenv("DOGE_U_" + chainURLchecksum)
   347  		password = os.Getenv("DOGE_P_" + chainURLchecksum)
   348  	}
   349  	if bytes.Equal(functionSelector, GetProveDataAvailabilityPeriodFinalitySelector(blockTime)) {
   350  		return ProveDataAvailabilityPeriodFinalityPoW(checkRet, chainURL, username, password)
   351  	} else if bytes.Equal(functionSelector, GetProvePaymentFinalitySelector(blockTime)) {
   352  		return ProvePaymentFinalityPoW(checkRet, false, currencyCode, chainURL, username, password)
   353  	} else if bytes.Equal(functionSelector, GetDisprovePaymentFinalitySelector(blockTime)) {
   354  		return ProvePaymentFinalityPoW(checkRet, true, currencyCode, chainURL, username, password)
   355  	}
   356  	return false, false
   357  }
   358  
   359  // =======================================================
   360  // XRP
   361  // =======================================================
   362  
   363  type GetXRPBlockRequestParams struct {
   364  	LedgerIndex  uint64 `json:"ledger_index"`
   365  	Full         bool   `json:"full"`
   366  	Accounts     bool   `json:"accounts"`
   367  	Transactions bool   `json:"transactions"`
   368  	Expand       bool   `json:"expand"`
   369  	OwnerFunds   bool   `json:"owner_funds"`
   370  }
   371  type GetXRPBlockRequestPayload struct {
   372  	Method string                     `json:"method"`
   373  	Params []GetXRPBlockRequestParams `json:"params"`
   374  }
   375  type CheckXRPErrorResponse struct {
   376  	Error string `json:"error"`
   377  }
   378  type GetXRPBlockResponse struct {
   379  	LedgerHash  string `json:"ledger_hash"`
   380  	LedgerIndex int    `json:"ledger_index"`
   381  	Validated   bool   `json:"validated"`
   382  }
   383  
   384  func GetXRPBlock(ledger uint64, chainURL string) (string, bool) {
   385  	data := GetXRPBlockRequestPayload{
   386  		Method: "ledger",
   387  		Params: []GetXRPBlockRequestParams{
   388  			{
   389  				LedgerIndex:  ledger,
   390  				Full:         false,
   391  				Accounts:     false,
   392  				Transactions: false,
   393  				Expand:       false,
   394  				OwnerFunds:   false,
   395  			},
   396  		},
   397  	}
   398  	payloadBytes, err := json.Marshal(data)
   399  	if err != nil {
   400  		return "", true
   401  	}
   402  	body := bytes.NewReader(payloadBytes)
   403  	req, err := http.NewRequest("POST", chainURL, body)
   404  	if err != nil {
   405  		return "", true
   406  	}
   407  	req.Header.Set("Content-Type", "application/json")
   408  	resp, err := client.Do(req)
   409  	if err != nil {
   410  		return "", true
   411  	}
   412  	defer resp.Body.Close()
   413  	if resp.StatusCode != 200 {
   414  		return "", true
   415  	}
   416  	respBody, err := ioutil.ReadAll(resp.Body)
   417  	if err != nil {
   418  		return "", true
   419  	}
   420  	var checkErrorResp map[string]CheckXRPErrorResponse
   421  	err = json.Unmarshal(respBody, &checkErrorResp)
   422  	if err != nil {
   423  		return "", true
   424  	}
   425  	if checkErrorResp["result"].Error != "" {
   426  		return "", true
   427  	}
   428  	var jsonResp map[string]GetXRPBlockResponse
   429  	err = json.Unmarshal(respBody, &jsonResp)
   430  	if err != nil {
   431  		return "", true
   432  	}
   433  	if !jsonResp["result"].Validated {
   434  		return "", true
   435  	}
   436  	return jsonResp["result"].LedgerHash, false
   437  }
   438  
   439  func ProveDataAvailabilityPeriodFinalityXRP(checkRet []byte, chainURL string) (bool, bool) {
   440  	ledger := binary.BigEndian.Uint64(checkRet[56:64])
   441  	ledgerHashString, err := GetXRPBlock(ledger, chainURL)
   442  	if err {
   443  		return false, true
   444  	}
   445  	if ledgerHashString != "" && bytes.Equal(crypto.Keccak256([]byte(ledgerHashString)), checkRet[96:128]) {
   446  		return true, false
   447  	}
   448  	return false, false
   449  }
   450  
   451  type GetXRPTxRequestParams struct {
   452  	Transaction string `json:"transaction"`
   453  	Binary      bool   `json:"binary"`
   454  }
   455  type GetXRPTxRequestPayload struct {
   456  	Method string                  `json:"method"`
   457  	Params []GetXRPTxRequestParams `json:"params"`
   458  }
   459  type GetXRPTxResponse struct {
   460  	Destination     string `json:"Destination"`
   461  	DestinationTag  int    `json:"DestinationTag"`
   462  	TransactionType string `json:"TransactionType"`
   463  	Hash            string `json:"hash"`
   464  	InLedger        int    `json:"inLedger"`
   465  	Validated       bool   `json:"validated"`
   466  	Meta            struct {
   467  		TransactionResult string      `json:"TransactionResult"`
   468  		Amount            interface{} `json:"delivered_amount"`
   469  	} `json:"meta"`
   470  }
   471  
   472  type GetXRPTxIssuedCurrency struct {
   473  	Currency string `json:"currency"`
   474  	Issuer   string `json:"issuer"`
   475  	Value    string `json:"value"`
   476  }
   477  
   478  func GetXRPTx(txHash string, latestAvailableLedger uint64, chainURL string) ([]byte, uint64, bool) {
   479  	data := GetXRPTxRequestPayload{
   480  		Method: "tx",
   481  		Params: []GetXRPTxRequestParams{
   482  			{
   483  				Transaction: txHash,
   484  				Binary:      false,
   485  			},
   486  		},
   487  	}
   488  	payloadBytes, err := json.Marshal(data)
   489  	if err != nil {
   490  		return []byte{}, 0, true
   491  	}
   492  	body := bytes.NewReader(payloadBytes)
   493  	req, err := http.NewRequest("POST", chainURL, body)
   494  	if err != nil {
   495  		return []byte{}, 0, true
   496  	}
   497  	req.Header.Set("Content-Type", "application/json")
   498  	resp, err := client.Do(req)
   499  	if err != nil {
   500  		return []byte{}, 0, true
   501  	}
   502  	defer resp.Body.Close()
   503  	if resp.StatusCode != 200 {
   504  		return []byte{}, 0, true
   505  	}
   506  	respBody, err := ioutil.ReadAll(resp.Body)
   507  	if err != nil {
   508  		return []byte{}, 0, true
   509  	}
   510  	var checkErrorResp map[string]CheckXRPErrorResponse
   511  	err = json.Unmarshal(respBody, &checkErrorResp)
   512  	if err != nil {
   513  		return []byte{}, 0, true
   514  	}
   515  	respErrString := checkErrorResp["result"].Error
   516  	if respErrString != "" {
   517  		if respErrString == "amendmentBlocked" ||
   518  			respErrString == "failedToForward" ||
   519  			respErrString == "invalid_API_version" ||
   520  			respErrString == "noClosed" ||
   521  			respErrString == "noCurrent" ||
   522  			respErrString == "noNetwork" ||
   523  			respErrString == "tooBusy" {
   524  			return []byte{}, 0, true
   525  		} else {
   526  			return []byte{}, 0, false
   527  		}
   528  	}
   529  	var jsonResp map[string]GetXRPTxResponse
   530  	err = json.Unmarshal(respBody, &jsonResp)
   531  	if err != nil {
   532  		return []byte{}, 0, false
   533  	}
   534  	if jsonResp["result"].TransactionType != "Payment" || !jsonResp["result"].Validated || jsonResp["result"].Meta.TransactionResult != "tesSUCCESS" {
   535  		return []byte{}, 0, false
   536  	}
   537  	inLedger := uint64(jsonResp["result"].InLedger)
   538  	if inLedger == 0 || inLedger >= latestAvailableLedger || !jsonResp["result"].Validated {
   539  		return []byte{}, 0, false
   540  	}
   541  	var currency string
   542  	var amount uint64
   543  	if stringAmount, ok := jsonResp["result"].Meta.Amount.(string); ok {
   544  		amount, err = strconv.ParseUint(stringAmount, 10, 64)
   545  		if err != nil {
   546  			return []byte{}, 0, false
   547  		}
   548  		currency = "xrp"
   549  	} else {
   550  		amountStruct, err := json.Marshal(jsonResp["result"].Meta.Amount)
   551  		if err != nil {
   552  			return []byte{}, 0, false
   553  		}
   554  		var issuedCurrencyResp GetXRPTxIssuedCurrency
   555  		err = json.Unmarshal(amountStruct, &issuedCurrencyResp)
   556  		if err != nil {
   557  			return []byte{}, 0, false
   558  		}
   559  		floatAmount, err := strconv.ParseFloat(issuedCurrencyResp.Value, 64)
   560  		if err != nil {
   561  			return []byte{}, 0, false
   562  		}
   563  		amount = uint64(floatAmount * math.Pow(10, 15))
   564  		currency = issuedCurrencyResp.Currency + issuedCurrencyResp.Issuer
   565  	}
   566  	txIdHash := crypto.Keccak256([]byte(jsonResp["result"].Hash))
   567  	destinationHash := crypto.Keccak256([]byte(jsonResp["result"].Destination))
   568  	destinationTagHash := crypto.Keccak256(common.LeftPadBytes(common.FromHex(hexutil.EncodeUint64(uint64(jsonResp["result"].DestinationTag))), 32))
   569  	destinationHash = crypto.Keccak256(destinationHash, destinationTagHash)
   570  	amountHash := crypto.Keccak256(common.LeftPadBytes(common.FromHex(hexutil.EncodeUint64(amount)), 32))
   571  	currencyHash := crypto.Keccak256([]byte(currency))
   572  	return crypto.Keccak256(txIdHash, destinationHash, amountHash, currencyHash), inLedger, false
   573  }
   574  
   575  func ProvePaymentFinalityXRP(checkRet []byte, isDisprove bool, chainURL string) (bool, bool) {
   576  	paymentHash, inLedger, err := GetXRPTx(string(checkRet[192:]), binary.BigEndian.Uint64(checkRet[88:96]), chainURL)
   577  	if err {
   578  		return false, true
   579  	}
   580  	if !isDisprove {
   581  		if len(paymentHash) > 0 && bytes.Equal(paymentHash, checkRet[96:128]) && inLedger == binary.BigEndian.Uint64(checkRet[56:64]) {
   582  			return true, false
   583  		}
   584  	} else {
   585  		if len(paymentHash) > 0 && bytes.Equal(paymentHash, checkRet[96:128]) && inLedger > binary.BigEndian.Uint64(checkRet[56:64]) {
   586  			return true, false
   587  		} else if len(paymentHash) == 0 {
   588  			return true, false
   589  		}
   590  	}
   591  	return false, false
   592  }
   593  
   594  func ProveXRP(sender common.Address, blockTime *big.Int, functionSelector []byte, checkRet []byte, chainURL string) (bool, bool) {
   595  	if bytes.Equal(functionSelector, GetProveDataAvailabilityPeriodFinalitySelector(blockTime)) {
   596  		return ProveDataAvailabilityPeriodFinalityXRP(checkRet, chainURL)
   597  	} else if bytes.Equal(functionSelector, GetProvePaymentFinalitySelector(blockTime)) {
   598  		return ProvePaymentFinalityXRP(checkRet, false, chainURL)
   599  	} else if bytes.Equal(functionSelector, GetDisprovePaymentFinalitySelector(blockTime)) {
   600  		return ProvePaymentFinalityXRP(checkRet, true, chainURL)
   601  	}
   602  	return false, false
   603  }
   604  
   605  // =======================================================
   606  // ALGO
   607  // =======================================================
   608  
   609  func ProveALGO(sender common.Address, blockTime *big.Int, functionSelector []byte, checkRet []byte, chainURL string) (bool, bool) {
   610  	return false, false
   611  }
   612  
   613  // =======================================================
   614  // Common
   615  // =======================================================
   616  
   617  func ProveChain(sender common.Address, blockTime *big.Int, functionSelector []byte, checkRet []byte, chainId uint32, chainURL string) (bool, bool) {
   618  	switch chainId {
   619  	case 0:
   620  		return ProvePoW(sender, blockTime, functionSelector, checkRet, "btc", chainURL)
   621  	case 1:
   622  		return ProvePoW(sender, blockTime, functionSelector, checkRet, "ltc", chainURL)
   623  	case 2:
   624  		return ProvePoW(sender, blockTime, functionSelector, checkRet, "dog", chainURL)
   625  	case 3:
   626  		return ProveXRP(sender, blockTime, functionSelector, checkRet, chainURL)
   627  	case 4:
   628  		return ProveALGO(sender, blockTime, functionSelector, checkRet, chainURL)
   629  	default:
   630  		return false, true
   631  	}
   632  }
   633  
   634  func ReadChain(sender common.Address, blockTime *big.Int, functionSelector []byte, checkRet []byte) bool {
   635  	chainId := binary.BigEndian.Uint32(checkRet[28:32])
   636  	var chainURLs string
   637  	switch chainId {
   638  	case 0:
   639  		chainURLs = os.Getenv("BTC_APIs")
   640  	case 1:
   641  		chainURLs = os.Getenv("LTC_APIs")
   642  	case 2:
   643  		chainURLs = os.Getenv("DOGE_APIs")
   644  	case 3:
   645  		chainURLs = os.Getenv("XRP_APIs")
   646  	case 4:
   647  		chainURLs = os.Getenv("ALGO_APIs")
   648  	}
   649  	if chainURLs == "" {
   650  		return false
   651  	}
   652  	for i := 0; i < apiRetries; i++ {
   653  		for _, chainURL := range strings.Split(chainURLs, ",") {
   654  			if chainURL == "" {
   655  				continue
   656  			}
   657  			verified, err := ProveChain(sender, blockTime, functionSelector, checkRet, chainId, chainURL)
   658  			if !verified && err {
   659  				continue
   660  			}
   661  			return verified
   662  		}
   663  		time.Sleep(apiRetryDelay)
   664  	}
   665  	return false
   666  }
   667  
   668  func GetVerificationPaths(functionSelector []byte, checkRet []byte) (string, string) {
   669  	prefix := "cache/"
   670  	acceptedPrefix := "ACCEPTED"
   671  	rejectedPrefix := "REJECTED"
   672  	functionHash := hex.EncodeToString(functionSelector[:])
   673  	verificationHash := hex.EncodeToString(crypto.Keccak256(checkRet[0:64], checkRet[96:128]))
   674  	suffix := "_" + functionHash + "_" + verificationHash
   675  	return prefix + acceptedPrefix + suffix, prefix + rejectedPrefix + suffix
   676  }
   677  
   678  // Verify proof against underlying chain
   679  func StateConnectorCall(sender common.Address, blockTime *big.Int, functionSelector []byte, checkRet []byte) bool {
   680  	if binary.BigEndian.Uint64(checkRet[88:96]) > 0 {
   681  		go func() {
   682  			acceptedPath, rejectedPath := GetVerificationPaths(functionSelector, checkRet)
   683  			_, errACCEPTED := os.Stat(acceptedPath)
   684  			_, errREJECTED := os.Stat(rejectedPath)
   685  			if errACCEPTED != nil && errREJECTED != nil {
   686  				if ReadChain(sender, blockTime, functionSelector, checkRet) {
   687  					verificationHashStore, err := os.Create(acceptedPath)
   688  					verificationHashStore.Close()
   689  					if err != nil {
   690  						// Permissions problem
   691  						panic(err)
   692  					}
   693  				} else {
   694  					verificationHashStore, err := os.Create(rejectedPath)
   695  					verificationHashStore.Close()
   696  					if err != nil {
   697  						// Permissions problem
   698  						panic(err)
   699  					}
   700  				}
   701  			}
   702  		}()
   703  		return true
   704  	} else {
   705  		acceptedPath, rejectedPath := GetVerificationPaths(functionSelector, checkRet)
   706  		_, errACCEPTED := os.Stat(acceptedPath)
   707  		_, errREJECTED := os.Stat(rejectedPath)
   708  		if errACCEPTED != nil && errREJECTED != nil {
   709  			for i := 0; i < 2*apiRetries; i++ {
   710  				_, errACCEPTED = os.Stat(acceptedPath)
   711  				_, errREJECTED = os.Stat(rejectedPath)
   712  				if errACCEPTED == nil || errREJECTED == nil {
   713  					break
   714  				}
   715  				time.Sleep(apiRetryDelay)
   716  			}
   717  		}
   718  		go func() {
   719  			removeFulfilledAPIRequests := os.Getenv("REMOVE_FULFILLED_API_REQUESTS")
   720  			if removeFulfilledAPIRequests == "1" {
   721  				errDeleteACCEPTED := os.Remove(acceptedPath)
   722  				errDeleteREJECTED := os.Remove(rejectedPath)
   723  				if errDeleteACCEPTED != nil && errDeleteREJECTED != nil {
   724  					// Permissions problem
   725  					panic(fmt.Sprintf("%s\n%s", errDeleteACCEPTED, errDeleteREJECTED))
   726  				}
   727  			}
   728  		}()
   729  		return errACCEPTED == nil
   730  	}
   731  }