mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 12:14:21 +01:00
* [btc-embedded] add chainfee.Estimator and extraAPI interfaces * dynamic fee amount * dynamic dust amount * [client] fix linter errors * [domain] fix unit tests * [server] return dust amount in GetInfo RPC * [sdk] fix lnd dependencie * go work sync * fix witness stack size forfeit tx size estimator * remove hardcoded fee values in covenant txbuilder * lower liquid feerate * fix after reviews * go work sync
305 lines
6.4 KiB
Go
305 lines
6.4 KiB
Go
package utils
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/ark-network/ark/common"
|
|
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
const (
|
|
ASP_URL = "asp_url"
|
|
ASP_PUBKEY = "asp_public_key"
|
|
ROUND_LIFETIME = "round_lifetime"
|
|
UNILATERAL_EXIT_DELAY = "unilateral_exit_delay"
|
|
BOARDING_TEMPLATE = "boarding_template"
|
|
ENCRYPTED_PRVKEY = "encrypted_private_key"
|
|
PASSWORD_HASH = "password_hash"
|
|
PUBKEY = "public_key"
|
|
NETWORK = "network"
|
|
EXPLORER = "explorer"
|
|
DUST = "dust"
|
|
|
|
defaultNetwork = "liquid"
|
|
state_file = "state.json"
|
|
)
|
|
|
|
var initialState = map[string]string{
|
|
ASP_URL: "",
|
|
ASP_PUBKEY: "",
|
|
ROUND_LIFETIME: "",
|
|
UNILATERAL_EXIT_DELAY: "",
|
|
ENCRYPTED_PRVKEY: "",
|
|
PASSWORD_HASH: "",
|
|
PUBKEY: "",
|
|
DUST: "546",
|
|
NETWORK: defaultNetwork,
|
|
}
|
|
|
|
func GetNetwork(ctx *cli.Context) (*common.Network, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
net, ok := state[NETWORK]
|
|
if !ok {
|
|
return nil, fmt.Errorf("network not found in state")
|
|
}
|
|
return networkFromString(net), nil
|
|
}
|
|
|
|
func GetRoundLifetime(ctx *cli.Context) (int64, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
lifetime := state[ROUND_LIFETIME]
|
|
if len(lifetime) <= 0 {
|
|
return -1, fmt.Errorf("missing round lifetime")
|
|
}
|
|
|
|
roundLifetime, err := strconv.Atoi(lifetime)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
return int64(roundLifetime), nil
|
|
}
|
|
|
|
func GetUnilateralExitDelay(ctx *cli.Context) (int64, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
delay := state[UNILATERAL_EXIT_DELAY]
|
|
if len(delay) <= 0 {
|
|
return -1, fmt.Errorf("missing unilateral exit delay")
|
|
}
|
|
|
|
redeemDelay, err := strconv.Atoi(delay)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
return int64(redeemDelay), nil
|
|
}
|
|
|
|
func GetBoardingDescriptor(ctx *cli.Context) (string, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
pubkey, err := GetWalletPublicKey(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
template := state[BOARDING_TEMPLATE]
|
|
if len(template) <= 0 {
|
|
return "", fmt.Errorf("missing boarding descriptor template")
|
|
}
|
|
|
|
pubkeyhex := hex.EncodeToString(schnorr.SerializePubKey(pubkey))
|
|
|
|
return strings.ReplaceAll(template, "USER", pubkeyhex), nil
|
|
}
|
|
|
|
func GetWalletPublicKey(ctx *cli.Context) (*secp256k1.PublicKey, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
publicKeyString := state[PUBKEY]
|
|
if len(publicKeyString) <= 0 {
|
|
return nil, fmt.Errorf("missing public key")
|
|
}
|
|
|
|
publicKeyBytes, err := hex.DecodeString(publicKeyString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return secp256k1.ParsePubKey(publicKeyBytes)
|
|
}
|
|
|
|
func GetAspPublicKey(ctx *cli.Context) (*secp256k1.PublicKey, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arkPubKey := state[ASP_PUBKEY]
|
|
if len(arkPubKey) <= 0 {
|
|
return nil, fmt.Errorf("missing asp public key")
|
|
}
|
|
|
|
pubKeyBytes, err := hex.DecodeString(arkPubKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return secp256k1.ParsePubKey(pubKeyBytes)
|
|
}
|
|
|
|
func GetDust(ctx *cli.Context) (uint64, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
dust := state[DUST]
|
|
if len(dust) <= 0 {
|
|
return 0, fmt.Errorf("missing dust")
|
|
}
|
|
|
|
dustAmount, err := strconv.Atoi(dust)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return uint64(dustAmount), nil
|
|
}
|
|
|
|
func GetState(ctx *cli.Context) (map[string]string, error) {
|
|
datadir := ctx.String("datadir")
|
|
stateFilePath := filepath.Join(datadir, state_file)
|
|
file, err := os.ReadFile(stateFilePath)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return nil, err
|
|
}
|
|
if err := setInitialState(stateFilePath); err != nil {
|
|
return nil, err
|
|
}
|
|
return initialState, nil
|
|
}
|
|
|
|
data := map[string]string{}
|
|
if err := json.Unmarshal(file, &data); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func PrivateKeyFromPassword(ctx *cli.Context) (*secp256k1.PrivateKey, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
encryptedPrivateKeyString := state[ENCRYPTED_PRVKEY]
|
|
if len(encryptedPrivateKeyString) <= 0 {
|
|
return nil, fmt.Errorf("missing encrypted private key")
|
|
}
|
|
|
|
encryptedPrivateKey, err := hex.DecodeString(encryptedPrivateKeyString)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid encrypted private key: %s", err)
|
|
}
|
|
|
|
password, err := ReadPassword(ctx, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fmt.Println("wallet unlocked")
|
|
|
|
cypher := NewAES128Cypher()
|
|
privateKeyBytes, err := cypher.decrypt(encryptedPrivateKey, password)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
privateKey := secp256k1.PrivKeyFromBytes(privateKeyBytes)
|
|
return privateKey, nil
|
|
}
|
|
|
|
func SetState(ctx *cli.Context, data map[string]string) error {
|
|
currentData, err := GetState(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
mergedData := merge(currentData, data)
|
|
|
|
jsonString, err := json.Marshal(mergedData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
datadir := ctx.String("datadir")
|
|
statePath := filepath.Join(datadir, state_file)
|
|
|
|
err = os.WriteFile(statePath, jsonString, 0755)
|
|
if err != nil {
|
|
return fmt.Errorf("writing to file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func networkFromString(net string) *common.Network {
|
|
switch net {
|
|
case common.Liquid.Name:
|
|
return &common.Liquid
|
|
case common.LiquidTestNet.Name:
|
|
return &common.LiquidTestNet
|
|
case common.LiquidRegTest.Name:
|
|
return &common.LiquidRegTest
|
|
case common.Bitcoin.Name:
|
|
return &common.Bitcoin
|
|
case common.BitcoinTestNet.Name:
|
|
return &common.BitcoinTestNet
|
|
case common.BitcoinRegTest.Name:
|
|
return &common.BitcoinRegTest
|
|
case common.BitcoinSigNet.Name:
|
|
return &common.BitcoinSigNet
|
|
default:
|
|
panic(fmt.Sprintf("unknown network (%s)", net))
|
|
}
|
|
}
|
|
|
|
func setInitialState(stateFilePath string) error {
|
|
jsonString, err := json.Marshal(initialState)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(stateFilePath, jsonString, 0755)
|
|
}
|
|
|
|
func getBaseURL(ctx *cli.Context) (string, error) {
|
|
state, err := GetState(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
baseURL := state[EXPLORER]
|
|
if len(baseURL) <= 0 {
|
|
return "", fmt.Errorf("missing explorer base url")
|
|
}
|
|
|
|
return baseURL, nil
|
|
}
|
|
|
|
func merge(maps ...map[string]string) map[string]string {
|
|
merge := make(map[string]string, 0)
|
|
for _, m := range maps {
|
|
for k, v := range m {
|
|
merge[k] = v
|
|
}
|
|
}
|
|
return merge
|
|
}
|