feat(asset): add RPC client + CLI for confidential assets (HF5)
rpc/assets.go: DeployAsset, EmitAsset, BurnAsset, GetAssetInfo daemon/server.go: get_asset_info RPC method (native LTHN + custom assets) cmd_deploy_itns.go: CLI command to deploy ITNS trust token cmd/core-chain: register asset command group Ready for HF5 activation — `core-chain asset deploy-itns` deploys the ITNS token via the wallet RPC. Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
0634e29d62
commit
944291934e
4 changed files with 242 additions and 0 deletions
|
|
@ -15,5 +15,6 @@ func main() {
|
|||
cli.Main(
|
||||
cli.WithCommands("chain", blockchain.AddChainCommands),
|
||||
cli.WithCommands("wallet", blockchain.AddWalletCommands),
|
||||
cli.WithCommands("asset", blockchain.AddAssetCommands),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
108
cmd_deploy_itns.go
Normal file
108
cmd_deploy_itns.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
||||
//
|
||||
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"dappco.re/go/core"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
|
||||
"dappco.re/go/core/blockchain/rpc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// AddAssetCommands registers the "asset" command group.
|
||||
//
|
||||
// blockchain.AddAssetCommands(root)
|
||||
func AddAssetCommands(root *cobra.Command) {
|
||||
assetCmd := &cobra.Command{
|
||||
Use: "asset",
|
||||
Short: "Confidential asset operations (HF5+)",
|
||||
}
|
||||
|
||||
var walletRPC string
|
||||
assetCmd.PersistentFlags().StringVar(&walletRPC, "wallet-rpc", "http://127.0.0.1:46944", "wallet RPC URL")
|
||||
|
||||
assetCmd.AddCommand(
|
||||
newDeployITNSCmd(&walletRPC),
|
||||
newAssetInfoCmd(&walletRPC),
|
||||
)
|
||||
|
||||
root.AddCommand(assetCmd)
|
||||
}
|
||||
|
||||
func newDeployITNSCmd(walletRPC *string) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "deploy-itns",
|
||||
Short: "Deploy the ITNS trust token (requires HF5)",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDeployITNS(*walletRPC)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newAssetInfoCmd(walletRPC *string) *cobra.Command {
|
||||
var assetID string
|
||||
cmd := &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "Get asset info by ID or ticker",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runAssetInfo(*walletRPC, assetID)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&assetID, "asset", "LTHN", "asset ID or ticker")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDeployITNS(walletRPC string) error {
|
||||
client := rpc.NewClient(walletRPC)
|
||||
|
||||
desc := rpc.AssetDescriptor{
|
||||
Ticker: "ITNS",
|
||||
FullName: "IntenseCoin",
|
||||
TotalMax: 1000000000000000000, // 1B at 12 decimals
|
||||
CurrentSup: 0,
|
||||
DecimalPoint: 12,
|
||||
HiddenSupply: false,
|
||||
MetaInfo: core.Concat(`{"network":"lethean","type":"trust","purpose":"sidechain gateway trust token"}`),
|
||||
}
|
||||
|
||||
log.Println("Deploying ITNS (IntenseCoin) confidential asset...")
|
||||
log.Printf(" Ticker: %s", desc.Ticker)
|
||||
log.Printf(" Name: %s", desc.FullName)
|
||||
log.Printf(" Max supply: %d (1B ITNS)", desc.TotalMax)
|
||||
log.Printf(" Decimals: %d", desc.DecimalPoint)
|
||||
|
||||
resp, err := client.DeployAsset(desc)
|
||||
if err != nil {
|
||||
return coreerr.E("runDeployITNS", "deploy failed", err)
|
||||
}
|
||||
|
||||
log.Println("")
|
||||
log.Println("ITNS DEPLOYED!")
|
||||
log.Printf(" Asset ID: %s", resp.AssetID)
|
||||
log.Printf(" TX Hash: %s", resp.TxID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runAssetInfo(walletRPC, assetID string) error {
|
||||
client := rpc.NewClient(walletRPC)
|
||||
|
||||
info, err := client.GetAssetInfo(assetID)
|
||||
if err != nil {
|
||||
return coreerr.E("runAssetInfo", core.Sprintf("asset %s", assetID), err)
|
||||
}
|
||||
|
||||
log.Printf("Asset: %s (%s)", info.Ticker, info.FullName)
|
||||
log.Printf(" Max supply: %d", info.TotalMax)
|
||||
log.Printf(" Current supply: %d", info.CurrentSup)
|
||||
log.Printf(" Decimals: %d", info.DecimalPoint)
|
||||
log.Printf(" Hidden supply: %v", info.HiddenSupply)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -92,6 +92,8 @@ func (s *Server) handleJSONRPC(w http.ResponseWriter, r *http.Request) {
|
|||
s.rpcGetAliasDetails(w, req)
|
||||
case "getblockcount":
|
||||
s.rpcGetBlockCount(w, req)
|
||||
case "get_asset_info":
|
||||
s.rpcGetAssetInfo(w, req)
|
||||
default:
|
||||
writeError(w, req.ID, -32601, core.Sprintf("method %s not found", req.Method))
|
||||
}
|
||||
|
|
@ -273,3 +275,32 @@ func (s *Server) rpcGetBlockCount(w http.ResponseWriter, req jsonRPCRequest) {
|
|||
"status": "OK",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) rpcGetAssetInfo(w http.ResponseWriter, req jsonRPCRequest) {
|
||||
var params struct {
|
||||
AssetID string `json:"asset_id"`
|
||||
}
|
||||
if req.Params != nil {
|
||||
json.Unmarshal(req.Params, ¶ms)
|
||||
}
|
||||
|
||||
// For the native LTHN asset, return hardcoded descriptor
|
||||
if params.AssetID == "LTHN" || params.AssetID == "d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a" {
|
||||
writeResult(w, req.ID, map[string]interface{}{
|
||||
"asset_descriptor": map[string]interface{}{
|
||||
"ticker": "LTHN",
|
||||
"full_name": "Lethean",
|
||||
"total_max_supply": 0,
|
||||
"current_supply": 0,
|
||||
"decimal_point": 12,
|
||||
"hidden_supply": false,
|
||||
},
|
||||
"asset_id": "d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a",
|
||||
"status": "OK",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// For other assets, return not found (until asset index is built)
|
||||
writeError(w, req.ID, -1, core.Sprintf("asset %s not found", params.AssetID))
|
||||
}
|
||||
|
|
|
|||
102
rpc/assets.go
Normal file
102
rpc/assets.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
||||
//
|
||||
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"dappco.re/go/core"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
)
|
||||
|
||||
// AssetDescriptor describes a confidential asset for deployment.
|
||||
//
|
||||
// desc := rpc.AssetDescriptor{Ticker: "ITNS", FullName: "IntenseCoin"}
|
||||
type AssetDescriptor struct {
|
||||
Ticker string `json:"ticker"`
|
||||
FullName string `json:"full_name"`
|
||||
TotalMax uint64 `json:"total_max_supply"`
|
||||
CurrentSup uint64 `json:"current_supply"`
|
||||
DecimalPoint uint8 `json:"decimal_point"`
|
||||
HiddenSupply bool `json:"hidden_supply"`
|
||||
MetaInfo string `json:"meta_info"`
|
||||
}
|
||||
|
||||
// DeployAssetResponse is the response from deploy_asset.
|
||||
//
|
||||
// resp.AssetID // hex string
|
||||
// resp.TxID // transaction hash
|
||||
type DeployAssetResponse struct {
|
||||
AssetID string `json:"new_asset_id"`
|
||||
TxID string `json:"tx_id"`
|
||||
}
|
||||
|
||||
// DeployAsset deploys a new confidential asset on the chain (HF5+).
|
||||
//
|
||||
// resp, err := client.DeployAsset(desc)
|
||||
func (c *Client) DeployAsset(desc AssetDescriptor) (*DeployAssetResponse, error) {
|
||||
params := struct {
|
||||
Descriptor AssetDescriptor `json:"asset_descriptor"`
|
||||
}{Descriptor: desc}
|
||||
|
||||
var resp DeployAssetResponse
|
||||
if err := c.call("deploy_asset", params, &resp); err != nil {
|
||||
return nil, coreerr.E("Client.DeployAsset", "deploy_asset", err)
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// EmitAsset emits (mints) additional supply of an existing asset.
|
||||
//
|
||||
// resp, err := client.EmitAsset(assetID, amount)
|
||||
func (c *Client) EmitAsset(assetID string, amount uint64) (string, error) {
|
||||
params := struct {
|
||||
AssetID string `json:"asset_id"`
|
||||
Amount uint64 `json:"amount"`
|
||||
}{AssetID: assetID, Amount: amount}
|
||||
|
||||
var resp struct {
|
||||
TxID string `json:"tx_id"`
|
||||
}
|
||||
if err := c.call("emit_asset", params, &resp); err != nil {
|
||||
return "", coreerr.E("Client.EmitAsset", "emit_asset", err)
|
||||
}
|
||||
return resp.TxID, nil
|
||||
}
|
||||
|
||||
// BurnAsset burns (destroys) supply of an asset.
|
||||
//
|
||||
// resp, err := client.BurnAsset(assetID, amount)
|
||||
func (c *Client) BurnAsset(assetID string, amount uint64) (string, error) {
|
||||
params := struct {
|
||||
AssetID string `json:"asset_id"`
|
||||
Amount uint64 `json:"amount"`
|
||||
}{AssetID: assetID, Amount: amount}
|
||||
|
||||
var resp struct {
|
||||
TxID string `json:"tx_id"`
|
||||
}
|
||||
if err := c.call("burn_asset", params, &resp); err != nil {
|
||||
return "", coreerr.E("Client.BurnAsset", "burn_asset", err)
|
||||
}
|
||||
return resp.TxID, nil
|
||||
}
|
||||
|
||||
// GetAssetInfo retrieves the descriptor for an asset by ID or ticker.
|
||||
//
|
||||
// info, err := client.GetAssetInfo("LTHN")
|
||||
func (c *Client) GetAssetInfo(assetIDOrTicker string) (*AssetDescriptor, error) {
|
||||
params := struct {
|
||||
AssetID string `json:"asset_id"`
|
||||
}{AssetID: assetIDOrTicker}
|
||||
|
||||
var resp struct {
|
||||
Descriptor AssetDescriptor `json:"asset_descriptor"`
|
||||
AssetID string `json:"asset_id"`
|
||||
}
|
||||
if err := c.call("get_asset_info", params, &resp); err != nil {
|
||||
return nil, coreerr.E("Client.GetAssetInfo", core.Sprintf("get_asset_info %s", assetIDOrTicker), err)
|
||||
}
|
||||
return &resp.Descriptor, nil
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue