From 1383d0aaa6522cdefd2390db6f115e95928e9c25 Mon Sep 17 00:00:00 2001
From: Julian Strobl <jmastr@mailbox.org>
Date: Fri, 11 Aug 2023 09:14:52 +0200
Subject: [PATCH] Add extended public key as IssuerPlanetmint (#62)

* Fix package and domain name

Signed-off-by: Julian Strobl <jmastr@mailbox.org>

* Add extended public key as `IssuerPlanetmint`

This patch introduces parts of the `chaincfg` for Planetmint and
especially the magic bytes that produces extended keys with the prefix
`pmpr` for a Planetmint extended private key and `pmpb` for a Planetmint
extended public key.

// Closes https://github.com/rddl-network/issues/issues/30

Signed-off-by: Julian Strobl <jmastr@mailbox.org>

* Validate Planetmint extended public key during machine attestation

Signed-off-by: Julian Strobl <jmastr@mailbox.org>

* [lint] Fix error return value is not checked

Signed-off-by: Julian Strobl <jmastr@mailbox.org>

---------

Signed-off-by: Julian Strobl <jmastr@mailbox.org>
---
 config/config.go                              |  2 +-
 config/params.go                              | 25 ++++++++++++++++
 testutil/keeper/asset.go                      |  7 +++--
 testutil/sample/sample.go                     | 30 ++++++++++++-------
 x/machine/keeper/msg_server_attest_machine.go | 14 +++++----
 5 files changed, 60 insertions(+), 18 deletions(-)
 create mode 100644 config/params.go

diff --git a/config/config.go b/config/config.go
index a61beaf..da7e666 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1,4 +1,4 @@
-package app
+package config
 
 import (
 	"encoding/json"
diff --git a/config/params.go b/config/params.go
new file mode 100644
index 0000000..d128ad1
--- /dev/null
+++ b/config/params.go
@@ -0,0 +1,25 @@
+package config
+
+import (
+	"github.com/btcsuite/btcd/chaincfg"
+)
+
+// PlmntNetParams defines the network parameters for the Planetmint network.
+var PlmntNetParams = chaincfg.Params{
+	Name: "planetmint",
+
+	// BIP32 hierarchical deterministic extended key magics
+	HDPrivateKeyID: [4]byte{0x03, 0xe1, 0x42, 0xb0}, // starts with pmpr
+	HDPublicKeyID:  [4]byte{0x03, 0xe1, 0x42, 0x47}, // starts with pmpb
+
+	// BIP44 coin type used in the hierarchical deterministic path for
+	// address generation.
+	HDCoinType: 8680,
+}
+
+func init() {
+	err := chaincfg.Register(&PlmntNetParams)
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/testutil/keeper/asset.go b/testutil/keeper/asset.go
index 2785fba..2c973e6 100644
--- a/testutil/keeper/asset.go
+++ b/testutil/keeper/asset.go
@@ -3,12 +3,14 @@ package keeper
 import (
 	"testing"
 
+	"planetmint-go/config"
 	"planetmint-go/testutil/sample"
 	"planetmint-go/x/asset/keeper"
 	"planetmint-go/x/asset/types"
 
 	assettestutils "planetmint-go/x/asset/testutil"
 
+	"github.com/btcsuite/btcd/chaincfg"
 	tmdb "github.com/cometbft/cometbft-db"
 	"github.com/cometbft/cometbft/libs/log"
 	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
@@ -48,8 +50,9 @@ func AssetKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) {
 	ctrl := gomock.NewController(t)
 	mk := assettestutils.NewMockMachineKeeper(ctrl)
 	sk, pk := sample.KeyPair()
-	_, lpk := sample.LiquidKeyPair()
-	id := sample.MachineIndex(pk, lpk)
+	_, ppk := sample.ExtendedKeyPair(config.PlmntNetParams)
+	_, lpk := sample.ExtendedKeyPair(chaincfg.MainNetParams)
+	id := sample.MachineIndex(pk, ppk, lpk)
 	mk.EXPECT().GetMachineIndex(ctx, pk).Return(id, true).AnyTimes()
 	mk.EXPECT().GetMachineIndex(ctx, sk).Return(id, false).AnyTimes()
 	mk.EXPECT().GetMachine(ctx, id).Return(sample.Machine(pk, pk), true).AnyTimes()
diff --git a/testutil/sample/sample.go b/testutil/sample/sample.go
index 7c8a2d6..67f9c18 100644
--- a/testutil/sample/sample.go
+++ b/testutil/sample/sample.go
@@ -3,6 +3,7 @@ package sample
 import (
 	"encoding/hex"
 
+	"planetmint-go/config"
 	machinetypes "planetmint-go/x/machine/types"
 
 	"github.com/btcsuite/btcd/btcutil/hdkeychain"
@@ -49,15 +50,16 @@ func AccAddress() string {
 
 func Machine(name, pubKey string) machinetypes.Machine {
 	metadata := Metadata()
-	_, liquidPubKey := LiquidKeyPair()
+	_, liquidPubKey := ExtendedKeyPair(chaincfg.MainNetParams)
+	_, planetmintPubKey := ExtendedKeyPair(config.PlmntNetParams)
 	m := machinetypes.Machine{
 		Name:             name,
 		Ticker:           name + "_ticker",
-		Domain:           "lab.r3c.net",
+		Domain:           "lab.r3c.network",
 		Reissue:          true,
 		Amount:           1000,
 		Precision:        8,
-		IssuerPlanetmint: pubKey,
+		IssuerPlanetmint: planetmintPubKey,
 		IssuerLiquid:     liquidPubKey,
 		MachineId:        pubKey,
 		Metadata:         &metadata,
@@ -65,10 +67,10 @@ func Machine(name, pubKey string) machinetypes.Machine {
 	return m
 }
 
-func MachineIndex(pubKey string, liquidPubKey string) machinetypes.MachineIndex {
+func MachineIndex(pubKey string, planetmintPubKey string, liquidPubKey string) machinetypes.MachineIndex {
 	return machinetypes.MachineIndex{
 		MachineId:        pubKey,
-		IssuerPlanetmint: pubKey,
+		IssuerPlanetmint: planetmintPubKey,
 		IssuerLiquid:     liquidPubKey,
 	}
 }
@@ -96,10 +98,18 @@ func Asset(sk string) (string, string) {
 	return cid, signatureHex
 }
 
-func LiquidKeyPair() (string, string) {
-	// Ignore errors as keypair was tested beforehand
-	seed, _ := bip39.NewSeedWithErrorChecking(Mnemonic, keyring.DefaultBIP39Passphrase)
-	xprivKey, _ := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
-	xpubKey, _ := xprivKey.Neuter()
+func ExtendedKeyPair(cfg chaincfg.Params) (string, string) {
+	seed, err := bip39.NewSeedWithErrorChecking(Mnemonic, keyring.DefaultBIP39Passphrase)
+	if err != nil {
+		panic(err)
+	}
+	xprivKey, err := hdkeychain.NewMaster(seed, &cfg)
+	if err != nil {
+		panic(err)
+	}
+	xpubKey, err := xprivKey.Neuter()
+	if err != nil {
+		panic(err)
+	}
 	return xprivKey.String(), xpubKey.String()
 }
diff --git a/x/machine/keeper/msg_server_attest_machine.go b/x/machine/keeper/msg_server_attest_machine.go
index a8ed24d..2b986ac 100644
--- a/x/machine/keeper/msg_server_attest_machine.go
+++ b/x/machine/keeper/msg_server_attest_machine.go
@@ -24,7 +24,11 @@ func (k msgServer) isNFTCreationRequest(machine *types.Machine) bool {
 func (k msgServer) AttestMachine(goCtx context.Context, msg *types.MsgAttestMachine) (*types.MsgAttestMachineResponse, error) {
 	ctx := sdk.UnwrapSDKContext(goCtx)
 
-	isValidIssuerLiquid := validateIssuerLiquid(msg.Machine.IssuerLiquid)
+	isValidIssuerPlanetmint := validateExtendedPublicKey(msg.Machine.IssuerPlanetmint, config.PlmntNetParams)
+	if !isValidIssuerPlanetmint {
+		return nil, errors.New("invalid planetmint key")
+	}
+	isValidIssuerLiquid := validateExtendedPublicKey(msg.Machine.IssuerLiquid, chaincfg.MainNetParams)
 	if !isValidIssuerLiquid {
 		return nil, errors.New("invalid liquid key")
 	}
@@ -41,13 +45,13 @@ func (k msgServer) AttestMachine(goCtx context.Context, msg *types.MsgAttestMach
 	return &types.MsgAttestMachineResponse{}, nil
 }
 
-func validateIssuerLiquid(issuerLiquid string) bool {
-	xpubKeyLiquid, err := hdkeychain.NewKeyFromString(issuerLiquid)
+func validateExtendedPublicKey(issuer string, cfg chaincfg.Params) bool {
+	xpubKey, err := hdkeychain.NewKeyFromString(issuer)
 	if err != nil {
 		return false
 	}
-	isValidLiquidKey := xpubKeyLiquid.IsForNet(&chaincfg.MainNetParams)
-	return isValidLiquidKey
+	isValidExtendedPublicKey := xpubKey.IsForNet(&cfg)
+	return isValidExtendedPublicKey
 }
 
 func (k msgServer) issueMachineNFT(machine *types.Machine) error {