mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 04:04:21 +01:00
Delay unilateral exit and support send to onchain address (#117)
* add delay on redeem close + forfeit close * increase default round lifetime (16 minutes min) * add sequence to final pset * update CLI and server to support delayed vtxos oncahin * rename future to "locked" * add configurable EXIT_DELAY variable * renaming * rename "close" --> "closure" * rename "close" to "closure" * error message config.go
This commit is contained in:
@@ -38,9 +38,9 @@ func balanceAction(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
wg.Add(3)
|
||||
|
||||
chRes := make(chan balanceRes, 2)
|
||||
chRes := make(chan balanceRes, 3)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
explorer := NewExplorer()
|
||||
@@ -48,24 +48,37 @@ func balanceAction(ctx *cli.Context) error {
|
||||
ctx, explorer, client, offchainAddr, withExpiryDetails,
|
||||
)
|
||||
if err != nil {
|
||||
chRes <- balanceRes{0, 0, nil, err}
|
||||
chRes <- balanceRes{0, 0, nil, nil, err}
|
||||
return
|
||||
}
|
||||
|
||||
chRes <- balanceRes{balance, 0, amountByExpiration, nil}
|
||||
chRes <- balanceRes{balance, 0, nil, amountByExpiration, nil}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
balance, err := getOnchainBalance(onchainAddr)
|
||||
if err != nil {
|
||||
chRes <- balanceRes{0, 0, nil, err}
|
||||
chRes <- balanceRes{0, 0, nil, nil, err}
|
||||
return
|
||||
}
|
||||
chRes <- balanceRes{0, balance, nil, nil}
|
||||
chRes <- balanceRes{0, balance, nil, nil, nil}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
availableBalance, futureBalance, err := getOnchainVtxosBalance()
|
||||
if err != nil {
|
||||
chRes <- balanceRes{0, 0, nil, nil, err}
|
||||
return
|
||||
}
|
||||
|
||||
chRes <- balanceRes{0, availableBalance, futureBalance, nil, err}
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
lockedOnchainBalance := []map[string]interface{}{}
|
||||
details := make([]map[string]interface{}, 0)
|
||||
offchainBalance, onchainBalance := uint64(0), uint64(0)
|
||||
nextExpiration := int64(0)
|
||||
@@ -78,7 +91,7 @@ func balanceAction(ctx *cli.Context) error {
|
||||
offchainBalance = res.offchainBalance
|
||||
}
|
||||
if res.onchainBalance > 0 {
|
||||
onchainBalance = res.onchainBalance
|
||||
onchainBalance += res.onchainBalance
|
||||
}
|
||||
if res.amountByExpiration != nil {
|
||||
for timestamp, amount := range res.amountByExpiration {
|
||||
@@ -96,15 +109,33 @@ func balanceAction(ctx *cli.Context) error {
|
||||
)
|
||||
}
|
||||
}
|
||||
if res.futureBalance != nil {
|
||||
for timestamp, amount := range res.futureBalance {
|
||||
fancyTime := time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")
|
||||
lockedOnchainBalance = append(
|
||||
lockedOnchainBalance,
|
||||
map[string]interface{}{
|
||||
"spendable_at": fancyTime,
|
||||
"amount": amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
count++
|
||||
if count == 2 {
|
||||
if count == 3 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
response := make(map[string]interface{})
|
||||
response["onchain_balance"] = onchainBalance
|
||||
response["onchain_balance"] = map[string]interface{}{
|
||||
"spendable_amount": onchainBalance,
|
||||
}
|
||||
|
||||
if len(lockedOnchainBalance) > 0 {
|
||||
response["onchain_balance"].(map[string]interface{})["locked_amount"] = lockedOnchainBalance
|
||||
}
|
||||
|
||||
offchainBalanceJSON := map[string]interface{}{
|
||||
"total": offchainBalance,
|
||||
@@ -148,6 +179,7 @@ func balanceAction(ctx *cli.Context) error {
|
||||
type balanceRes struct {
|
||||
offchainBalance uint64
|
||||
onchainBalance uint64
|
||||
amountByExpiration map[int64]uint64
|
||||
futureBalance map[int64]uint64 // availableAt -> onchain balance
|
||||
amountByExpiration map[int64]uint64 // expireAt -> offchain balance
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func getClientFromState(ctx *cli.Context) (arkv1.ArkServiceClient, func(), error
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
addr, ok := state["ark_url"]
|
||||
addr, ok := state["ark_url"].(string)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("missing ark_url")
|
||||
}
|
||||
|
||||
414
client/common.go
414
client/common.go
@@ -15,15 +15,16 @@ import (
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/elementsutil"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/payment"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/taproot"
|
||||
"github.com/vulpemventures/go-elements/transaction"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
@@ -42,7 +43,7 @@ func verifyPassword(password []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
passwordHashString, ok := state["password_hash"]
|
||||
passwordHashString, ok := state["password_hash"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("password hash not found")
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func privateKeyFromPassword() (*secp256k1.PrivateKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encryptedPrivateKeyString, ok := state["encrypted_private_key"]
|
||||
encryptedPrivateKeyString, ok := state["encrypted_private_key"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("encrypted private key not found")
|
||||
}
|
||||
@@ -114,7 +115,7 @@ func getWalletPublicKey() (*secp256k1.PublicKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKeyString, ok := state["public_key"]
|
||||
publicKeyString, ok := state["public_key"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("public key not found")
|
||||
}
|
||||
@@ -133,7 +134,7 @@ func getServiceProviderPublicKey() (*secp256k1.PublicKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
arkPubKey, ok := state["ark_pubkey"]
|
||||
arkPubKey, ok := state["ark_pubkey"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("ark public key not found")
|
||||
}
|
||||
@@ -146,6 +147,34 @@ func getServiceProviderPublicKey() (*secp256k1.PublicKey, error) {
|
||||
return secp256k1.ParsePubKey(pubKeyBytes)
|
||||
}
|
||||
|
||||
func getLifetime() (int64, error) {
|
||||
state, err := getState()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
lifetime, ok := state["ark_lifetime"].(float64)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("lifetime not found")
|
||||
}
|
||||
|
||||
return int64(lifetime), nil
|
||||
}
|
||||
|
||||
func getExitDelay() (int64, error) {
|
||||
state, err := getState()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
exitDelay, ok := state["exit_delay"].(float64)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("exit delay not found")
|
||||
}
|
||||
|
||||
return int64(exitDelay), nil
|
||||
}
|
||||
|
||||
func coinSelect(vtxos []vtxo, amount uint64) ([]vtxo, uint64, error) {
|
||||
selected := make([]vtxo, 0)
|
||||
notSelected := make([]vtxo, 0)
|
||||
@@ -218,6 +247,10 @@ type utxo struct {
|
||||
Vout uint32 `json:"vout"`
|
||||
Amount uint64 `json:"value"`
|
||||
Asset string `json:"asset"`
|
||||
Status struct {
|
||||
Confirmed bool `json:"confirmed"`
|
||||
Blocktime int64 `json:"block_time"`
|
||||
} `json:"status"`
|
||||
}
|
||||
|
||||
func getOnchainUtxos(addr string) ([]utxo, error) {
|
||||
@@ -227,6 +260,7 @@ func getOnchainUtxos(addr string) ([]utxo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
@@ -260,6 +294,68 @@ func getOnchainBalance(addr string) (uint64, error) {
|
||||
return balance, nil
|
||||
}
|
||||
|
||||
func getOnchainVtxosBalance() (availableBalance uint64, futureBalance map[int64]uint64, err error) {
|
||||
userPubKey, err := getWalletPublicKey()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
aspPublicKey, err := getServiceProviderPublicKey()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
exitDelay, err := getExitDelay()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
vtxoTapKey, _, err := computeVtxoTaprootScript(userPubKey, aspPublicKey, uint(exitDelay))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, net := getNetwork()
|
||||
|
||||
payment, err := payment.FromTweakedKey(vtxoTapKey, net, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
addr, err := payment.TaprootAddress()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
utxos, err := getOnchainUtxos(addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
availableBalance = uint64(0)
|
||||
futureBalance = make(map[int64]uint64, 0)
|
||||
now := time.Now()
|
||||
for _, utxo := range utxos {
|
||||
blocktime := now
|
||||
if utxo.Status.Confirmed {
|
||||
blocktime = time.Unix(utxo.Status.Blocktime, 0)
|
||||
}
|
||||
|
||||
availableAt := blocktime.Add(time.Duration(exitDelay) * time.Second)
|
||||
if availableAt.After(now) {
|
||||
if _, ok := futureBalance[availableAt.Unix()]; !ok {
|
||||
futureBalance[availableAt.Unix()] = 0
|
||||
}
|
||||
|
||||
futureBalance[availableAt.Unix()] += utxo.Amount
|
||||
} else {
|
||||
availableBalance += utxo.Amount
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getTxBlocktime(txid string) (confirmed bool, blocktime int64, err error) {
|
||||
_, net := getNetwork()
|
||||
baseUrl := explorerUrl[net.Name]
|
||||
@@ -431,7 +527,7 @@ func handleRoundStream(
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, seconds, err := findSweepClosure(congestionTree)
|
||||
seconds, err := getLifetime()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -446,8 +542,7 @@ func handleRoundStream(
|
||||
return "", err
|
||||
}
|
||||
|
||||
// validate the receivers
|
||||
sweepLeaf, err := tree.SweepScript(aspPublicKey, seconds)
|
||||
exitDelay, err := getExitDelay()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -485,16 +580,11 @@ func handleRoundStream(
|
||||
found := false
|
||||
|
||||
// compute the receiver output taproot key
|
||||
vtxoScript, err := tree.VtxoScript(userPubKey)
|
||||
outputTapKey, _, err := computeVtxoTaprootScript(userPubKey, aspPublicKey, uint(exitDelay))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
vtxoTaprootTree := taproot.AssembleTaprootScriptTree(*vtxoScript, *sweepLeaf)
|
||||
root := vtxoTaprootTree.RootNode.TapHash()
|
||||
unspendableKey := tree.UnspendableKey()
|
||||
vtxoTaprootKey := schnorr.SerializePubKey(taproot.ComputeTaprootOutputKey(unspendableKey, root[:]))
|
||||
|
||||
leaves := congestionTree.Leaves()
|
||||
for _, leaf := range leaves {
|
||||
tx, err := psetv2.NewPsetFromBase64(leaf.Tx)
|
||||
@@ -506,7 +596,7 @@ func handleRoundStream(
|
||||
if len(output.Script) == 0 {
|
||||
continue
|
||||
}
|
||||
if bytes.Equal(output.Script[2:], vtxoTaprootKey) {
|
||||
if bytes.Equal(output.Script[2:], outputTapKey.SerializeCompressed()) {
|
||||
if output.Value != receiver.Amount {
|
||||
continue
|
||||
}
|
||||
@@ -676,15 +766,15 @@ func findSweepClosure(
|
||||
}
|
||||
|
||||
for _, tapLeaf := range tx.Inputs[0].TapLeafScript {
|
||||
isSweep, _, lifetime, err := tree.DecodeSweepScript(tapLeaf.Script)
|
||||
closure := &tree.CSVSigClosure{}
|
||||
valid, err := closure.Decode(tapLeaf.Script)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if isSweep {
|
||||
seconds = lifetime
|
||||
if valid && closure.Seconds > seconds {
|
||||
seconds = closure.Seconds
|
||||
sweepClosure = &tapLeaf.TapElementsLeaf
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,3 +822,289 @@ func getRedeemBranches(
|
||||
|
||||
return redeemBranches, nil
|
||||
}
|
||||
|
||||
func computeVtxoTaprootScript(
|
||||
userPubKey *secp256k1.PublicKey,
|
||||
aspPublicKey *secp256k1.PublicKey,
|
||||
exitDelay uint,
|
||||
) (*secp256k1.PublicKey, *taproot.TapscriptElementsProof, error) {
|
||||
redeemClosure := &tree.CSVSigClosure{
|
||||
Pubkey: userPubKey,
|
||||
Seconds: exitDelay,
|
||||
}
|
||||
|
||||
forfeitClosure := &tree.ForfeitClosure{
|
||||
Pubkey: userPubKey,
|
||||
AspPubkey: aspPublicKey,
|
||||
}
|
||||
|
||||
redeemLeaf, err := redeemClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
vtxoTaprootTree := taproot.AssembleTaprootScriptTree(*redeemLeaf, *forfeitLeaf)
|
||||
root := vtxoTaprootTree.RootNode.TapHash()
|
||||
|
||||
unspendableKey := tree.UnspendableKey()
|
||||
vtxoTaprootKey := taproot.ComputeTaprootOutputKey(unspendableKey, root[:])
|
||||
|
||||
redeemLeafHash := redeemLeaf.TapHash()
|
||||
proofIndex := vtxoTaprootTree.LeafProofIndex[redeemLeafHash]
|
||||
proof := vtxoTaprootTree.LeafMerkleProofs[proofIndex]
|
||||
|
||||
return vtxoTaprootKey, &proof, nil
|
||||
}
|
||||
|
||||
func addVtxoInput(
|
||||
updater *psetv2.Updater,
|
||||
inputArgs psetv2.InputArgs,
|
||||
exitDelay uint,
|
||||
tapLeafProof *taproot.TapscriptElementsProof,
|
||||
) error {
|
||||
sequence, err := common.BIP68EncodeAsNumber(exitDelay)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nextInputIndex := len(updater.Pset.Inputs)
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{inputArgs}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updater.Pset.Inputs[nextInputIndex].Sequence = sequence
|
||||
|
||||
return updater.AddInTapLeafScript(
|
||||
nextInputIndex,
|
||||
psetv2.NewTapLeafScript(
|
||||
*tapLeafProof,
|
||||
tree.UnspendableKey(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func coinSelectOnchain(targetAmount uint64, exclude []utxo) (utxos []utxo, delayedUtxos []utxo, change uint64, err error) {
|
||||
_, onchainAddr, err := getAddress()
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
fromExplorer, err := getOnchainUtxos(onchainAddr)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
utxos = make([]utxo, 0)
|
||||
selectedAmount := uint64(0)
|
||||
for _, utxo := range fromExplorer {
|
||||
if selectedAmount >= targetAmount {
|
||||
break
|
||||
}
|
||||
|
||||
for _, excluded := range exclude {
|
||||
if utxo.Txid == excluded.Txid && utxo.Vout == excluded.Vout {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
utxos = append(utxos, utxo)
|
||||
selectedAmount += utxo.Amount
|
||||
}
|
||||
|
||||
if selectedAmount >= targetAmount {
|
||||
return utxos, nil, selectedAmount - targetAmount, nil
|
||||
}
|
||||
|
||||
userPubKey, err := getWalletPublicKey()
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
aspPublicKey, err := getServiceProviderPublicKey()
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
exitDelay, err := getExitDelay()
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
vtxoTapKey, _, err := computeVtxoTaprootScript(userPubKey, aspPublicKey, uint(exitDelay))
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
_, net := getNetwork()
|
||||
|
||||
pay, err := payment.FromTweakedKey(vtxoTapKey, net, nil)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
addr, err := pay.TaprootAddress()
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
fromExplorer, err = getOnchainUtxos(addr)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
delayedUtxos = make([]utxo, 0)
|
||||
for _, utxo := range fromExplorer {
|
||||
if selectedAmount >= targetAmount {
|
||||
break
|
||||
}
|
||||
|
||||
availableAt := time.Unix(utxo.Status.Blocktime, 0).Add(time.Duration(exitDelay) * time.Second)
|
||||
if availableAt.After(time.Now()) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, excluded := range exclude {
|
||||
if utxo.Txid == excluded.Txid && utxo.Vout == excluded.Vout {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
delayedUtxos = append(delayedUtxos, utxo)
|
||||
selectedAmount += utxo.Amount
|
||||
}
|
||||
|
||||
if selectedAmount < targetAmount {
|
||||
return nil, nil, 0, fmt.Errorf("insufficient balance: %d to cover %d", selectedAmount, targetAmount)
|
||||
}
|
||||
|
||||
return utxos, delayedUtxos, selectedAmount - targetAmount, nil
|
||||
}
|
||||
|
||||
func addInputs(
|
||||
updater *psetv2.Updater,
|
||||
selected []utxo, // the utxos to add owned by the P2WPKH script
|
||||
delayedSelected []utxo, // the utxos to add owned by the VTXO script
|
||||
net *network.Network,
|
||||
) error {
|
||||
_, onchainAddr, err := getAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changeScript, err := address.ToOutputScript(onchainAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, coin := range selected {
|
||||
fmt.Println("adding input", coin.Txid, coin.Vout)
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{
|
||||
{
|
||||
Txid: coin.Txid,
|
||||
TxIndex: coin.Vout,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
assetID, err := elementsutil.AssetHashToBytes(coin.Asset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := elementsutil.ValueToBytes(coin.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
witnessUtxo := transaction.TxOutput{
|
||||
Asset: assetID,
|
||||
Value: value,
|
||||
Script: changeScript,
|
||||
Nonce: []byte{0x00},
|
||||
}
|
||||
|
||||
if err := updater.AddInWitnessUtxo(len(updater.Pset.Inputs)-1, &witnessUtxo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(delayedSelected) > 0 {
|
||||
userPubKey, err := getWalletPublicKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aspPublicKey, err := getServiceProviderPublicKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exitDelay, err := getExitDelay()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vtxoTapKey, leafProof, err := computeVtxoTaprootScript(userPubKey, aspPublicKey, uint(exitDelay))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pay, err := payment.FromTweakedKey(vtxoTapKey, net, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr, err := pay.TaprootAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
script, err := address.ToOutputScript(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, coin := range delayedSelected {
|
||||
if err := addVtxoInput(
|
||||
updater,
|
||||
psetv2.InputArgs{
|
||||
Txid: coin.Txid,
|
||||
TxIndex: coin.Vout,
|
||||
},
|
||||
uint(exitDelay),
|
||||
leafProof,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
assetID, err := elementsutil.AssetHashToBytes(coin.Asset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := elementsutil.ValueToBytes(coin.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
witnessUtxo := transaction.TxOutput{
|
||||
Asset: assetID,
|
||||
Value: value,
|
||||
Script: script,
|
||||
Nonce: []byte{0x00},
|
||||
}
|
||||
|
||||
if err := updater.AddInWitnessUtxo(len(updater.Pset.Inputs)-1, &witnessUtxo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -78,15 +78,17 @@ func connectToAsp(ctx *cli.Context, net, url string) error {
|
||||
}
|
||||
defer close()
|
||||
|
||||
resp, err := client.GetPubkey(ctx.Context, &arkv1.GetPubkeyRequest{})
|
||||
resp, err := client.GetInfo(ctx.Context, &arkv1.GetInfoRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return setState(map[string]string{
|
||||
"ark_url": url,
|
||||
"network": net,
|
||||
"ark_pubkey": resp.Pubkey,
|
||||
return setState(map[string]interface{}{
|
||||
"ark_url": url,
|
||||
"network": net,
|
||||
"ark_pubkey": resp.Pubkey,
|
||||
"ark_lifetime": resp.Lifetime,
|
||||
"exit_delay": resp.ExitDelay,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -114,7 +116,7 @@ func initWallet(ctx *cli.Context, key, password string) error {
|
||||
|
||||
passwordHash := hashPassword([]byte(password))
|
||||
|
||||
state := map[string]string{
|
||||
state := map[string]interface{}{
|
||||
"encrypted_private_key": hex.EncodeToString(encryptedPrivateKey),
|
||||
"password_hash": hex.EncodeToString(passwordHash),
|
||||
"public_key": hex.EncodeToString(privateKey.PubKey().SerializeCompressed()),
|
||||
|
||||
@@ -29,9 +29,10 @@ var (
|
||||
network.Testnet.Name: "https://blockstream.info/liquidtestnet/api",
|
||||
}
|
||||
|
||||
initialState = map[string]string{
|
||||
initialState = map[string]interface{}{
|
||||
"ark_url": "",
|
||||
"ark_pubkey": "",
|
||||
"ark_lifetime": 0,
|
||||
"encrypted_private_key": "",
|
||||
"password_hash": "",
|
||||
"public_key": "",
|
||||
@@ -109,7 +110,7 @@ func cleanAndExpandPath(path string) string {
|
||||
return filepath.Clean(os.ExpandEnv(path))
|
||||
}
|
||||
|
||||
func getState() (map[string]string, error) {
|
||||
func getState() (map[string]interface{}, error) {
|
||||
file, err := os.ReadFile(statePath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
@@ -121,7 +122,7 @@ func getState() (map[string]string, error) {
|
||||
return initialState, nil
|
||||
}
|
||||
|
||||
data := map[string]string{}
|
||||
data := map[string]interface{}{}
|
||||
if err := json.Unmarshal(file, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -137,7 +138,7 @@ func setInitialState() error {
|
||||
return os.WriteFile(statePath, jsonString, 0755)
|
||||
}
|
||||
|
||||
func setState(data map[string]string) error {
|
||||
func setState(data map[string]interface{}) error {
|
||||
currentData, err := getState()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -157,8 +158,8 @@ func setState(data map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func merge(maps ...map[string]string) map[string]string {
|
||||
merge := make(map[string]string, 0)
|
||||
func merge(maps ...map[string]interface{}) map[string]interface{} {
|
||||
merge := make(map[string]interface{}, 0)
|
||||
for _, m := range maps {
|
||||
for k, v := range m {
|
||||
merge[k] = v
|
||||
|
||||
@@ -15,15 +15,9 @@ func receiveAction(ctx *cli.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state, err := getState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relays := []string{state["ark_url"]}
|
||||
|
||||
return printJSON(map[string]interface{}{
|
||||
"offchain_address": offchainAddr,
|
||||
"onchain_address": onchainAddr,
|
||||
"relays": relays,
|
||||
})
|
||||
}
|
||||
|
||||
133
client/redeem.go
133
client/redeem.go
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -20,7 +18,7 @@ var (
|
||||
Name: "address",
|
||||
Usage: "main chain address receiving the redeeemed VTXO",
|
||||
Value: "",
|
||||
Required: true,
|
||||
Required: false,
|
||||
}
|
||||
|
||||
amountToRedeemFlag = cli.Uint64Flag{
|
||||
@@ -50,20 +48,9 @@ func redeemAction(ctx *cli.Context) error {
|
||||
amount := ctx.Uint64("amount")
|
||||
force := ctx.Bool("force")
|
||||
|
||||
if len(addr) <= 0 {
|
||||
if len(addr) <= 0 && !force {
|
||||
return fmt.Errorf("missing address flag (--address)")
|
||||
}
|
||||
if _, err := address.ToOutputScript(addr); err != nil {
|
||||
return fmt.Errorf("invalid onchain address")
|
||||
}
|
||||
net, err := address.NetworkForAddress(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid onchain address: unknown network")
|
||||
}
|
||||
_, liquidNet := getNetwork()
|
||||
if net.Name != liquidNet.Name {
|
||||
return fmt.Errorf("invalid onchain address: must be for %s network", liquidNet.Name)
|
||||
}
|
||||
|
||||
if !force && amount <= 0 {
|
||||
return fmt.Errorf("missing amount flag (--amount)")
|
||||
@@ -74,13 +61,26 @@ func redeemAction(ctx *cli.Context) error {
|
||||
fmt.Printf("WARNING: unilateral exit (--force) ignores --amount flag, it will redeem all your VTXOs\n")
|
||||
}
|
||||
|
||||
return unilateralRedeem(ctx, addr)
|
||||
return unilateralRedeem(ctx)
|
||||
}
|
||||
|
||||
return collaborativeRedeem(ctx, addr, amount)
|
||||
}
|
||||
|
||||
func collaborativeRedeem(ctx *cli.Context, addr string, amount uint64) error {
|
||||
if _, err := address.ToOutputScript(addr); err != nil {
|
||||
return fmt.Errorf("invalid onchain address")
|
||||
}
|
||||
|
||||
net, err := address.NetworkForAddress(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid onchain address: unknown network")
|
||||
}
|
||||
_, liquidNet := getNetwork()
|
||||
if net.Name != liquidNet.Name {
|
||||
return fmt.Errorf("invalid onchain address: must be for %s network", liquidNet.Name)
|
||||
}
|
||||
|
||||
if isConf, _ := address.IsConfidential(addr); isConf {
|
||||
info, _ := address.FromConfidential(addr)
|
||||
addr = info.Address
|
||||
@@ -173,12 +173,7 @@ func collaborativeRedeem(ctx *cli.Context, addr string, amount uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func unilateralRedeem(ctx *cli.Context, addr string) error {
|
||||
onchainScript, err := address.ToOutputScript(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func unilateralRedeem(ctx *cli.Context) error {
|
||||
client, close, err := getClientFromState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -202,20 +197,11 @@ func unilateralRedeem(ctx *cli.Context, addr string) error {
|
||||
totalVtxosAmount += vtxo.amount
|
||||
}
|
||||
|
||||
ok := askForConfirmation(fmt.Sprintf("redeem %d sats to %s ?", totalVtxosAmount, addr))
|
||||
ok := askForConfirmation(fmt.Sprintf("redeem %d sats ?", totalVtxosAmount))
|
||||
if !ok {
|
||||
return fmt.Errorf("aborting unilateral exit")
|
||||
}
|
||||
|
||||
finalPset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updater, err := psetv2.NewUpdater(finalPset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// transactionsMap avoid duplicates
|
||||
transactionsMap := make(map[string]struct{}, 0)
|
||||
transactions := make([]string, 0)
|
||||
@@ -226,10 +212,6 @@ func unilateralRedeem(ctx *cli.Context, addr string) error {
|
||||
}
|
||||
|
||||
for _, branch := range redeemBranches {
|
||||
if err := branch.AddVtxoInput(updater); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
branchTxs, err := branch.RedeemPath()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -243,48 +225,6 @@ func unilateralRedeem(ctx *cli.Context, addr string) error {
|
||||
}
|
||||
}
|
||||
|
||||
_, net := getNetwork()
|
||||
|
||||
outputs := []psetv2.OutputArgs{
|
||||
{
|
||||
Asset: net.AssetID,
|
||||
Amount: totalVtxosAmount,
|
||||
Script: onchainScript,
|
||||
},
|
||||
}
|
||||
|
||||
if err := updater.AddOutputs(outputs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utx, err := updater.Pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vBytes := utx.VirtualSize()
|
||||
feeAmount := uint64(math.Ceil(float64(vBytes) * 0.25))
|
||||
|
||||
if totalVtxosAmount-feeAmount <= 0 {
|
||||
return fmt.Errorf("not enough VTXOs to pay the fees (%d sats), aborting unilateral exit", feeAmount)
|
||||
}
|
||||
|
||||
updater.Pset.Outputs[0].Value = totalVtxosAmount - feeAmount
|
||||
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: net.AssetID,
|
||||
Amount: feeAmount,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prvKey, err := privateKeyFromPassword()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, txHex := range transactions {
|
||||
for {
|
||||
txid, err := explorer.Broadcast(txHex)
|
||||
@@ -303,43 +243,6 @@ func unilateralRedeem(ctx *cli.Context, addr string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := signPset(finalPset, explorer, prvKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, input := range finalPset.Inputs {
|
||||
if len(input.TapScriptSig) > 0 || len(input.PartialSigs) > 0 {
|
||||
if err := psetv2.Finalize(finalPset, i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signedTx, err := psetv2.Extract(finalPset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hex, err := signedTx.ToHex()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
id, err := explorer.Broadcast(hex)
|
||||
if err != nil {
|
||||
if strings.Contains(strings.ToLower(err.Error()), "bad-txns-inputs-missingorspent") {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if id != "" {
|
||||
fmt.Printf("(final) redeem tx %s\n", id)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
201
client/send.go
201
client/send.go
@@ -4,10 +4,13 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
type receiver struct {
|
||||
@@ -15,6 +18,11 @@ type receiver struct {
|
||||
Amount uint64 `json:"amount"`
|
||||
}
|
||||
|
||||
func (r *receiver) IsOnchain() bool {
|
||||
_, err := address.ToOutputScript(r.To)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
var (
|
||||
receiversFlag = cli.StringFlag{
|
||||
Name: "receivers",
|
||||
@@ -22,7 +30,7 @@ var (
|
||||
}
|
||||
toFlag = cli.StringFlag{
|
||||
Name: "to",
|
||||
Usage: "ark address of the recipient",
|
||||
Usage: "address of the recipient",
|
||||
}
|
||||
amountFlag = cli.Uint64Flag{
|
||||
Name: "amount",
|
||||
@@ -63,6 +71,33 @@ func sendAction(ctx *cli.Context) error {
|
||||
return fmt.Errorf("no receivers specified")
|
||||
}
|
||||
|
||||
onchainReceivers := make([]receiver, 0)
|
||||
offchainReceivers := make([]receiver, 0)
|
||||
|
||||
for _, receiver := range receiversJSON {
|
||||
if receiver.IsOnchain() {
|
||||
onchainReceivers = append(onchainReceivers, receiver)
|
||||
} else {
|
||||
offchainReceivers = append(offchainReceivers, receiver)
|
||||
}
|
||||
}
|
||||
|
||||
if len(onchainReceivers) > 0 {
|
||||
if err := sendOnchain(ctx, onchainReceivers); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(offchainReceivers) > 0 {
|
||||
if err := sendOffchain(ctx, offchainReceivers); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendOffchain(ctx *cli.Context, receivers []receiver) error {
|
||||
offchainAddr, _, err := getAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -76,7 +111,7 @@ func sendAction(ctx *cli.Context) error {
|
||||
receiversOutput := make([]*arkv1.Output, 0)
|
||||
sumOfReceivers := uint64(0)
|
||||
|
||||
for _, receiver := range receiversJSON {
|
||||
for _, receiver := range receivers {
|
||||
_, _, aspKey, err := common.DecodeAddress(receiver.To)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid receiver address: %s", err)
|
||||
@@ -163,11 +198,169 @@ func sendAction(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := printJSON(map[string]interface{}{
|
||||
return printJSON(map[string]interface{}{
|
||||
"pool_txid": poolTxID,
|
||||
})
|
||||
}
|
||||
|
||||
func sendOnchain(ctx *cli.Context, receivers []receiver) error {
|
||||
pset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updater, err := psetv2.NewUpdater(pset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, net := getNetwork()
|
||||
|
||||
targetAmount := uint64(0)
|
||||
for _, receiver := range receivers {
|
||||
targetAmount += receiver.Amount
|
||||
if receiver.Amount < DUST {
|
||||
return fmt.Errorf("invalid amount (%d), must be greater than dust %d", receiver.Amount, DUST)
|
||||
}
|
||||
|
||||
script, err := address.ToOutputScript(receiver.To)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: net.AssetID,
|
||||
Amount: receiver.Amount,
|
||||
Script: script,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
selected, delayedSelected, change, err := coinSelectOnchain(targetAmount, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := addInputs(updater, selected, delayedSelected, net); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if change > 0 {
|
||||
_, changeAddr, err := getAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changeScript, err := address.ToOutputScript(changeAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: net.AssetID,
|
||||
Amount: change,
|
||||
Script: changeScript,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
utx, err := updater.Pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vBytes := utx.VirtualSize()
|
||||
feeAmount := uint64(math.Ceil(float64(vBytes) * 0.5))
|
||||
|
||||
if change > feeAmount {
|
||||
updater.Pset.Outputs[len(updater.Pset.Outputs)-1].Value = change - feeAmount
|
||||
} else if change == feeAmount {
|
||||
updater.Pset.Outputs = updater.Pset.Outputs[:len(updater.Pset.Outputs)-1]
|
||||
} else { // change < feeAmount
|
||||
if change > 0 {
|
||||
updater.Pset.Outputs = updater.Pset.Outputs[:len(updater.Pset.Outputs)-1]
|
||||
}
|
||||
// reselect the difference
|
||||
selected, delayedSelected, newChange, err := coinSelectOnchain(
|
||||
feeAmount-change,
|
||||
append(selected, delayedSelected...),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := addInputs(updater, selected, delayedSelected, net); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if newChange > 0 {
|
||||
_, changeAddr, err := getAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changeScript, err := address.ToOutputScript(changeAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: net.AssetID,
|
||||
Amount: newChange,
|
||||
Script: changeScript,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: net.AssetID,
|
||||
Amount: feeAmount,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
prvKey, err := privateKeyFromPassword()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
explorer := NewExplorer()
|
||||
|
||||
if err := signPset(updater.Pset, explorer, prvKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := psetv2.FinalizeAll(updater.Pset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
extracted, err := psetv2.Extract(pset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hex, err := extracted.ToHex()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txid, err := explorer.Broadcast(hex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return printJSON(map[string]interface{}{
|
||||
"txid": txid,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -122,20 +122,29 @@ func signPset(
|
||||
continue
|
||||
}
|
||||
|
||||
pubkey := prvKey.PubKey()
|
||||
|
||||
vtxoLeaf, err := tree.VtxoScript(pubkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(input.TapLeafScript) > 0 {
|
||||
genesis, err := chainhash.NewHashFromStr(liquidNet.GenesisBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pubkey := prvKey.PubKey()
|
||||
for _, leaf := range input.TapLeafScript {
|
||||
if bytes.Equal(leaf.Script, vtxoLeaf.Script) {
|
||||
closure, err := tree.DecodeClosure(leaf.Script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sign := false
|
||||
|
||||
switch c := closure.(type) {
|
||||
case *tree.CSVSigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], pubkey.SerializeCompressed()[1:])
|
||||
case *tree.ForfeitClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], pubkey.SerializeCompressed()[1:])
|
||||
}
|
||||
|
||||
if sign {
|
||||
hash := leaf.TapHash()
|
||||
|
||||
preimage := utx.HashForWitnessV1(
|
||||
|
||||
@@ -15,8 +15,6 @@ import (
|
||||
type RedeemBranch interface {
|
||||
// RedeemPath returns the list of transactions to broadcast in order to access the vtxo output
|
||||
RedeemPath() ([]string, error)
|
||||
// AddInput adds the vtxo input created by the branch
|
||||
AddVtxoInput(updater *psetv2.Updater) error
|
||||
// ExpireAt returns the expiration time of the branch
|
||||
ExpireAt() (*time.Time, error)
|
||||
}
|
||||
@@ -92,89 +90,41 @@ func (r *redeemBranch) RedeemPath() ([]string, error) {
|
||||
}
|
||||
|
||||
for _, leaf := range input.TapLeafScript {
|
||||
isSweep, _, _, err := tree.DecodeSweepScript(leaf.Script)
|
||||
closure, err := tree.DecodeClosure(leaf.Script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isSweep {
|
||||
continue
|
||||
}
|
||||
switch closure.(type) {
|
||||
case *tree.UnrollClosure:
|
||||
controlBlock, err := leaf.ControlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
controlBlock, err := leaf.ControlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unsignedTx, err := pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unsignedTx, err := pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unsignedTx.Inputs[i].Witness = [][]byte{
|
||||
leaf.Script,
|
||||
controlBlock[:],
|
||||
}
|
||||
|
||||
unsignedTx.Inputs[i].Witness = [][]byte{
|
||||
leaf.Script,
|
||||
controlBlock[:],
|
||||
hex, err := unsignedTx.ToHex()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions = append(transactions, hex)
|
||||
}
|
||||
|
||||
hex, err := unsignedTx.ToHex()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions = append(transactions, hex)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
// AddVtxoInput is a wrapper around psetv2.Updater adding a taproot input letting to spend the vtxo output
|
||||
func (r *redeemBranch) AddVtxoInput(updater *psetv2.Updater) error {
|
||||
walletPubkey, err := getWalletPublicKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nextInputIndex := len(updater.Pset.Inputs)
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{
|
||||
{
|
||||
Txid: r.vtxo.txid,
|
||||
TxIndex: r.vtxo.vout,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add taproot tree letting to spend the vtxo
|
||||
checksigLeaf, err := tree.VtxoScript(walletPubkey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
vtxoTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
*checksigLeaf,
|
||||
*r.sweepClosure,
|
||||
)
|
||||
|
||||
proofIndex := vtxoTaprootTree.LeafProofIndex[checksigLeaf.TapHash()]
|
||||
|
||||
if err := updater.AddInTapLeafScript(
|
||||
nextInputIndex,
|
||||
psetv2.NewTapLeafScript(
|
||||
vtxoTaprootTree.LeafMerkleProofs[proofIndex],
|
||||
r.internalKey,
|
||||
),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *redeemBranch) ExpireAt() (*time.Time, error) {
|
||||
lastKnownBlocktime := int64(0)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package tree
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
@@ -17,9 +18,53 @@ const (
|
||||
OP_PUSHCURRENTINPUTINDEX = 0xcd
|
||||
)
|
||||
|
||||
// VtxoScript returns a simple checksig script for a given pubkey
|
||||
func VtxoScript(pubkey *secp256k1.PublicKey) (*taproot.TapElementsLeaf, error) {
|
||||
script, err := checksigScript(pubkey)
|
||||
type Closure interface {
|
||||
Leaf() (*taproot.TapElementsLeaf, error)
|
||||
Decode(script []byte) (bool, error)
|
||||
}
|
||||
|
||||
type UnrollClosure struct {
|
||||
LeftKey, RightKey *secp256k1.PublicKey
|
||||
LeftAmount, RightAmount uint64
|
||||
}
|
||||
|
||||
type CSVSigClosure struct {
|
||||
Pubkey *secp256k1.PublicKey
|
||||
Seconds uint
|
||||
}
|
||||
|
||||
type ForfeitClosure struct {
|
||||
Pubkey *secp256k1.PublicKey
|
||||
AspPubkey *secp256k1.PublicKey
|
||||
}
|
||||
|
||||
func DecodeClosure(script []byte) (Closure, error) {
|
||||
var closure Closure
|
||||
|
||||
closure = &UnrollClosure{}
|
||||
if valid, err := closure.Decode(script); err == nil && valid {
|
||||
return closure, nil
|
||||
}
|
||||
|
||||
closure = &CSVSigClosure{}
|
||||
if valid, err := closure.Decode(script); err == nil && valid {
|
||||
return closure, nil
|
||||
}
|
||||
|
||||
closure = &ForfeitClosure{}
|
||||
if valid, err := closure.Decode(script); err == nil && valid {
|
||||
return closure, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid closure script")
|
||||
|
||||
}
|
||||
|
||||
func (f *ForfeitClosure) Leaf() (*taproot.TapElementsLeaf, error) {
|
||||
aspKeyBytes := schnorr.SerializePubKey(f.AspPubkey)
|
||||
userKeyBytes := schnorr.SerializePubKey(f.Pubkey)
|
||||
|
||||
script, err := txscript.NewScriptBuilder().AddData(aspKeyBytes).AddOp(txscript.OP_CHECKSIGVERIFY).AddData(userKeyBytes).AddOp(txscript.OP_CHECKSIG).Script()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -28,67 +73,152 @@ func VtxoScript(pubkey *secp256k1.PublicKey) (*taproot.TapElementsLeaf, error) {
|
||||
return &tapLeaf, nil
|
||||
}
|
||||
|
||||
// SweepScript returns a taproot leaf letting the owner of the key to spend the output after a given timeDelta
|
||||
func SweepScript(sweepKey *secp256k1.PublicKey, seconds uint) (*taproot.TapElementsLeaf, error) {
|
||||
sweepScript, err := csvChecksigScript(sweepKey, seconds)
|
||||
func (f *ForfeitClosure) Decode(script []byte) (bool, error) {
|
||||
valid, aspPubKey, err := decodeChecksigScript(script)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
valid, pubkey, err := decodeChecksigScript(script[33:])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
f.Pubkey = pubkey
|
||||
f.AspPubkey = aspPubKey
|
||||
|
||||
rebuilt, err := f.Leaf()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rebuilt.Script, script) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (d *CSVSigClosure) Leaf() (*taproot.TapElementsLeaf, error) {
|
||||
script, err := csvChecksigScript(d.Pubkey, d.Seconds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tapLeaf := taproot.NewBaseTapElementsLeaf(sweepScript)
|
||||
tapLeaf := taproot.NewBaseTapElementsLeaf(script)
|
||||
return &tapLeaf, nil
|
||||
}
|
||||
|
||||
// BranchScript returns a taproot leaf that will split the coin in two outputs
|
||||
// each output (left and right) will have the given amount and the given taproot key as witness program
|
||||
func BranchScript(
|
||||
leftKey, rightKey *secp256k1.PublicKey, leftAmount, rightAmount uint64,
|
||||
) taproot.TapElementsLeaf {
|
||||
nextScriptLeft := withOutput(txscript.OP_0, schnorr.SerializePubKey(leftKey), leftAmount, rightKey != nil)
|
||||
branchScript := append([]byte{}, nextScriptLeft...)
|
||||
if rightKey != nil {
|
||||
nextScriptRight := withOutput(txscript.OP_1, schnorr.SerializePubKey(rightKey), rightAmount, false)
|
||||
branchScript = append(branchScript, nextScriptRight...)
|
||||
func (d *CSVSigClosure) Decode(script []byte) (bool, error) {
|
||||
csvIndex := bytes.Index(script, []byte{txscript.OP_CHECKSEQUENCEVERIFY, txscript.OP_DROP})
|
||||
if csvIndex == -1 || csvIndex == 0 {
|
||||
return false, nil
|
||||
}
|
||||
return taproot.NewBaseTapElementsLeaf(branchScript)
|
||||
|
||||
sequence := script[1:csvIndex]
|
||||
|
||||
seconds, err := common.BIP68Decode(sequence)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
checksigScript := script[csvIndex+2:]
|
||||
valid, pubkey, err := decodeChecksigScript(checksigScript)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
rebuilt, err := csvChecksigScript(pubkey, seconds)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rebuilt, script) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
d.Pubkey = pubkey
|
||||
d.Seconds = seconds
|
||||
|
||||
return valid, nil
|
||||
}
|
||||
|
||||
func decodeBranchScript(script []byte) (valid bool, leftKey, rightKey *secp256k1.PublicKey, leftAmount, rightAmount uint64, err error) {
|
||||
func (c *UnrollClosure) Leaf() (*taproot.TapElementsLeaf, error) {
|
||||
if c.LeftKey == nil || c.LeftAmount == 0 {
|
||||
return nil, fmt.Errorf("left key and amount are required")
|
||||
}
|
||||
|
||||
nextScriptLeft := withOutput(txscript.OP_0, schnorr.SerializePubKey(c.LeftKey), c.LeftAmount, c.RightKey != nil)
|
||||
branchScript := append([]byte{}, nextScriptLeft...)
|
||||
if c.RightKey != nil {
|
||||
if c.RightAmount == 0 {
|
||||
return nil, fmt.Errorf("right amount is required")
|
||||
}
|
||||
|
||||
nextScriptRight := withOutput(txscript.OP_1, schnorr.SerializePubKey(c.RightKey), c.RightAmount, false)
|
||||
branchScript = append(branchScript, nextScriptRight...)
|
||||
}
|
||||
leaf := taproot.NewBaseTapElementsLeaf(branchScript)
|
||||
return &leaf, nil
|
||||
}
|
||||
|
||||
func (c *UnrollClosure) Decode(script []byte) (valid bool, err error) {
|
||||
if len(script) != 52 && len(script) != 104 {
|
||||
return false, nil, nil, 0, 0, nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isLeftOnly := len(script) == 52
|
||||
|
||||
validLeft, leftKey, leftAmount, err := decodeWithOutputScript(script[:52], txscript.OP_0, !isLeftOnly)
|
||||
if err != nil {
|
||||
return false, nil, nil, 0, 0, err
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !validLeft {
|
||||
return false, nil, nil, 0, 0, nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
c.LeftAmount = leftAmount
|
||||
c.LeftKey = leftKey
|
||||
|
||||
if isLeftOnly {
|
||||
return true, leftKey, nil, leftAmount, 0, nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
validRight, rightKey, rightAmount, err := decodeWithOutputScript(script[52:], txscript.OP_1, false)
|
||||
if err != nil {
|
||||
return false, nil, nil, 0, 0, err
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !validRight {
|
||||
return false, nil, nil, 0, 0, nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
rebuilt := BranchScript(leftKey, rightKey, leftAmount, rightAmount)
|
||||
c.RightAmount = rightAmount
|
||||
c.RightKey = rightKey
|
||||
|
||||
rebuilt, err := c.Leaf()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rebuilt.Script, script) {
|
||||
return false, nil, nil, 0, 0, nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, leftKey, rightKey, leftAmount, rightAmount, nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func decodeWithOutputScript(script []byte, expectedIndex byte, isVerify bool) (valid bool, pubkey *secp256k1.PublicKey, amount uint64, err error) {
|
||||
@@ -139,53 +269,9 @@ func decodeChecksigScript(script []byte) (valid bool, pubkey *secp256k1.PublicKe
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
rebuilt, err := checksigScript(pubkey)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rebuilt, script) {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
return true, pubkey, nil
|
||||
}
|
||||
|
||||
func DecodeSweepScript(script []byte) (valid bool, aspPubKey *secp256k1.PublicKey, seconds uint, err error) {
|
||||
csvIndex := bytes.Index(script, []byte{txscript.OP_CHECKSEQUENCEVERIFY, txscript.OP_DROP})
|
||||
if csvIndex == -1 || csvIndex == 0 {
|
||||
return false, nil, 0, nil
|
||||
}
|
||||
|
||||
sequence := script[1:csvIndex]
|
||||
|
||||
seconds, err = common.BIP68Decode(sequence)
|
||||
if err != nil {
|
||||
return false, nil, 0, err
|
||||
}
|
||||
|
||||
checksigScript := script[csvIndex+2:]
|
||||
valid, aspPubKey, err = decodeChecksigScript(checksigScript)
|
||||
if err != nil {
|
||||
return false, nil, 0, err
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return false, nil, 0, nil
|
||||
}
|
||||
|
||||
rebuilt, err := csvChecksigScript(aspPubKey, seconds)
|
||||
if err != nil {
|
||||
return false, nil, 0, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rebuilt, script) {
|
||||
return false, nil, 0, nil
|
||||
}
|
||||
|
||||
return valid, aspPubKey, seconds, nil
|
||||
}
|
||||
|
||||
// checkSequenceVerifyScript without checksig
|
||||
func checkSequenceVerifyScript(seconds uint) ([]byte, error) {
|
||||
sequence, err := common.BIP68Encode(seconds)
|
||||
|
||||
@@ -229,35 +229,33 @@ func validateNodeTransaction(
|
||||
return ErrInvalidTaprootScript
|
||||
}
|
||||
|
||||
isSweepLeaf, aspKey, seconds, err := DecodeSweepScript(tapLeaf.Script)
|
||||
close, err := DecodeClosure(tapLeaf.Script)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid sweep script: %w", err)
|
||||
}
|
||||
|
||||
if isSweepLeaf {
|
||||
if !aspKey.IsEqual(aspKey) {
|
||||
return ErrInvalidASP
|
||||
}
|
||||
|
||||
if int64(seconds) != expectedSequenceSeconds {
|
||||
return ErrInvalidSweepSequence
|
||||
}
|
||||
|
||||
sweepLeafFound = true
|
||||
continue
|
||||
}
|
||||
|
||||
isBranchLeaf, leftKey, rightKey, leftAmount, rightAmount, err := decodeBranchScript(tapLeaf.Script)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid branch script: %w", err)
|
||||
}
|
||||
switch c := close.(type) {
|
||||
case *CSVSigClosure:
|
||||
isASP := c.Pubkey.IsEqual(expectedPublicKeyASP)
|
||||
isSweepDelay := int64(c.Seconds) == expectedSequenceSeconds
|
||||
|
||||
if isBranchLeaf {
|
||||
if isASP && !isSweepDelay {
|
||||
return ErrInvalidSweepSequence
|
||||
}
|
||||
|
||||
if isSweepDelay && !isASP {
|
||||
return ErrInvalidASP
|
||||
}
|
||||
|
||||
if isASP && isSweepDelay {
|
||||
sweepLeafFound = true
|
||||
}
|
||||
case *UnrollClosure:
|
||||
branchLeafFound = true
|
||||
|
||||
// check outputs
|
||||
nbOuts := len(childTx.Outputs)
|
||||
if leftKey != nil && rightKey != nil {
|
||||
if c.LeftKey != nil && c.RightKey != nil {
|
||||
if nbOuts != 3 {
|
||||
return ErrNumberOfOutputs
|
||||
}
|
||||
@@ -270,26 +268,29 @@ func validateNodeTransaction(
|
||||
leftWitnessProgram := childTx.Outputs[0].Script[2:]
|
||||
leftOutputAmount := childTx.Outputs[0].Value
|
||||
|
||||
if !bytes.Equal(leftWitnessProgram, schnorr.SerializePubKey(leftKey)) {
|
||||
if !bytes.Equal(leftWitnessProgram, schnorr.SerializePubKey(c.LeftKey)) {
|
||||
return ErrInvalidLeftOutput
|
||||
}
|
||||
|
||||
if leftAmount != leftOutputAmount {
|
||||
if c.LeftAmount != leftOutputAmount {
|
||||
return ErrInvalidLeftOutput
|
||||
}
|
||||
|
||||
if rightKey != nil {
|
||||
if c.RightKey != nil {
|
||||
rightWitnessProgram := childTx.Outputs[1].Script[2:]
|
||||
rightOutputAmount := childTx.Outputs[1].Value
|
||||
|
||||
if !bytes.Equal(rightWitnessProgram, schnorr.SerializePubKey(rightKey)) {
|
||||
if !bytes.Equal(rightWitnessProgram, schnorr.SerializePubKey(c.RightKey)) {
|
||||
return ErrInvalidRightOutput
|
||||
}
|
||||
|
||||
if rightAmount != rightOutputAmount {
|
||||
if c.RightAmount != rightOutputAmount {
|
||||
return ErrInvalidRightOutput
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,28 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/info": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetInfo",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1GetInfoResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/claim": {
|
||||
"post": {
|
||||
"operationId": "ArkService_ClaimPayment",
|
||||
@@ -203,28 +225,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/pubkey": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetPubkey",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1GetPubkeyResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/round/{txid}": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetRound",
|
||||
@@ -367,11 +367,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1GetPubkeyResponse": {
|
||||
"v1GetInfoResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pubkey": {
|
||||
"type": "string"
|
||||
},
|
||||
"lifetime": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"exitDelay": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -48,9 +48,9 @@ service ArkService {
|
||||
get: "/v1/vtxos/{address}"
|
||||
};
|
||||
}
|
||||
rpc GetPubkey(GetPubkeyRequest) returns (GetPubkeyResponse) {
|
||||
rpc GetInfo(GetInfoRequest) returns (GetInfoResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/pubkey"
|
||||
get: "/v1/info"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -172,8 +172,10 @@ message Vtxo {
|
||||
string pool_txid = 4;
|
||||
}
|
||||
|
||||
message GetPubkeyRequest {}
|
||||
message GetInfoRequest {}
|
||||
|
||||
message GetPubkeyResponse {
|
||||
message GetInfoResponse {
|
||||
string pubkey = 1;
|
||||
int64 lifetime = 2;
|
||||
int64 exit_delay = 3;
|
||||
}
|
||||
@@ -1388,14 +1388,14 @@ func (x *Vtxo) GetPoolTxid() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetPubkeyRequest struct {
|
||||
type GetInfoRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *GetPubkeyRequest) Reset() {
|
||||
*x = GetPubkeyRequest{}
|
||||
func (x *GetInfoRequest) Reset() {
|
||||
*x = GetInfoRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ark_v1_service_proto_msgTypes[26]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -1403,13 +1403,13 @@ func (x *GetPubkeyRequest) Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetPubkeyRequest) String() string {
|
||||
func (x *GetInfoRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetPubkeyRequest) ProtoMessage() {}
|
||||
func (*GetInfoRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetPubkeyRequest) ProtoReflect() protoreflect.Message {
|
||||
func (x *GetInfoRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ark_v1_service_proto_msgTypes[26]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -1421,21 +1421,23 @@ func (x *GetPubkeyRequest) ProtoReflect() protoreflect.Message {
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetPubkeyRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetPubkeyRequest) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use GetInfoRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetInfoRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ark_v1_service_proto_rawDescGZIP(), []int{26}
|
||||
}
|
||||
|
||||
type GetPubkeyResponse struct {
|
||||
type GetInfoResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Pubkey string `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
|
||||
Pubkey string `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
|
||||
Lifetime int64 `protobuf:"varint,2,opt,name=lifetime,proto3" json:"lifetime,omitempty"`
|
||||
ExitDelay int64 `protobuf:"varint,3,opt,name=exit_delay,json=exitDelay,proto3" json:"exit_delay,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetPubkeyResponse) Reset() {
|
||||
*x = GetPubkeyResponse{}
|
||||
func (x *GetInfoResponse) Reset() {
|
||||
*x = GetInfoResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ark_v1_service_proto_msgTypes[27]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -1443,13 +1445,13 @@ func (x *GetPubkeyResponse) Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetPubkeyResponse) String() string {
|
||||
func (x *GetInfoResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetPubkeyResponse) ProtoMessage() {}
|
||||
func (*GetInfoResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetPubkeyResponse) ProtoReflect() protoreflect.Message {
|
||||
func (x *GetInfoResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ark_v1_service_proto_msgTypes[27]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -1461,18 +1463,32 @@ func (x *GetPubkeyResponse) ProtoReflect() protoreflect.Message {
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetPubkeyResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetPubkeyResponse) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use GetInfoResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetInfoResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ark_v1_service_proto_rawDescGZIP(), []int{27}
|
||||
}
|
||||
|
||||
func (x *GetPubkeyResponse) GetPubkey() string {
|
||||
func (x *GetInfoResponse) GetPubkey() string {
|
||||
if x != nil {
|
||||
return x.Pubkey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetInfoResponse) GetLifetime() int64 {
|
||||
if x != nil {
|
||||
return x.Lifetime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GetInfoResponse) GetExitDelay() int64 {
|
||||
if x != nil {
|
||||
return x.ExitDelay
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_ark_v1_service_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_ark_v1_service_proto_rawDesc = []byte{
|
||||
@@ -1590,77 +1606,80 @@ var file_ark_v1_service_proto_rawDesc = []byte{
|
||||
0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x6e, 0x74,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x70, 0x6f, 0x6f, 0x6c, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x78, 0x69, 0x64, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65,
|
||||
0x74, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2b,
|
||||
0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x32, 0xfd, 0x06, 0x0a, 0x0a,
|
||||
0x41, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x0f, 0x52, 0x65,
|
||||
0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e,
|
||||
0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50,
|
||||
0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e,
|
||||
0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50,
|
||||
0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f,
|
||||
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70,
|
||||
0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12,
|
||||
0x67, 0x0a, 0x0c, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12,
|
||||
0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x61,
|
||||
0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61,
|
||||
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x73, 0x0a, 0x0f, 0x46, 0x69, 0x6e, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72,
|
||||
0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x50, 0x61, 0x79,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72,
|
||||
0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x50, 0x61, 0x79,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3,
|
||||
0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x57, 0x0a,
|
||||
0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3,
|
||||
0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f,
|
||||
0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x65, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65,
|
||||
0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12,
|
||||
0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, 0x12, 0x50, 0x0a,
|
||||
0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50,
|
||||
0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x61, 0x72, 0x6b,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x69,
|
||||
0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12,
|
||||
0x55, 0x0a, 0x06, 0x46, 0x61, 0x75, 0x63, 0x65, 0x74, 0x12, 0x15, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x46, 0x61, 0x75, 0x63, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x75, 0x63, 0x65, 0x74,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16,
|
||||
0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x61, 0x75, 0x63, 0x65, 0x74, 0x2f, 0x7b, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74,
|
||||
0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e,
|
||||
0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15,
|
||||
0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x54, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50,
|
||||
0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61,
|
||||
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12,
|
||||
0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x42, 0x92, 0x01, 0x0a, 0x0a,
|
||||
0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b,
|
||||
0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa,
|
||||
0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56,
|
||||
0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x52, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x78, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65,
|
||||
0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x64, 0x0a, 0x0f,
|
||||
0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x69, 0x66, 0x65, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x69, 0x66, 0x65, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x61,
|
||||
0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x65, 0x78, 0x69, 0x74, 0x44, 0x65, 0x6c,
|
||||
0x61, 0x79, 0x32, 0xf5, 0x06, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x12, 0x73, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x61, 0x79,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65,
|
||||
0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65,
|
||||
0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a,
|
||||
0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x72, 0x65,
|
||||
0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x67, 0x0a, 0x0c, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x50,
|
||||
0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x43, 0x6c, 0x61, 0x69, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x61,
|
||||
0x69, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76,
|
||||
0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12,
|
||||
0x73, 0x0a, 0x0f, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14,
|
||||
0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x66, 0x69, 0x6e, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64,
|
||||
0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x65, 0x0a,
|
||||
0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12,
|
||||
0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e,
|
||||
0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e,
|
||||
0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74,
|
||||
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12,
|
||||
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e,
|
||||
0x74, 0x73, 0x30, 0x01, 0x12, 0x50, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61,
|
||||
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x14, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12,
|
||||
0x15, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x55, 0x0a, 0x06, 0x46, 0x61, 0x75, 0x63, 0x65, 0x74,
|
||||
0x12, 0x15, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x75, 0x63, 0x65, 0x74,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x46, 0x61, 0x75, 0x63, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x61, 0x75,
|
||||
0x63, 0x65, 0x74, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x5d, 0x0a,
|
||||
0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78,
|
||||
0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x4c, 0x0a, 0x07,
|
||||
0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a,
|
||||
0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x92, 0x01, 0x0a, 0x0a, 0x63,
|
||||
0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69,
|
||||
0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||
0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b, 0x2f,
|
||||
0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02,
|
||||
0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31,
|
||||
0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74,
|
||||
0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1703,8 +1722,8 @@ var file_ark_v1_service_proto_goTypes = []interface{}{
|
||||
(*ListVtxosRequest)(nil), // 23: ark.v1.ListVtxosRequest
|
||||
(*ListVtxosResponse)(nil), // 24: ark.v1.ListVtxosResponse
|
||||
(*Vtxo)(nil), // 25: ark.v1.Vtxo
|
||||
(*GetPubkeyRequest)(nil), // 26: ark.v1.GetPubkeyRequest
|
||||
(*GetPubkeyResponse)(nil), // 27: ark.v1.GetPubkeyResponse
|
||||
(*GetInfoRequest)(nil), // 26: ark.v1.GetInfoRequest
|
||||
(*GetInfoResponse)(nil), // 27: ark.v1.GetInfoResponse
|
||||
}
|
||||
var file_ark_v1_service_proto_depIdxs = []int32{
|
||||
9, // 0: ark.v1.RegisterPaymentRequest.inputs:type_name -> ark.v1.Input
|
||||
@@ -1728,7 +1747,7 @@ var file_ark_v1_service_proto_depIdxs = []int32{
|
||||
19, // 18: ark.v1.ArkService.Ping:input_type -> ark.v1.PingRequest
|
||||
21, // 19: ark.v1.ArkService.Faucet:input_type -> ark.v1.FaucetRequest
|
||||
23, // 20: ark.v1.ArkService.ListVtxos:input_type -> ark.v1.ListVtxosRequest
|
||||
26, // 21: ark.v1.ArkService.GetPubkey:input_type -> ark.v1.GetPubkeyRequest
|
||||
26, // 21: ark.v1.ArkService.GetInfo:input_type -> ark.v1.GetInfoRequest
|
||||
1, // 22: ark.v1.ArkService.RegisterPayment:output_type -> ark.v1.RegisterPaymentResponse
|
||||
3, // 23: ark.v1.ArkService.ClaimPayment:output_type -> ark.v1.ClaimPaymentResponse
|
||||
5, // 24: ark.v1.ArkService.FinalizePayment:output_type -> ark.v1.FinalizePaymentResponse
|
||||
@@ -1737,7 +1756,7 @@ var file_ark_v1_service_proto_depIdxs = []int32{
|
||||
20, // 27: ark.v1.ArkService.Ping:output_type -> ark.v1.PingResponse
|
||||
22, // 28: ark.v1.ArkService.Faucet:output_type -> ark.v1.FaucetResponse
|
||||
24, // 29: ark.v1.ArkService.ListVtxos:output_type -> ark.v1.ListVtxosResponse
|
||||
27, // 30: ark.v1.ArkService.GetPubkey:output_type -> ark.v1.GetPubkeyResponse
|
||||
27, // 30: ark.v1.ArkService.GetInfo:output_type -> ark.v1.GetInfoResponse
|
||||
22, // [22:31] is the sub-list for method output_type
|
||||
13, // [13:22] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
@@ -2064,7 +2083,7 @@ func file_ark_v1_service_proto_init() {
|
||||
}
|
||||
}
|
||||
file_ark_v1_service_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetPubkeyRequest); i {
|
||||
switch v := v.(*GetInfoRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -2076,7 +2095,7 @@ func file_ark_v1_service_proto_init() {
|
||||
}
|
||||
}
|
||||
file_ark_v1_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetPubkeyResponse); i {
|
||||
switch v := v.(*GetInfoResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
||||
@@ -334,20 +334,20 @@ func local_request_ArkService_ListVtxos_0(ctx context.Context, marshaler runtime
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_GetPubkey_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetPubkeyRequest
|
||||
func request_ArkService_GetInfo_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetInfoRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := client.GetPubkey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
msg, err := client.GetInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_GetPubkey_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetPubkeyRequest
|
||||
func local_request_ArkService_GetInfo_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetInfoRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := server.GetPubkey(ctx, &protoReq)
|
||||
msg, err := server.GetInfo(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
@@ -540,7 +540,7 @@ func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_GetPubkey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle("GET", pattern_ArkService_GetInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
@@ -548,12 +548,12 @@ func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/GetPubkey", runtime.WithHTTPPathPattern("/v1/pubkey"))
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/GetInfo", runtime.WithHTTPPathPattern("/v1/info"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_GetPubkey_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
resp, md, err := local_request_ArkService_GetInfo_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
@@ -561,7 +561,7 @@ func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_GetPubkey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_ArkService_GetInfo_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
@@ -782,25 +782,25 @@ func RegisterArkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux,
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_GetPubkey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
mux.Handle("GET", pattern_ArkService_GetInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/GetPubkey", runtime.WithHTTPPathPattern("/v1/pubkey"))
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/GetInfo", runtime.WithHTTPPathPattern("/v1/info"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_GetPubkey_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
resp, md, err := request_ArkService_GetInfo_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_GetPubkey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
forward_ArkService_GetInfo_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
@@ -824,7 +824,7 @@ var (
|
||||
|
||||
pattern_ArkService_ListVtxos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "vtxos", "address"}, ""))
|
||||
|
||||
pattern_ArkService_GetPubkey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "pubkey"}, ""))
|
||||
pattern_ArkService_GetInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "info"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -844,5 +844,5 @@ var (
|
||||
|
||||
forward_ArkService_ListVtxos_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_GetPubkey_0 = runtime.ForwardResponseMessage
|
||||
forward_ArkService_GetInfo_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ type ArkServiceClient interface {
|
||||
Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error)
|
||||
Faucet(ctx context.Context, in *FaucetRequest, opts ...grpc.CallOption) (*FaucetResponse, error)
|
||||
ListVtxos(ctx context.Context, in *ListVtxosRequest, opts ...grpc.CallOption) (*ListVtxosResponse, error)
|
||||
GetPubkey(ctx context.Context, in *GetPubkeyRequest, opts ...grpc.CallOption) (*GetPubkeyResponse, error)
|
||||
GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error)
|
||||
}
|
||||
|
||||
type arkServiceClient struct {
|
||||
@@ -132,9 +132,9 @@ func (c *arkServiceClient) ListVtxos(ctx context.Context, in *ListVtxosRequest,
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) GetPubkey(ctx context.Context, in *GetPubkeyRequest, opts ...grpc.CallOption) (*GetPubkeyResponse, error) {
|
||||
out := new(GetPubkeyResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/GetPubkey", in, out, opts...)
|
||||
func (c *arkServiceClient) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) {
|
||||
out := new(GetInfoResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/GetInfo", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -153,7 +153,7 @@ type ArkServiceServer interface {
|
||||
Ping(context.Context, *PingRequest) (*PingResponse, error)
|
||||
Faucet(context.Context, *FaucetRequest) (*FaucetResponse, error)
|
||||
ListVtxos(context.Context, *ListVtxosRequest) (*ListVtxosResponse, error)
|
||||
GetPubkey(context.Context, *GetPubkeyRequest) (*GetPubkeyResponse, error)
|
||||
GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedArkServiceServer should be embedded to have forward compatible implementations.
|
||||
@@ -184,8 +184,8 @@ func (UnimplementedArkServiceServer) Faucet(context.Context, *FaucetRequest) (*F
|
||||
func (UnimplementedArkServiceServer) ListVtxos(context.Context, *ListVtxosRequest) (*ListVtxosResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListVtxos not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) GetPubkey(context.Context, *GetPubkeyRequest) (*GetPubkeyResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetPubkey not implemented")
|
||||
func (UnimplementedArkServiceServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
|
||||
}
|
||||
|
||||
// UnsafeArkServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
@@ -346,20 +346,20 @@ func _ArkService_ListVtxos_Handler(srv interface{}, ctx context.Context, dec fun
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_GetPubkey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetPubkeyRequest)
|
||||
func _ArkService_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetInfoRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).GetPubkey(ctx, in)
|
||||
return srv.(ArkServiceServer).GetInfo(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/GetPubkey",
|
||||
FullMethod: "/ark.v1.ArkService/GetInfo",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).GetPubkey(ctx, req.(*GetPubkeyRequest))
|
||||
return srv.(ArkServiceServer).GetInfo(ctx, req.(*GetInfoRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
@@ -400,8 +400,8 @@ var ArkService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _ArkService_ListVtxos_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetPubkey",
|
||||
Handler: _ArkService_GetPubkey_Handler,
|
||||
MethodName: "GetInfo",
|
||||
Handler: _ArkService_GetInfo_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
|
||||
@@ -37,6 +37,12 @@ func main() {
|
||||
log.Infof("round lifetime must be a multiple of 512, %d -> %d", setLifetime, cfg.RoundLifetime)
|
||||
}
|
||||
|
||||
if cfg.ExitDelay%512 != 0 {
|
||||
setExitDelay := cfg.ExitDelay
|
||||
cfg.ExitDelay = cfg.ExitDelay - (cfg.ExitDelay % 512)
|
||||
log.Infof("exit delay must be a multiple of 512, %d -> %d", setExitDelay, cfg.ExitDelay)
|
||||
}
|
||||
|
||||
appConfig := &appconfig.Config{
|
||||
DbType: cfg.DbType,
|
||||
DbDir: cfg.DbDir,
|
||||
@@ -48,6 +54,7 @@ func main() {
|
||||
WalletAddr: cfg.WalletAddr,
|
||||
MinRelayFee: cfg.MinRelayFee,
|
||||
RoundLifetime: cfg.RoundLifetime,
|
||||
ExitDelay: cfg.ExitDelay,
|
||||
}
|
||||
svc, err := grpcservice.NewService(svcConfig, appConfig)
|
||||
if err != nil {
|
||||
|
||||
@@ -43,6 +43,7 @@ type Config struct {
|
||||
WalletAddr string
|
||||
MinRelayFee uint64
|
||||
RoundLifetime int64
|
||||
ExitDelay int64
|
||||
|
||||
repo ports.RepoManager
|
||||
svc application.Service
|
||||
@@ -96,8 +97,8 @@ func (c *Config) Validate() error {
|
||||
return err
|
||||
}
|
||||
// round life time must be a multiple of 512
|
||||
if c.RoundLifetime <= 0 || c.RoundLifetime%512 != 0 {
|
||||
return fmt.Errorf("invalid round lifetime, must be greater than 0 and a multiple of 512")
|
||||
if c.RoundLifetime < 512 || c.RoundLifetime%512 != 0 {
|
||||
return fmt.Errorf("invalid round lifetime, must be greater or equal than 512 and a multiple of 512")
|
||||
}
|
||||
seq, err := common.BIP68Encode(uint(c.RoundLifetime))
|
||||
if err != nil {
|
||||
@@ -113,6 +114,10 @@ func (c *Config) Validate() error {
|
||||
return fmt.Errorf("invalid round lifetime, must be a multiple of 512")
|
||||
}
|
||||
|
||||
if c.ExitDelay < 512 || c.ExitDelay%512 != 0 {
|
||||
return fmt.Errorf("invalid exit delay, must be greater or equal than 512 and a multiple of 512")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -165,7 +170,7 @@ func (c *Config) txBuilderService() error {
|
||||
case "dummy":
|
||||
svc = txbuilderdummy.NewTxBuilder(c.wallet, net)
|
||||
case "covenant":
|
||||
svc = txbuilder.NewTxBuilder(c.wallet, net, c.RoundLifetime)
|
||||
svc = txbuilder.NewTxBuilder(c.wallet, net, c.RoundLifetime, c.ExitDelay)
|
||||
default:
|
||||
err = fmt.Errorf("unknown tx builder type")
|
||||
}
|
||||
@@ -214,7 +219,7 @@ func (c *Config) schedulerService() error {
|
||||
func (c *Config) appService() error {
|
||||
net := c.mainChain()
|
||||
svc, err := application.NewService(
|
||||
c.Network, net, c.RoundInterval, c.RoundLifetime, c.MinRelayFee,
|
||||
c.Network, net, c.RoundInterval, c.RoundLifetime, c.ExitDelay, c.MinRelayFee,
|
||||
c.wallet, c.repo, c.txBuilder, c.scanner, c.scheduler,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -24,6 +24,7 @@ type Config struct {
|
||||
LogLevel int
|
||||
MinRelayFee uint64
|
||||
RoundLifetime int64
|
||||
ExitDelay int64
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -40,6 +41,7 @@ var (
|
||||
Network = "NETWORK"
|
||||
MinRelayFee = "MIN_RELAY_FEE"
|
||||
RoundLifetime = "ROUND_LIFETIME"
|
||||
ExitDelay = "EXIT_DELAY"
|
||||
|
||||
defaultDatadir = common.AppDataDir("arkd", false)
|
||||
defaultRoundInterval = 10
|
||||
@@ -53,6 +55,7 @@ var (
|
||||
defaultLogLevel = 5
|
||||
defaultMinRelayFee = 30
|
||||
defaultRoundLifetime = 512
|
||||
defaultExitDelay = 512
|
||||
)
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
@@ -71,6 +74,7 @@ func LoadConfig() (*Config, error) {
|
||||
viper.SetDefault(Network, defaultNetwork)
|
||||
viper.SetDefault(RoundLifetime, defaultRoundLifetime)
|
||||
viper.SetDefault(MinRelayFee, defaultMinRelayFee)
|
||||
viper.SetDefault(ExitDelay, defaultExitDelay)
|
||||
|
||||
net, err := getNetwork()
|
||||
if err != nil {
|
||||
@@ -95,6 +99,7 @@ func LoadConfig() (*Config, error) {
|
||||
Network: net,
|
||||
MinRelayFee: viper.GetUint64(MinRelayFee),
|
||||
RoundLifetime: viper.GetInt64(RoundLifetime),
|
||||
ExitDelay: viper.GetInt64(ExitDelay),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ type Service interface {
|
||||
GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent
|
||||
UpdatePaymentStatus(ctx context.Context, id string) error
|
||||
ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, error)
|
||||
GetPubkey(ctx context.Context) (string, error)
|
||||
GetInfo(ctx context.Context) (string, int64, int64, error)
|
||||
}
|
||||
|
||||
type service struct {
|
||||
@@ -47,6 +47,7 @@ type service struct {
|
||||
roundLifetime int64
|
||||
roundInterval int64
|
||||
minRelayFee uint64
|
||||
exitDelay int64
|
||||
|
||||
wallet ports.WalletService
|
||||
repoManager ports.RepoManager
|
||||
@@ -62,7 +63,7 @@ type service struct {
|
||||
|
||||
func NewService(
|
||||
network common.Network, onchainNetwork network.Network,
|
||||
roundInterval, roundLifetime int64, minRelayFee uint64,
|
||||
roundInterval, roundLifetime int64, exitDelay int64, minRelayFee uint64,
|
||||
walletSvc ports.WalletService, repoManager ports.RepoManager,
|
||||
builder ports.TxBuilder, scanner ports.BlockchainScanner,
|
||||
scheduler ports.SchedulerService,
|
||||
@@ -81,7 +82,7 @@ func NewService(
|
||||
|
||||
svc := &service{
|
||||
network, onchainNetwork, pubkey,
|
||||
roundLifetime, roundInterval, minRelayFee,
|
||||
roundLifetime, roundInterval, minRelayFee, exitDelay,
|
||||
walletSvc, repoManager, builder, scanner, sweeper,
|
||||
paymentRequests, forfeitTxs, eventsCh,
|
||||
}
|
||||
@@ -218,8 +219,8 @@ func (s *service) GetRoundByTxid(ctx context.Context, poolTxid string) (*domain.
|
||||
return s.repoManager.Rounds().GetRoundWithTxid(ctx, poolTxid)
|
||||
}
|
||||
|
||||
func (s *service) GetPubkey(ctx context.Context) (string, error) {
|
||||
return hex.EncodeToString(s.pubkey.SerializeCompressed()), nil
|
||||
func (s *service) GetInfo(ctx context.Context) (string, int64, int64, error) {
|
||||
return hex.EncodeToString(s.pubkey.SerializeCompressed()), s.roundLifetime, s.exitDelay, nil
|
||||
}
|
||||
|
||||
func (s *service) start() {
|
||||
|
||||
@@ -2,7 +2,6 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -10,7 +9,6 @@ import (
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
@@ -326,17 +324,6 @@ func (s *sweeper) findSweepableOutputs(
|
||||
newNodesToCheck = append(newNodesToCheck, children...)
|
||||
continue
|
||||
}
|
||||
|
||||
// if the node is a leaf, the vtxos outputs should added as onchain outputs if they are not swept yet
|
||||
vtxoExpiration, sweepInput, err := s.leafToSweepInput(ctx, blocktime, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sweepInput != nil {
|
||||
expirationTime = vtxoExpiration
|
||||
sweepInputs = []ports.SweepInput{*sweepInput}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := sweepableOutputs[expirationTime]; !ok {
|
||||
@@ -351,62 +338,6 @@ func (s *sweeper) findSweepableOutputs(
|
||||
return sweepableOutputs, nil
|
||||
}
|
||||
|
||||
func (s *sweeper) leafToSweepInput(ctx context.Context, txBlocktime int64, node tree.Node) (int64, *ports.SweepInput, error) {
|
||||
pset, err := psetv2.NewPsetFromBase64(node.Tx)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
vtxo, err := extractVtxoOutpoint(pset)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
fromRepo, err := s.repoManager.Vtxos().GetVtxos(ctx, []domain.VtxoKey{*vtxo})
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
if len(fromRepo) == 0 {
|
||||
return -1, nil, fmt.Errorf("vtxo not found")
|
||||
}
|
||||
|
||||
if fromRepo[0].Swept {
|
||||
return -1, nil, nil
|
||||
}
|
||||
|
||||
if fromRepo[0].Redeemed {
|
||||
return -1, nil, nil
|
||||
}
|
||||
|
||||
// if the vtxo is not swept or redeemed, add it to the onchain outputs
|
||||
pubKeyBytes, err := hex.DecodeString(fromRepo[0].Pubkey)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
pubKey, err := secp256k1.ParsePubKey(pubKeyBytes)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
sweepLeaf, lifetime, err := s.builder.GetLeafSweepClosure(node, pubKey)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
sweepInput := ports.SweepInput{
|
||||
InputArgs: psetv2.InputArgs{
|
||||
Txid: vtxo.Txid,
|
||||
TxIndex: vtxo.VOut,
|
||||
},
|
||||
SweepLeaf: *sweepLeaf,
|
||||
Amount: fromRepo[0].Amount,
|
||||
}
|
||||
|
||||
return txBlocktime + lifetime, &sweepInput, nil
|
||||
}
|
||||
|
||||
func (s *sweeper) nodeToSweepInputs(parentBlocktime int64, node tree.Node) (int64, []ports.SweepInput, error) {
|
||||
pset, err := psetv2.NewPsetFromBase64(node.Tx)
|
||||
if err != nil {
|
||||
@@ -543,14 +474,14 @@ func containsTree(tr0 tree.CongestionTree, tr1 tree.CongestionTree) (bool, error
|
||||
// given a congestion tree input, searches and returns the sweep leaf and its lifetime in seconds
|
||||
func extractSweepLeaf(input psetv2.Input) (sweepLeaf *psetv2.TapLeafScript, lifetime int64, err error) {
|
||||
for _, leaf := range input.TapLeafScript {
|
||||
isSweep, _, seconds, err := tree.DecodeSweepScript(leaf.Script)
|
||||
closure := &tree.CSVSigClosure{}
|
||||
valid, err := closure.Decode(leaf.Script)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if isSweep {
|
||||
lifetime = int64(seconds)
|
||||
if valid && closure.Seconds > uint(lifetime) {
|
||||
sweepLeaf = &leaf
|
||||
break
|
||||
lifetime = int64(closure.Seconds)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,5 @@ type TxBuilder interface {
|
||||
wallet WalletService,
|
||||
inputs []SweepInput,
|
||||
) (signedSweepTx string, err error)
|
||||
GetLeafSweepClosure(node tree.Node, userPubKey *secp256k1.PublicKey) (*psetv2.TapLeafScript, int64, error)
|
||||
GetVtxoScript(userPubkey, aspPubkey *secp256k1.PublicKey) ([]byte, error)
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ func (s *service) EstimateFees(
|
||||
}
|
||||
|
||||
if len(in.TapLeafScript) == 1 {
|
||||
isSweep, _, _, err := tree.DecodeSweepScript(in.TapLeafScript[0].Script)
|
||||
isSweep, err := (&tree.CSVSigClosure{}).Decode(in.TapLeafScript[0].Script)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -24,12 +24,13 @@ type txBuilder struct {
|
||||
wallet ports.WalletService
|
||||
net *network.Network
|
||||
roundLifetime int64 // in seconds
|
||||
exitDelay int64 // in seconds
|
||||
}
|
||||
|
||||
func NewTxBuilder(
|
||||
wallet ports.WalletService, net network.Network, roundLifetime int64,
|
||||
wallet ports.WalletService, net network.Network, roundLifetime int64, exitDelay int64,
|
||||
) ports.TxBuilder {
|
||||
return &txBuilder{wallet, &net, roundLifetime}
|
||||
return &txBuilder{wallet, &net, roundLifetime, exitDelay}
|
||||
}
|
||||
|
||||
func (b *txBuilder) GetVtxoScript(userPubkey, aspPubkey *secp256k1.PublicKey) ([]byte, error) {
|
||||
@@ -113,7 +114,7 @@ func (b *txBuilder) BuildPoolTx(
|
||||
// This is safe as the memory allocated for `craftCongestionTree` is freed
|
||||
// only after `BuildPoolTx` returns.
|
||||
treeFactoryFn, sharedOutputScript, sharedOutputAmount, err := craftCongestionTree(
|
||||
b.net.AssetID, aspPubkey, payments, minRelayFee, b.roundLifetime,
|
||||
b.net.AssetID, aspPubkey, payments, minRelayFee, b.roundLifetime, b.exitDelay,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -148,60 +149,31 @@ func (b *txBuilder) BuildPoolTx(
|
||||
return
|
||||
}
|
||||
|
||||
func (b *txBuilder) GetLeafSweepClosure(
|
||||
node tree.Node, userPubKey *secp256k1.PublicKey,
|
||||
) (*psetv2.TapLeafScript, int64, error) {
|
||||
if !node.Leaf {
|
||||
return nil, 0, fmt.Errorf("node is not a leaf")
|
||||
}
|
||||
|
||||
pset, err := psetv2.NewPsetFromBase64(node.Tx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
input := pset.Inputs[0]
|
||||
|
||||
sweepLeaf, lifetime, err := extractSweepLeaf(input)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// craft the vtxo taproot tree
|
||||
vtxoScript, err := tree.VtxoScript(userPubKey)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
vtxoTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
*vtxoScript,
|
||||
sweepLeaf.TapElementsLeaf,
|
||||
)
|
||||
|
||||
proofIndex := vtxoTaprootTree.LeafProofIndex[sweepLeaf.TapHash()]
|
||||
proof := vtxoTaprootTree.LeafMerkleProofs[proofIndex]
|
||||
|
||||
return &psetv2.TapLeafScript{
|
||||
TapElementsLeaf: proof.TapElementsLeaf,
|
||||
ControlBlock: proof.ToControlBlock(sweepLeaf.ControlBlock.InternalKey),
|
||||
}, lifetime, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) getLeafScriptAndTree(
|
||||
userPubkey, aspPubkey *secp256k1.PublicKey,
|
||||
) ([]byte, *taproot.IndexedElementsTapScriptTree, error) {
|
||||
redeemClosure, err := tree.VtxoScript(userPubkey)
|
||||
redeemClosure := &tree.CSVSigClosure{
|
||||
Pubkey: userPubkey,
|
||||
Seconds: uint(b.exitDelay),
|
||||
}
|
||||
|
||||
redeemLeaf, err := redeemClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sweepClosure, err := tree.SweepScript(aspPubkey, uint(b.roundLifetime))
|
||||
forfeitClosure := &tree.ForfeitClosure{
|
||||
Pubkey: userPubkey,
|
||||
AspPubkey: aspPubkey,
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
taprootTree := taproot.AssembleTaprootScriptTree(
|
||||
*redeemClosure, *sweepClosure,
|
||||
*redeemLeaf, *forfeitLeaf,
|
||||
)
|
||||
|
||||
root := taprootTree.RootNode.TapHash()
|
||||
@@ -471,9 +443,25 @@ func (b *txBuilder) createForfeitTxs(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var forfeitProof *taproot.TapscriptElementsProof
|
||||
|
||||
for _, proof := range vtxoTaprootTree.LeafMerkleProofs {
|
||||
isForfeit, err := (&tree.ForfeitClosure{}).Decode(proof.Script)
|
||||
if !isForfeit || err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
forfeitProof = &proof
|
||||
break
|
||||
}
|
||||
|
||||
if forfeitProof == nil {
|
||||
return nil, fmt.Errorf("forfeit proof not found")
|
||||
}
|
||||
|
||||
for _, connector := range connectors {
|
||||
txs, err := craftForfeitTxs(
|
||||
connector, vtxo, vtxoTaprootTree, vtxoScript, aspScript,
|
||||
connector, vtxo, *forfeitProof, vtxoScript, aspScript,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -485,24 +473,3 @@ func (b *txBuilder) createForfeitTxs(
|
||||
}
|
||||
return forfeitTxs, nil
|
||||
}
|
||||
|
||||
// given a congestion tree input, searches and returns the sweep leaf and its lifetime in seconds
|
||||
func extractSweepLeaf(input psetv2.Input) (sweepLeaf *psetv2.TapLeafScript, lifetime int64, err error) {
|
||||
for _, leaf := range input.TapLeafScript {
|
||||
isSweep, _, seconds, err := tree.DecodeSweepScript(leaf.Script)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if isSweep {
|
||||
lifetime = int64(seconds)
|
||||
sweepLeaf = &leaf
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sweepLeaf == nil {
|
||||
return nil, 0, fmt.Errorf("sweep leaf not found")
|
||||
}
|
||||
|
||||
return sweepLeaf, lifetime, nil
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ const (
|
||||
testingKey = "0218d5ca8b58797b7dbd65c075dd7ba7784b3f38ab71b1a5a8e3f94ba0257654a6"
|
||||
minRelayFee = uint64(30)
|
||||
roundLifetime = int64(1209344)
|
||||
exitDelay = int64(512)
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -44,7 +45,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func TestBuildPoolTx(t *testing.T) {
|
||||
builder := txbuilder.NewTxBuilder(wallet, network.Liquid, roundLifetime)
|
||||
builder := txbuilder.NewTxBuilder(wallet, network.Liquid, roundLifetime, exitDelay)
|
||||
|
||||
fixtures, err := parsePoolTxFixtures()
|
||||
require.NoError(t, err)
|
||||
@@ -79,7 +80,7 @@ func TestBuildPoolTx(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildForfeitTxs(t *testing.T) {
|
||||
builder := txbuilder.NewTxBuilder(wallet, network.Liquid, 1209344)
|
||||
builder := txbuilder.NewTxBuilder(wallet, network.Liquid, 1209344, exitDelay)
|
||||
|
||||
fixtures, err := parseForfeitTxsFixtures()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
func craftForfeitTxs(
|
||||
connectorTx *psetv2.Pset,
|
||||
vtxo domain.Vtxo,
|
||||
vtxoTaprootTree *taproot.IndexedElementsTapScriptTree,
|
||||
vtxoForfeitTapleaf taproot.TapscriptElementsProof,
|
||||
vtxoScript, aspScript []byte,
|
||||
) (forfeitTxs []string, err error) {
|
||||
connectors, prevouts := getConnectorInputs(connectorTx)
|
||||
@@ -66,11 +66,9 @@ func craftForfeitTxs(
|
||||
|
||||
unspendableKey := tree.UnspendableKey()
|
||||
|
||||
for _, proof := range vtxoTaprootTree.LeafMerkleProofs {
|
||||
tapScript := psetv2.NewTapLeafScript(proof, unspendableKey)
|
||||
if err := updater.AddInTapLeafScript(1, tapScript); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tapScript := psetv2.NewTapLeafScript(vtxoForfeitTapleaf, unspendableKey)
|
||||
if err := updater.AddInTapLeafScript(1, tapScript); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
connectorAmount, err := elementsutil.ValueFromBytes(connectorPrevout.Value)
|
||||
|
||||
@@ -33,55 +33,56 @@ func sweepTransaction(
|
||||
|
||||
for i, input := range sweepInputs {
|
||||
leaf := input.SweepLeaf
|
||||
isSweep, _, lifetime, err := tree.DecodeSweepScript(leaf.Script)
|
||||
sweepClosure := &tree.CSVSigClosure{}
|
||||
isSweep, err := sweepClosure.Decode(leaf.Script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isSweep {
|
||||
amount += input.Amount
|
||||
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{input.InputArgs}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updater.AddInTapLeafScript(i, leaf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assetHash, err := elementsutil.AssetHashToBytes(lbtc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := elementsutil.ValueToBytes(input.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := leaf.ControlBlock.RootHash(leaf.Script)
|
||||
taprootKey := taproot.ComputeTaprootOutputKey(leaf.ControlBlock.InternalKey, root)
|
||||
script, err := taprootOutputScript(taprootKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
witnessUtxo := transaction.NewTxOutput(assetHash, value, script)
|
||||
|
||||
if err := updater.AddInWitnessUtxo(i, witnessUtxo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sequence, err := common.BIP68EncodeAsNumber(lifetime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater.Pset.Inputs[i].Sequence = sequence
|
||||
continue
|
||||
if !isSweep {
|
||||
return nil, fmt.Errorf("invalid sweep script")
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid sweep script")
|
||||
amount += input.Amount
|
||||
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{input.InputArgs}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updater.AddInTapLeafScript(i, leaf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assetHash, err := elementsutil.AssetHashToBytes(lbtc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := elementsutil.ValueToBytes(input.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := leaf.ControlBlock.RootHash(leaf.Script)
|
||||
taprootKey := taproot.ComputeTaprootOutputKey(leaf.ControlBlock.InternalKey, root)
|
||||
script, err := taprootOutputScript(taprootKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
witnessUtxo := transaction.NewTxOutput(assetHash, value, script)
|
||||
|
||||
if err := updater.AddInWitnessUtxo(i, witnessUtxo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sequence, err := common.BIP68EncodeAsNumber(sweepClosure.Seconds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater.Pset.Inputs[i].Sequence = sequence
|
||||
continue
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -22,6 +22,7 @@ type node struct {
|
||||
asset string
|
||||
feeSats uint64
|
||||
roundLifetime int64
|
||||
exitDelay int64
|
||||
|
||||
_inputTaprootKey *secp256k1.PublicKey
|
||||
_inputTaprootTree *taproot.IndexedElementsTapScriptTree
|
||||
@@ -130,7 +131,12 @@ func (n *node) getWitnessData() (
|
||||
return n._inputTaprootKey, n._inputTaprootTree, nil
|
||||
}
|
||||
|
||||
sweepClosure, err := tree.SweepScript(n.sweepKey, uint(n.roundLifetime))
|
||||
sweepClosure := &tree.CSVSigClosure{
|
||||
Pubkey: n.sweepKey,
|
||||
Seconds: uint(n.roundLifetime),
|
||||
}
|
||||
|
||||
sweepLeaf, err := sweepClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -141,12 +147,18 @@ func (n *node) getWitnessData() (
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
branchTaprootScript := tree.BranchScript(
|
||||
taprootKey, nil, n.getAmount(), 0,
|
||||
)
|
||||
unrollClosure := &tree.UnrollClosure{
|
||||
LeftKey: taprootKey,
|
||||
LeftAmount: n.getAmount(),
|
||||
}
|
||||
|
||||
unrollLeaf, err := unrollClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
branchTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
branchTaprootScript, *sweepClosure,
|
||||
*unrollLeaf, *sweepLeaf,
|
||||
)
|
||||
root := branchTaprootTree.RootNode.TapHash()
|
||||
|
||||
@@ -173,12 +185,21 @@ func (n *node) getWitnessData() (
|
||||
|
||||
leftAmount := n.left.getAmount() + n.feeSats
|
||||
rightAmount := n.right.getAmount() + n.feeSats
|
||||
branchTaprootLeaf := tree.BranchScript(
|
||||
leftKey, rightKey, leftAmount, rightAmount,
|
||||
)
|
||||
|
||||
unrollClosure := &tree.UnrollClosure{
|
||||
LeftKey: leftKey,
|
||||
LeftAmount: leftAmount,
|
||||
RightKey: rightKey,
|
||||
RightAmount: rightAmount,
|
||||
}
|
||||
|
||||
unrollLeaf, err := unrollClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
branchTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
branchTaprootLeaf, *sweepClosure,
|
||||
*unrollLeaf, *sweepLeaf,
|
||||
)
|
||||
root := branchTaprootTree.RootNode.TapHash()
|
||||
|
||||
@@ -200,11 +221,6 @@ func (n *node) getVtxoWitnessData() (
|
||||
return nil, nil, fmt.Errorf("cannot call vtxoWitness on a non-leaf node")
|
||||
}
|
||||
|
||||
sweepClosure, err := tree.SweepScript(n.sweepKey, uint(n.roundLifetime))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key, err := hex.DecodeString(n.receivers[0].Pubkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -215,14 +231,28 @@ func (n *node) getVtxoWitnessData() (
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
vtxoLeaf, err := tree.VtxoScript(pubkey)
|
||||
redeemClosure := &tree.CSVSigClosure{
|
||||
Pubkey: pubkey,
|
||||
Seconds: uint(n.exitDelay),
|
||||
}
|
||||
|
||||
redeemLeaf, err := redeemClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
forfeitClosure := &tree.ForfeitClosure{
|
||||
Pubkey: pubkey,
|
||||
AspPubkey: n.sweepKey,
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// TODO: add forfeit path
|
||||
leafTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
*vtxoLeaf, *sweepClosure,
|
||||
*redeemLeaf, *forfeitLeaf,
|
||||
)
|
||||
root := leafTaprootTree.RootNode.TapHash()
|
||||
|
||||
@@ -357,14 +387,14 @@ func (n *node) createFinalCongestionTree() treeFactory {
|
||||
|
||||
func craftCongestionTree(
|
||||
asset string, aspPublicKey *secp256k1.PublicKey,
|
||||
payments []domain.Payment, feeSatsPerNode uint64, roundLifetime int64,
|
||||
payments []domain.Payment, feeSatsPerNode uint64, roundLifetime int64, exitDelay int64,
|
||||
) (
|
||||
buildCongestionTree treeFactory,
|
||||
sharedOutputScript []byte, sharedOutputAmount uint64, err error,
|
||||
) {
|
||||
receivers := getOffchainReceivers(payments)
|
||||
root, err := createPartialCongestionTree(
|
||||
receivers, aspPublicKey, asset, feeSatsPerNode, roundLifetime,
|
||||
receivers, aspPublicKey, asset, feeSatsPerNode, roundLifetime, exitDelay,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -391,6 +421,7 @@ func createPartialCongestionTree(
|
||||
asset string,
|
||||
feeSatsPerNode uint64,
|
||||
roundLifetime int64,
|
||||
exitDelay int64,
|
||||
) (root *node, err error) {
|
||||
if len(receivers) == 0 {
|
||||
return nil, fmt.Errorf("no receivers provided")
|
||||
@@ -404,6 +435,7 @@ func createPartialCongestionTree(
|
||||
asset: asset,
|
||||
feeSats: feeSatsPerNode,
|
||||
roundLifetime: roundLifetime,
|
||||
exitDelay: exitDelay,
|
||||
}
|
||||
nodes = append(nodes, leafNode)
|
||||
}
|
||||
|
||||
@@ -178,14 +178,16 @@ func (h *handler) ListVtxos(ctx context.Context, req *arkv1.ListVtxosRequest) (*
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handler) GetPubkey(ctx context.Context, req *arkv1.GetPubkeyRequest) (*arkv1.GetPubkeyResponse, error) {
|
||||
pubkey, err := h.svc.GetPubkey(ctx)
|
||||
func (h *handler) GetInfo(ctx context.Context, req *arkv1.GetInfoRequest) (*arkv1.GetInfoResponse, error) {
|
||||
pubkey, lifetime, delay, err := h.svc.GetInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.GetPubkeyResponse{
|
||||
Pubkey: pubkey,
|
||||
return &arkv1.GetInfoResponse{
|
||||
Pubkey: pubkey,
|
||||
Lifetime: lifetime,
|
||||
ExitDelay: delay,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user