Files
ark/noah/common.go
Pietralberto Mazza d150c4bbac Add support for collaborative redemption of vtxos & Changes to ark wallet (#72)
* Add internal support for collaborative exit

* Update protos and interface layer

* Fixes after proto updates

* Fix printing json & Do not print ark pubkey in config

* Add collaborative redeem command

* Polish

* Add address validation

* Fix building tree without right branch

* Fixes and validation checks

* Fixes

* Fix counting complete queued payments

* Add relays

* Add and compute onchain balance concurrently

* Tiny refactor

* Merge `config connect` into `init` cmd
2023-12-29 17:09:50 +01:00

267 lines
5.2 KiB
Go

package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"syscall"
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
"github.com/ark-network/ark/common"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/urfave/cli/v2"
"github.com/vulpemventures/go-elements/network"
"github.com/vulpemventures/go-elements/payment"
"golang.org/x/term"
)
func hashPassword(password []byte) []byte {
hash := sha256.Sum256(password)
return hash[:]
}
func verifyPassword(password []byte) error {
state, err := getState()
if err != nil {
return err
}
passwordHashString, ok := state["password_hash"]
if !ok {
return fmt.Errorf("password hash not found")
}
passwordHash, err := hex.DecodeString(passwordHashString)
if err != nil {
return err
}
currentPassHash := hashPassword(password)
if !bytes.Equal(passwordHash, currentPassHash) {
return fmt.Errorf("invalid password")
}
return nil
}
func readPassword() ([]byte, error) {
fmt.Print("password: ")
passwordInput, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println() // new line
if err != nil {
return nil, err
}
err = verifyPassword(passwordInput)
if err != nil {
return nil, err
}
return passwordInput, nil
}
func privateKeyFromPassword() (*secp256k1.PrivateKey, error) {
state, err := getState()
if err != nil {
return nil, err
}
encryptedPrivateKeyString, ok := state["encrypted_private_key"]
if !ok {
return nil, fmt.Errorf("encrypted private key not found")
}
encryptedPrivateKey, err := hex.DecodeString(encryptedPrivateKeyString)
if err != nil {
return nil, err
}
password, err := readPassword()
if err != nil {
return nil, err
}
cypher := NewAES128Cypher()
privateKeyBytes, err := cypher.Decrypt(encryptedPrivateKey, password)
if err != nil {
return nil, err
}
privateKey := secp256k1.PrivKeyFromBytes(privateKeyBytes)
return privateKey, nil
}
func getWalletPublicKey() (*secp256k1.PublicKey, error) {
state, err := getState()
if err != nil {
return nil, err
}
publicKeyString, ok := state["public_key"]
if !ok {
return nil, fmt.Errorf("public key not found")
}
_, publicKey, err := common.DecodePubKey(publicKeyString)
if err != nil {
return nil, err
}
return publicKey, nil
}
func getServiceProviderPublicKey() (*secp256k1.PublicKey, error) {
state, err := getState()
if err != nil {
return nil, err
}
arkPubKey, ok := state["ark_pubkey"]
if !ok {
return nil, fmt.Errorf("ark public key not found")
}
_, pubKey, err := common.DecodePubKey(arkPubKey)
if err != nil {
return nil, err
}
return pubKey, nil
}
func coinSelect(vtxos []vtxo, amount uint64) ([]vtxo, uint64, error) {
selected := make([]vtxo, 0)
selectedAmount := uint64(0)
for _, vtxo := range vtxos {
if selectedAmount >= amount {
break
}
selected = append(selected, vtxo)
selectedAmount += vtxo.amount
}
if selectedAmount < amount {
return nil, 0, fmt.Errorf("insufficient balance: %d to cover %d", selectedAmount, amount)
}
change := selectedAmount - amount
return selected, change, nil
}
func getOffchainBalance(
ctx *cli.Context, client arkv1.ArkServiceClient, addr string,
) (uint64, error) {
vtxos, err := getVtxos(ctx, client, addr)
if err != nil {
return 0, err
}
var balance uint64
for _, vtxo := range vtxos {
balance += vtxo.amount
}
return balance, nil
}
func getOnchainBalance(addr string) (uint64, error) {
_, net, err := getNetwork()
if err != nil {
return 0, err
}
baseUrl := explorerUrl[net.Name]
resp, err := http.Get(fmt.Sprintf("%s/address/%s/utxo", baseUrl, addr))
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return 0, err
}
if resp.StatusCode != http.StatusOK {
return 0, fmt.Errorf(string(body))
}
payload := []interface{}{}
if err := json.Unmarshal(body, &payload); err != nil {
return 0, err
}
balance := uint64(0)
for _, p := range payload {
utxo := p.(map[string]interface{})
asset, ok := utxo["asset"].(string)
if !ok || asset != net.AssetID {
continue
}
balance += uint64(utxo["value"].(float64))
}
return balance, nil
}
func getNetwork() (*common.Network, *network.Network, error) {
state, err := getState()
if err != nil {
return nil, nil, err
}
net, ok := state["network"]
if !ok {
return &common.MainNet, &network.Liquid, nil
}
if net == "testnet" {
return &common.TestNet, &network.Testnet, nil
}
return &common.MainNet, &network.Liquid, nil
}
func getAddress() (offchainAddr, onchainAddr string, err error) {
publicKey, err := getWalletPublicKey()
if err != nil {
return
}
aspPublicKey, err := getServiceProviderPublicKey()
if err != nil {
return
}
arkNet, liquidNet, err := getNetwork()
if err != nil {
return
}
arkAddr, err := common.EncodeAddress(arkNet.Addr, publicKey, aspPublicKey)
if err != nil {
return
}
p2wpkh := payment.FromPublicKey(publicKey, liquidNet, nil)
liquidAddr, err := p2wpkh.WitnessPubKeyHash()
if err != nil {
return
}
offchainAddr = arkAddr
onchainAddr = liquidAddr
return
}
func printJSON(resp interface{}) error {
jsonBytes, err := json.MarshalIndent(resp, "", "\t")
if err != nil {
return err
}
fmt.Println(string(jsonBytes))
return nil
}