mirror of
https://github.com/planetmint/planetmint-go.git
synced 2025-03-30 15:08:28 +00:00
Replace call to external python program (#215)
* Split asset registry endpoint Into scheme, domain and path. We need the domain in the liquid contract. * Switch to pure Go implementation This is a reimplementation of the `issue2liquid.py`. See https://github.com/rddl-network/issuer_service // Closes #196 * Move elements RPC URL into config Signed-off-by: Julian Strobl <jmastr@mailbox.org>
This commit is contained in:
parent
363d82d344
commit
ede70b073b
100
config/config.go
100
config/config.go
@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@ -12,7 +13,9 @@ const DefaultConfigTemplate = `
|
||||
|
||||
[planetmint]
|
||||
|
||||
asset-registry-endpoint = "{{ .PlmntConfig.AssetRegistryEndpoint }}"
|
||||
asset-registry-scheme = "{{ .PlmntConfig.AssetRegistryScheme}}"
|
||||
asset-registry-domain = "{{ .PlmntConfig.AssetRegistryDomain }}"
|
||||
asset-registry-path = "{{ .PlmntConfig.AssetRegistryPath }}"
|
||||
token-denom = "{{ .PlmntConfig.TokenDenom }}"
|
||||
stake-denom = "{{ .PlmntConfig.StakeDenom }}"
|
||||
fee-denom = "{{ .PlmntConfig.FeeDenom }}"
|
||||
@ -23,7 +26,8 @@ rpc-host = "{{ .PlmntConfig.RPCHost }}"
|
||||
rpc-port = {{ .PlmntConfig.RPCPort }}
|
||||
rpc-user = "{{ .PlmntConfig.RPCUser }}"
|
||||
rpc-password = "{{ .PlmntConfig.RPCPassword }}"
|
||||
issuance-service-dir = "{{ .PlmntConfig.IssuanceServiceDir }}"
|
||||
rpc-scheme = "{{ .PlmntConfig.RPCScheme }}"
|
||||
rpc-wallet = "{{ .PlmntConfig.RPCWallet }}"
|
||||
reissuance-asset = "{{ .PlmntConfig.ReissuanceAsset }}"
|
||||
validator-address = "{{ .PlmntConfig.ValidatorAddress }}"
|
||||
distribution-address-inv = "{{ .PlmntConfig.DistributionAddrInv }}"
|
||||
@ -35,26 +39,29 @@ re-issuance-epochs = {{ .PlmntConfig.ReIssuanceEpochs }}
|
||||
|
||||
// Config defines Planetmint's top level configuration
|
||||
type Config struct {
|
||||
AssetRegistryEndpoint string `json:"asset-registry-endpoint" mapstructure:"asset-registry-endpoint"`
|
||||
TokenDenom string `json:"token-denom" mapstructure:"token-denom"`
|
||||
StakeDenom string `json:"stake-denom" mapstructure:"stake-denom"`
|
||||
FeeDenom string `json:"fee-denom" mapstructure:"fee-denom"`
|
||||
StagedDenom string `json:"staged-denom" mapstructure:"staged-denom"`
|
||||
ClaimDenom string `json:"claim-denom" mapstructure:"claim-denom"`
|
||||
ConfigRootDir string `json:"config-root-dir" mapstructure:"config-root-dir"`
|
||||
PopEpochs int `json:"pop-epochs" mapstructure:"pop-epochs"`
|
||||
RPCHost string `json:"rpc-host" mapstructure:"rpc-host"`
|
||||
RPCPort int `json:"rpc-port" mapstructure:"rpc-port"`
|
||||
RPCUser string `json:"rpc-user" mapstructure:"rpc-user"`
|
||||
RPCPassword string `json:"rpc-password" mapstructure:"rpc-password"`
|
||||
IssuanceServiceDir string `json:"issuance-service-dir" mapstructure:"issuance-service-dir"`
|
||||
ReissuanceAsset string `json:"reissuance-asset" mapstructure:"reissuance-asset"`
|
||||
ValidatorAddress string `json:"validator-address" mapstructure:"validator-address"`
|
||||
DistributionAddrInv string `json:"distribution-addr-inv" mapstructure:"distribution-addr-inv"`
|
||||
DistributionAddrDAO string `json:"distribution-addr-dao" mapstructure:"distribution-addr-dao"`
|
||||
DistributionAddrPop string `json:"distribution-addr-pop" mapstructure:"distribution-addr-pop"`
|
||||
DistributionEpochs int `json:"distribution-epochs" mapstructure:"distribution-epochs"`
|
||||
ReIssuanceEpochs int `json:"re-issuance-epochs" mapstructure:"re-issuance-epochs"`
|
||||
AssetRegistryScheme string `json:"asset-registry-scheme" mapstructure:"asset-registry-scheme"`
|
||||
AssetRegistryDomain string `json:"asset-registry-domain" mapstructure:"asset-registry-domain"`
|
||||
AssetRegistryPath string `json:"asset-registry-path" mapstructure:"asset-registry-path"`
|
||||
TokenDenom string `json:"token-denom" mapstructure:"token-denom"`
|
||||
StakeDenom string `json:"stake-denom" mapstructure:"stake-denom"`
|
||||
FeeDenom string `json:"fee-denom" mapstructure:"fee-denom"`
|
||||
StagedDenom string `json:"staged-denom" mapstructure:"staged-denom"`
|
||||
ClaimDenom string `json:"claim-denom" mapstructure:"claim-denom"`
|
||||
ConfigRootDir string `json:"config-root-dir" mapstructure:"config-root-dir"`
|
||||
PopEpochs int `json:"pop-epochs" mapstructure:"pop-epochs"`
|
||||
RPCHost string `json:"rpc-host" mapstructure:"rpc-host"`
|
||||
RPCPort int `json:"rpc-port" mapstructure:"rpc-port"`
|
||||
RPCUser string `json:"rpc-user" mapstructure:"rpc-user"`
|
||||
RPCPassword string `json:"rpc-password" mapstructure:"rpc-password"`
|
||||
RPCScheme string `json:"rpc-scheme" mapstructure:"rpc-scheme"`
|
||||
RPCWallet string `json:"rpc-wallet" mapstructure:"rpc-wallet"`
|
||||
ReissuanceAsset string `json:"reissuance-asset" mapstructure:"reissuance-asset"`
|
||||
ValidatorAddress string `json:"validator-address" mapstructure:"validator-address"`
|
||||
DistributionAddrInv string `json:"distribution-addr-inv" mapstructure:"distribution-addr-inv"`
|
||||
DistributionAddrDAO string `json:"distribution-addr-dao" mapstructure:"distribution-addr-dao"`
|
||||
DistributionAddrPop string `json:"distribution-addr-pop" mapstructure:"distribution-addr-pop"`
|
||||
DistributionEpochs int `json:"distribution-epochs" mapstructure:"distribution-epochs"`
|
||||
ReIssuanceEpochs int `json:"re-issuance-epochs" mapstructure:"re-issuance-epochs"`
|
||||
}
|
||||
|
||||
// cosmos-sdk wide global singleton
|
||||
@ -66,26 +73,29 @@ var (
|
||||
// DefaultConfig returns planetmint's default configuration.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
AssetRegistryEndpoint: "https://assets.rddl.io/register_asset",
|
||||
TokenDenom: "plmnt",
|
||||
StakeDenom: "plmntstake",
|
||||
FeeDenom: "plmnt",
|
||||
StagedDenom: "stagedcrddl",
|
||||
ClaimDenom: "crddl",
|
||||
ConfigRootDir: "",
|
||||
PopEpochs: 24, // 24 CometBFT epochs of 5s equate 120s
|
||||
RPCHost: "localhost",
|
||||
RPCPort: 18884,
|
||||
RPCUser: "user",
|
||||
RPCPassword: "password",
|
||||
IssuanceServiceDir: "/opt/issuer_service",
|
||||
ReissuanceAsset: "7add40beb27df701e02ee85089c5bc0021bc813823fedb5f1dcb5debda7f3da9",
|
||||
ValidatorAddress: "plmnt1w5dww335zhh98pzv783hqre355ck3u4w4hjxcx",
|
||||
DistributionAddrInv: "vjTyRN2G42Yq3T5TJBecHj1dF1xdhKF89hKV4HJN3uXxUbaVGVR76hAfVRQqQCovWaEpar7G5qBBprFG",
|
||||
DistributionAddrDAO: "vjU8eMzU3JbUWZEpVANt2ePJuPWSPixgjiSj2jDMvkVVQQi2DDnZuBRVX4Ygt5YGBf5zvTWCr1ntdqYH",
|
||||
DistributionAddrPop: "vjTvXCFSReRsZ7grdsAreRR12KuKpDw8idueQJK9Yh1BYS7ggAqgvCxCgwh13KGK6M52y37HUmvr4GdD",
|
||||
DistributionEpochs: 17640, // CometBFT epochs of 5s equate 1 day (12*60*24) + 15 min (15*24) to wait for confirmations on the re-issuance
|
||||
ReIssuanceEpochs: 17280, // CometBFT epochs of 5s equate 1 day (12*60*24)
|
||||
AssetRegistryScheme: "https",
|
||||
AssetRegistryDomain: "assets.rddl.io",
|
||||
AssetRegistryPath: "register_asset",
|
||||
TokenDenom: "plmnt",
|
||||
StakeDenom: "plmntstake",
|
||||
FeeDenom: "plmnt",
|
||||
StagedDenom: "stagedcrddl",
|
||||
ClaimDenom: "crddl",
|
||||
ConfigRootDir: "",
|
||||
PopEpochs: 24, // 24 CometBFT epochs of 5s equate 120s
|
||||
RPCHost: "localhost",
|
||||
RPCPort: 18884,
|
||||
RPCUser: "user",
|
||||
RPCPassword: "password",
|
||||
RPCScheme: "http",
|
||||
RPCWallet: "rpcwallet",
|
||||
ReissuanceAsset: "7add40beb27df701e02ee85089c5bc0021bc813823fedb5f1dcb5debda7f3da9",
|
||||
ValidatorAddress: "plmnt1w5dww335zhh98pzv783hqre355ck3u4w4hjxcx",
|
||||
DistributionAddrInv: "vjTyRN2G42Yq3T5TJBecHj1dF1xdhKF89hKV4HJN3uXxUbaVGVR76hAfVRQqQCovWaEpar7G5qBBprFG",
|
||||
DistributionAddrDAO: "vjU8eMzU3JbUWZEpVANt2ePJuPWSPixgjiSj2jDMvkVVQQi2DDnZuBRVX4Ygt5YGBf5zvTWCr1ntdqYH",
|
||||
DistributionAddrPop: "vjTvXCFSReRsZ7grdsAreRR12KuKpDw8idueQJK9Yh1BYS7ggAqgvCxCgwh13KGK6M52y37HUmvr4GdD",
|
||||
DistributionEpochs: 17640, // CometBFT epochs of 5s equate 1 day (12*60*24) + 15 min (15*24) to wait for confirmations on the re-issuance
|
||||
ReIssuanceEpochs: 17280, // CometBFT epochs of 5s equate 1 day (12*60*24)
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,6 +107,12 @@ func GetConfig() *Config {
|
||||
return plmntConfig
|
||||
}
|
||||
|
||||
// GetRPCURL returns the elements RPC URL
|
||||
func (config *Config) GetRPCURL() (url string) {
|
||||
url = fmt.Sprintf("%s://%s:%s@%s:%d/wallet/%s", config.RPCScheme, config.RPCUser, config.RPCPassword, config.RPCHost, config.RPCPort, config.RPCWallet)
|
||||
return
|
||||
}
|
||||
|
||||
func (config *Config) SetRoot(root string) *Config {
|
||||
config.ConfigRootDir = root
|
||||
return config
|
||||
|
1
go.mod
1
go.mod
@ -21,6 +21,7 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
|
||||
github.com/planetmint/planetmint-go/lib v0.2.1
|
||||
github.com/rddl-network/elements-rpc v0.2.0
|
||||
github.com/spf13/cast v1.5.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
|
2
go.sum
2
go.sum
@ -889,6 +889,8 @@ github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Ung
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rddl-network/elements-rpc v0.2.0 h1:xZCbNZaeVYO2gVnwYUF25/aPKcCYcOwjUZM0hb1C/xI=
|
||||
github.com/rddl-network/elements-rpc v0.2.0/go.mod h1:WOSYDMhq+V74lReSInnSejbdEyGI8hiQZSn4cSoFuxo=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
@ -3,16 +3,19 @@ package keeper
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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"
|
||||
elements "github.com/rddl-network/elements-rpc"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
@ -85,35 +88,93 @@ func validateExtendedPublicKey(issuer string, cfg chaincfg.Params) bool {
|
||||
func (k msgServer) issueNFTAsset(goCtx context.Context, name string, machineAddress string) (assetID string, contract string, err error) {
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
conf := config.GetConfig()
|
||||
cmdName := "poetry"
|
||||
cmdArgs := []string{"run", "python", "issuer_service/issue2liquid.py", name, machineAddress}
|
||||
|
||||
// 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()
|
||||
url := conf.GetRPCURL()
|
||||
address, err := elements.GetNewAddress(url, []string{``})
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "Issue2Liquid.py failed with %s\n", err)
|
||||
err = errorsmod.Wrap(types.ErrMachineNFTIssuance, stderr.String())
|
||||
} else {
|
||||
util.GetAppLogger().Info(ctx, "Liquid Token Issuance: "+stdout.String())
|
||||
lines := strings.Split(stdout.String(), "\n")
|
||||
if len(lines) == 3 {
|
||||
assetID = lines[0]
|
||||
contract = lines[1]
|
||||
} else {
|
||||
err = errorsmod.Wrap(types.ErrMachineNFTIssuanceNoOutput, stderr.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
addressInfo, err := elements.GetAddressInfo(url, []string{address})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hex, err := elements.CreateRawTransaction(url, []string{`[]`, `[{"data":"00"}]`})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fundRawTransactionResult, err := elements.FundRawTransaction(url, []string{hex, `{"feeRate":0.00001000}`})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := types.Contract{
|
||||
Entity: types.Entity{
|
||||
Domain: conf.AssetRegistryDomain,
|
||||
},
|
||||
IssuerPubkey: addressInfo.Pubkey,
|
||||
MachineAddr: machineAddress,
|
||||
Name: name,
|
||||
Precision: 0,
|
||||
Version: 0,
|
||||
}
|
||||
contractBytes, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// e.g. {"entity":{"domain":"testnet-assets.rddl.io"}, "issuer_pubkey":"02...}
|
||||
contract = string(contractBytes)
|
||||
|
||||
h := sha256.New()
|
||||
_, err = h.Write(contractBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// e.g. 7ca8bb403ee5dccddef7b89b163048cf39439553f0402351217a4a03d2224df8
|
||||
hash := h.Sum(nil)
|
||||
|
||||
// Reverse hash, e.g. f84d22d2034a7a21512340f053954339cf4830169bb8f7decddce53e40bba87c
|
||||
for i, j := 0, len(hash)-1; i < j; i, j = i+1, j-1 {
|
||||
hash[i], hash[j] = hash[j], hash[i]
|
||||
}
|
||||
|
||||
rawIssueAssetResults, err := elements.RawIssueAsset(url, []string{fundRawTransactionResult.Hex,
|
||||
`[{"asset_amount":0.00000001, "asset_address":"` + address + `", "blind":false, "contract_hash":"` + fmt.Sprintf("%+x", hash) + `"}]`,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rawIssueAssetResult := rawIssueAssetResults[len(rawIssueAssetResults)-1]
|
||||
hex, err = elements.BlindRawTransaction(url, []string{rawIssueAssetResult.Hex, `true`, `[]`, `false`})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assetID = rawIssueAssetResult.Asset
|
||||
|
||||
signRawTransactionWithWalletResult, err := elements.SignRawTransactionWithWallet(url, []string{hex})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
testMempoolAcceptResults, err := elements.TestMempoolAccept(url, []string{`["` + signRawTransactionWithWalletResult.Hex + `"]`})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
testMempoolAcceptResult := testMempoolAcceptResults[len(testMempoolAcceptResults)-1]
|
||||
if !testMempoolAcceptResult.Allowed {
|
||||
log.Fatalln("not accepted by mempool")
|
||||
}
|
||||
|
||||
hex, err = elements.SendRawTransaction(url, []string{signRawTransactionWithWalletResult.Hex})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
util.GetAppLogger().Info(ctx, "Liquid Token Issuance assetID: "+assetID+" contract: "+contract+" tx: "+hex)
|
||||
return assetID, contract, err
|
||||
}
|
||||
|
||||
@ -160,7 +221,8 @@ func (k msgServer) registerAsset(goCtx context.Context, assetID string, contract
|
||||
return errorsmod.Wrap(types.ErrAssetRegistryReqFailure, "Marshall "+err.Error())
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(goCtx, http.MethodPost, conf.AssetRegistryEndpoint, bytes.NewBuffer(jsonData))
|
||||
assetRegistryEndpoint := fmt.Sprintf("%s://%s/%s", conf.AssetRegistryScheme, conf.AssetRegistryDomain, conf.AssetRegistryPath)
|
||||
req, err := http.NewRequestWithContext(goCtx, http.MethodPost, assetRegistryEndpoint, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return errorsmod.Wrap(types.ErrAssetRegistryReqFailure, "Request creation: "+err.Error())
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ var (
|
||||
ErrInvalidTrustAnchorKey = errorsmod.Register(ModuleName, 9, "invalid trust anchor pubkey")
|
||||
ErrTrustAnchorAlreadyRegistered = errorsmod.Register(ModuleName, 10, "trust anchor is already registered")
|
||||
ErrMachineNFTIssuance = errorsmod.Register(ModuleName, 11, "the machine NFT could not be issued")
|
||||
ErrMachineNFTIssuanceNoOutput = errorsmod.Register(ModuleName, 12, "the machine NFT issuing process derivated")
|
||||
ErrAssetRegistryReqFailure = errorsmod.Register(ModuleName, 13, "request to asset registry could not be created")
|
||||
ErrAssetRegistryReqSending = errorsmod.Register(ModuleName, 14, "request to asset registry could not be sent")
|
||||
ErrAssetRegistryRepsonse = errorsmod.Register(ModuleName, 15, "request response issue")
|
||||
|
@ -1 +1,14 @@
|
||||
package types
|
||||
|
||||
type Entity struct {
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
type Contract struct {
|
||||
Entity Entity `json:"entity"`
|
||||
IssuerPubkey string `json:"issuer_pubkey"` //nolint:tagliatelle // the format liquid network needs it
|
||||
MachineAddr string `json:"machine_addr"` //nolint:tagliatelle // the format liquid network needs it
|
||||
Name string `json:"name"`
|
||||
Precision uint64 `json:"precision"`
|
||||
Version uint64 `json:"version"`
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user