planetmint-go/x/machine/keeper/msg_server_attest_machine.go
Jürgen Eckel f11f66e14f
112 direct call to issue2liquid (#121)
* removed IssueResponseHandler
* added asset-registration endpoint config
* added methods to locally issue the asset and register it
* added issue_service directory configuration
* removed obsolete configuration options
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>

---------

Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
2023-10-05 12:38:35 +02:00

168 lines
4.8 KiB
Go

package keeper
import (
"bytes"
"context"
"encoding/json"
"io"
"log"
"net/http"
"os/exec"
"strconv"
"strings"
config "github.com/planetmint/planetmint-go/config"
"github.com/planetmint/planetmint-go/util"
"github.com/planetmint/planetmint-go/x/machine/types"
"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/chaincfg"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func (k msgServer) isNFTCreationRequest(machine *types.Machine) bool {
if !machine.GetReissue() && machine.GetAmount() == 1 && machine.GetPrecision() == 8 {
return true
}
return false
}
func (k msgServer) AttestMachine(goCtx context.Context, msg *types.MsgAttestMachine) (*types.MsgAttestMachineResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
// the ante handler verifies that the MachineID exists. Additional result checks got moved to the ante-handler
// and removed from here due to inconsistency or checking the same thing over and over again.
ta, _, _ := k.GetTrustAnchor(ctx, msg.Machine.MachineId)
isValidMachineId, err := util.ValidateSignature(msg.Machine.MachineId, msg.Machine.MachineIdSignature, msg.Machine.MachineId)
if !isValidMachineId {
return nil, err
}
isValidIssuerPlanetmint := validateExtendedPublicKey(msg.Machine.IssuerPlanetmint, config.PlmntNetParams)
if !isValidIssuerPlanetmint {
return nil, errorsmod.Wrap(types.ErrInvalidKey, "planetmint")
}
isValidIssuerLiquid := validateExtendedPublicKey(msg.Machine.IssuerLiquid, config.LiquidNetParams)
if !isValidIssuerLiquid {
return nil, errorsmod.Wrap(types.ErrInvalidKey, "liquid")
}
if k.isNFTCreationRequest(msg.Machine) && util.IsValidatorBlockProposer(ctx, ctx.BlockHeader().ProposerAddress) {
err := k.issueMachineNFT(msg.Machine)
if err != nil {
return nil, types.ErrNFTIssuanceFailed
}
}
if msg.Machine.GetType() == 0 { // 0 == RDDL_MACHINE_UNDEFINED
return nil, types.ErrMachineTypeUndefined
}
k.StoreMachine(ctx, *msg.Machine)
k.StoreMachineIndex(ctx, *msg.Machine)
err = k.StoreTrustAnchor(ctx, ta, true)
if err != nil {
return nil, err
}
return &types.MsgAttestMachineResponse{}, err
}
func validateExtendedPublicKey(issuer string, cfg chaincfg.Params) bool {
xpubKey, err := hdkeychain.NewKeyFromString(issuer)
if err != nil {
return false
}
isValidExtendedPublicKey := xpubKey.IsForNet(&cfg)
return isValidExtendedPublicKey
}
func (k msgServer) issueNFTAsset(name string, machine_address string) (asset_id string, contract string, err error) {
conf := config.GetConfig()
cmdName := "poetry"
cmdArgs := []string{"run", "python", "issuer_service/issue2liquid.py", name, machine_address}
// Create a new command
cmd := exec.Command(cmdName, cmdArgs...)
// If you want to set the working directory
cmd.Dir = conf.IssuanceServiceDir
// Capture the output
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
// Execute the command
err = cmd.Run()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
err = errorsmod.Wrap(types.ErrMachineNFTIssuance, stderr.String())
}
lines := strings.Split(stdout.String(), "\n")
if len(lines) == 3 {
asset_id = lines[0]
contract = lines[1]
} else {
err = errorsmod.Wrap(types.ErrMachineNFTIssuanceNoOutput, stderr.String())
}
return asset_id, contract, err
}
func (k msgServer) registerAsset(asset_id string, contract string) error {
conf := config.GetConfig()
// Create your request payload
data := map[string]interface{}{
"asset_id": asset_id,
"contract": contract,
}
jsonData, err := json.Marshal(data)
if err != nil {
return errorsmod.Wrap(types.ErrAssetRegistryReqFailure, "Marshall "+err.Error())
}
req, err := http.NewRequest("POST", conf.AssetRegistryEndpoint, bytes.NewBuffer(jsonData))
if err != nil {
return errorsmod.Wrap(types.ErrAssetRegistryReqFailure, "Request creation: "+err.Error())
}
// Set headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set("accept", "application/json")
// Send request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return errorsmod.Wrap(types.ErrAssetRegistryReqSending, err.Error())
}
defer resp.Body.Close()
// Read response
if resp.StatusCode > 299 {
return errorsmod.Wrap(types.ErrAssetRegistryRepsonse, "Error reading response body:"+strconv.Itoa(resp.StatusCode))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return errorsmod.Wrap(types.ErrAssetRegistryRepsonse, "Error reading response body:"+err.Error())
}
result_obj := string(body)
if strings.Contains(result_obj, asset_id) {
return nil
} else {
return errorsmod.Wrap(types.ErrAssetRegistryRepsonse, "does not confirm asset registration")
}
}
func (k msgServer) issueMachineNFT(machine *types.Machine) error {
asset_id, contract, err := k.issueNFTAsset(machine.Name, machine.Address)
if err != nil {
return err
}
return k.registerAsset(asset_id, contract)
}