github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/extension/data_handler.go (about)

     1  package extension
     2  
     3  import (
     4  	"encoding/json"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/kisexp/xdchain/common"
    10  	"github.com/kisexp/xdchain/core/types"
    11  	"github.com/kisexp/xdchain/log"
    12  )
    13  
    14  /*
    15  	The file can have two formats:
    16  
    17  	1.
    18  	{
    19  		"psiContracts": {
    20  			"psi1": {
    21  				"contract1address": ...,
    22  				"contract2address": ...,
    23  			},
    24  			...
    25  		}
    26  	}
    27  
    28  	2.
    29  	{
    30  		"contract1address": ...,
    31  		"contract2address": ...,
    32  	}
    33  
    34  */
    35  
    36  const extensionContractData = "activeExtensions.json"
    37  
    38  type DataHandler interface {
    39  	Load() (map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract, error)
    40  
    41  	Save(extensionContracts map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract) error
    42  }
    43  
    44  type JsonFileDataHandler struct {
    45  	saveFile string
    46  }
    47  
    48  func NewJsonFileDataHandler(dataDirectory string) *JsonFileDataHandler {
    49  	return &JsonFileDataHandler{
    50  		saveFile: filepath.Join(dataDirectory, extensionContractData),
    51  	}
    52  }
    53  
    54  /*
    55  	The strategy when loading the save file is too check if the newer "psiContracts" field is present.
    56  	If so, then everything should exist under that key, and so we can unmarshal and return immediately.
    57  
    58  	If not, then the save file was made from a previous version. Load up all the data as before and
    59  	put it under the "private" PSI.
    60  
    61  	It should never be the case the file contains both types of data at once.
    62  */
    63  func (handler *JsonFileDataHandler) Load() (map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract, error) {
    64  	if _, err := os.Stat(handler.saveFile); !(err == nil || !os.IsNotExist(err)) {
    65  		return map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract{types.DefaultPrivateStateIdentifier: {}}, nil
    66  	}
    67  
    68  	blob, err := ioutil.ReadFile(handler.saveFile)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	var untyped map[string]json.RawMessage
    74  	if err := json.Unmarshal(blob, &untyped); err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	if psiContracts, ok := untyped["psiContracts"]; ok {
    79  		var contracts map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract
    80  		json.Unmarshal(psiContracts, &contracts)
    81  		return contracts, nil
    82  	}
    83  
    84  	currentContracts := make(map[common.Address]*ExtensionContract)
    85  	for key, val := range untyped {
    86  		extAddress := common.HexToAddress(key)
    87  		var ext ExtensionContract
    88  		json.Unmarshal(val, &ext)
    89  		currentContracts[extAddress] = &ext
    90  	}
    91  	return map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract{types.DefaultPrivateStateIdentifier: currentContracts}, nil
    92  }
    93  
    94  func (handler *JsonFileDataHandler) Save(extensionContracts map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract) error {
    95  	//we want to put the map under "psiContracts" key to distinguish from existing data
    96  	saveData := make(map[string]interface{})
    97  	saveData["psiContracts"] = extensionContracts
    98  
    99  	//no unmarshallable types, so can't error
   100  	output, _ := json.Marshal(&saveData)
   101  
   102  	if errSaving := ioutil.WriteFile(handler.saveFile, output, 0644); errSaving != nil {
   103  		log.Error("Couldn't save outstanding extension contract details")
   104  		return errSaving
   105  	}
   106  	return nil
   107  }