package extension import ( "encoding/json" "io/ioutil" "os" "path/filepath" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" ) /* The file can have two formats: 1. { "psiContracts": { "psi1": { "contract1address": ..., "contract2address": ..., }, ... } } 2. { "contract1address": ..., "contract2address": ..., } */ const extensionContractData = "activeExtensions.json" type DataHandler interface { Load() (map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract, error) Save(extensionContracts map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract) error } type JsonFileDataHandler struct { saveFile string } func NewJsonFileDataHandler(dataDirectory string) *JsonFileDataHandler { return &JsonFileDataHandler{ saveFile: filepath.Join(dataDirectory, extensionContractData), } } /* The strategy when loading the save file is too check if the newer "psiContracts" field is present. If so, then everything should exist under that key, and so we can unmarshal and return immediately. If not, then the save file was made from a previous version. Load up all the data as before and put it under the "private" PSI. It should never be the case the file contains both types of data at once. */ func (handler *JsonFileDataHandler) Load() (map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract, error) { if _, err := os.Stat(handler.saveFile); !(err == nil || !os.IsNotExist(err)) { return map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract{types.DefaultPrivateStateIdentifier: {}}, nil } blob, err := ioutil.ReadFile(handler.saveFile) if err != nil { return nil, err } var untyped map[string]json.RawMessage if err := json.Unmarshal(blob, &untyped); err != nil { return nil, err } if psiContracts, ok := untyped["psiContracts"]; ok { var contracts map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract json.Unmarshal(psiContracts, &contracts) return contracts, nil } currentContracts := make(map[common.Address]*ExtensionContract) for key, val := range untyped { extAddress := common.HexToAddress(key) var ext ExtensionContract json.Unmarshal(val, &ext) currentContracts[extAddress] = &ext } return map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract{types.DefaultPrivateStateIdentifier: currentContracts}, nil } func (handler *JsonFileDataHandler) Save(extensionContracts map[types.PrivateStateIdentifier]map[common.Address]*ExtensionContract) error { //we want to put the map under "psiContracts" key to distinguish from existing data saveData := make(map[string]interface{}) saveData["psiContracts"] = extensionContracts //no unmarshallable types, so can't error output, _ := json.Marshal(&saveData) if errSaving := ioutil.WriteFile(handler.saveFile, output, 0644); errSaving != nil { log.Error("Couldn't save outstanding extension contract details") return errSaving } return nil }