Reputation: 21
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
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
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
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:
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.
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
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
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