Vango Lu
Vango Lu

Reputation: 21

panic: gob: type elliptic.p256Curve has no exported fields

I am trying to build a blockchain project when I'm catching an issue about gob Serialize. I have a struct Wallet which uses elliptic.P256() Curve struct, and when I'm trying to serialize Wallet, a bug of no exported fields occured.

Really hope for some help.

There is my code.

const walletFile = "Wallets.dat"

type Wallets struct {
    WalletsMap map[string]*Wallet
}

type Wallet struct {
    PrivateKey ecdsa.PrivateKey
    PublicKey []byte
}

func (w *Wallets) SaveWallets() {
    var content bytes.Buffer

    gob.Register(elliptic.P256())

    encoder := gob.NewEncoder(&content)
    err := encoder.Encode(&w)
    if err != nil {
        log.Panic(err)
    }

    err = ioutil.WriteFile(walletFile, content.Bytes(), 0644)
    if err != nil {
        log.Panic(err)
    }

}


func NewWallets() (*Wallets, error) {
    if _, err := os.Stat(walletFile); os.IsNotExist(err) {
        wallets := &Wallets{}
        wallets.WalletsMap = make(map[string]*Wallet)
        return wallets, err
    }

    fileContent, err := ioutil.ReadFile(walletFile)
    if err != nil {
        log.Panic(err)
    }

    var wallets Wallets
    gob.Register(elliptic.P256())
    decoder := gob.NewDecoder(bytes.NewReader(fileContent))
    err = decoder.Decode(&wallets)
    if err != nil {
        log.Panic(err)
    }

    return &wallets, nil
}

The issue

2022/09/18 19:42:33 gob: type elliptic.p256Curve has no exported fields
panic: gob: type elliptic.p256Curve has no exported fields

Upvotes: 1

Views: 1511

Answers (5)

Hossein Mayboudi
Hossein Mayboudi

Reputation: 505

We can fix this issue by following the code. The solution is available at Github as well. I will try to use MarshalBinary.

// wallet.go
func (w Wallet) MarshalJSON() ([]byte, error) {
    mapStringAny := map[string]any{
        "PrivateKey": map[string]any{
            "D": w.PrivateKey.D,
            "PublicKey": map[string]any{
                "X": w.PrivateKey.PublicKey.X,
                "Y": w.PrivateKey.PublicKey.Y,
            },
            "X": w.PrivateKey.X,
            "Y": w.PrivateKey.Y,
        },
        "PublicKey": w.PublicKey,
    }
    return json.Marshal(mapStringAny)
}
// wallets.go

// LoadFromFile loads wallets from the file
func (ws *Wallets) LoadFromFile() error {
    if _, err := os.Stat(walletFile); os.IsNotExist(err) {
        return err
    }

    fileContent, err := os.ReadFile(walletFile)
    if err != nil {
        log.Panic(err)
    }

    err = json.Unmarshal(fileContent, ws)
    if err != nil {
        log.Panic(err)
    }

    return nil
}

// SaveToFile saves wallets to a file
func (ws Wallets) SaveToFile() {
    jsonData, err := json.Marshal(ws)
    if err != nil {
        log.Panic(err)
    }

    err = os.WriteFile(walletFile, jsonData, 0666)
    if err != nil {
        log.Panic(err)
    }
}

Upvotes: 0

zweix
zweix

Reputation: 21

I have a solution here

In go1.20, the serialization of god requires reflection, while there is an interface in the attribute of type ecdsa.PrivateKey, which may not be usable.

How to solve it? Ecdsa.PrivateKey maybe have a specialized serialization method.

The demo is as follows:

type Wallet struct {
    PrivateKey ecdsa.PrivateKey
    PublicKey  []byte
}

func (w *Wallet) Save() {
    filename := filepath.Join(constcoe.Wallets, string(w.Address())+".wlt")

    privKeyBytes, err := x509.MarshalECPrivateKey(&w.PrivateKey)
    utils.Handle(err)
    privKeyFile, err := os.Create(filename)
    utils.Handle(err)
    err = pem.Encode(privKeyFile, &pem.Block{
        // Type:  "EC PRIVATE KEY",
        Bytes: privKeyBytes,
    })
    utils.Handle(err)
    privKeyFile.Close()
}


func LoadWallet(address string) *Wallet {
    filename := filepath.Join(constcoe.Wallets, address+".wlt")
    if !utils.FileExists(filename) {
        utils.Handle(errors.New("no wallet with such address"))
    }

    privKeyFile, err := os.ReadFile(filename)
    utils.Handle(err)
    pemBlock, _ := pem.Decode(privKeyFile)
    utils.Handle(err)
    privKey, err := x509.ParseECPrivateKey(pemBlock.Bytes)
    utils.Handle(err)
    publicKey := append(privKey.PublicKey.X.Bytes(), privKey.PublicKey.Y.Bytes()...)
    return &Wallet{
        PrivateKey: *privKey,
        PublicKey:  publicKey,
    }
}

Upvotes: 1

Proton
Proton

Reputation: 361

Change your Go version to 1.18.10 for less. I have encountered the same issue due to the latest Go version i.e. 1.19.5

We need to downgrade the Go version the old way because Go doesn't provide anything like a fancy version manager.

Steps to downgrade Go Version:

  1. Uninstall the existing Go version

    To uninstall go, locate where Go is on your system.

    $where go This command will locate the program file in the user path.

    To uninstall, delete the /usr/local/go directory or the source directory which you have received as output in the previous command. Use command $ sudo rm -rf /usr/local/go to delete the Go directory.

    To confirm run the command $ go version, system will prompt "command go not found" if you have successfully deleted Go ditrectory.

  2. Install the new version

    Go to the downloads page and download the version release(choose installer instead of Archive to make things easy for yourself) which is compatible with your OS and Architecture. Unzip and extract the package installer and the new Go version is now installed in your system.

After that, you will need to restart your terminal for the change to take effect. To check if you have installed Go successfully, run the command $go version. The command prints the installed version of Go. Also make sure that GOROOT and GOPATH haven't changed.

Upvotes: 0

riddledwithknife
riddledwithknife

Reputation: 1

Its problem in 1.9.* versions of Go. Just install 1.8.7 for example. Everything gonna work fine after this)

Upvotes: -1

Elara6331
Elara6331

Reputation: 36

What you seem to be trying to do here is serialize a P256 curve from the crypto/elliptic package. The issue is that the P256() function returns an interface called elliptic.Curve.

What this error is telling you is that the underlying type of the elliptic.Curve, in this case elliptic.p256Curve, doesn't have any fields that are exported (named with the first letter capitalized). Go's reflect package, which encoding/gob uses, only works on exported fields.

You might want to try using crypto/elliptic's Marshal() or GenerateKey() functions.

Upvotes: 0

Related Questions