mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-19 05:04:21 +01:00
Forfeit transactions: signing process (#81)
* sign forfeit transactions * revert txid + move SignVtxos func * Fix nolint * Fix * fix connectorsToInputArgs function --------- Co-authored-by: altafan <18440657+altafan@users.noreply.github.com>
This commit is contained in:
@@ -5,7 +5,7 @@ go 1.21.0
|
|||||||
replace github.com/ark-network/ark/common => ../common
|
replace github.com/ark-network/ark/common => ../common
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ark-network/ark/common v0.0.0-00010101000000-000000000000
|
github.com/ark-network/ark/common v0.0.0
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0
|
||||||
github.com/dgraph-io/badger/v4 v4.1.0
|
github.com/dgraph-io/badger/v4 v4.1.0
|
||||||
github.com/go-co-op/gocron v1.36.0
|
github.com/go-co-op/gocron v1.36.0
|
||||||
@@ -18,7 +18,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/timshannon/badgerhold/v4 v4.0.3
|
github.com/timshannon/badgerhold/v4 v4.0.3
|
||||||
github.com/urfave/cli/v2 v2.26.0
|
github.com/urfave/cli/v2 v2.26.0
|
||||||
github.com/vulpemventures/go-elements v0.5.1
|
github.com/vulpemventures/go-elements v0.5.2
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
|
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
|
||||||
google.golang.org/grpc v1.59.0
|
google.golang.org/grpc v1.59.0
|
||||||
google.golang.org/protobuf v1.31.0
|
google.golang.org/protobuf v1.31.0
|
||||||
|
|||||||
@@ -327,8 +327,8 @@ github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
|||||||
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
|
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
|
||||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
|
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
|
||||||
github.com/vulpemventures/go-elements v0.5.1 h1:F83n7dScOnAnpyH9VgWLdC68Qcva+2EWVzzNuW2UKak=
|
github.com/vulpemventures/go-elements v0.5.2 h1:vIDzVpRXG5PnlzHA8tCnr2Tn7raIV5cHy7bRyDrbuM4=
|
||||||
github.com/vulpemventures/go-elements v0.5.1/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
|
github.com/vulpemventures/go-elements v0.5.2/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
|
||||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
|
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
|
||||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
|
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/ark-network/ark/common"
|
"github.com/ark-network/ark/common"
|
||||||
"github.com/ark-network/ark/internal/core/domain"
|
"github.com/ark-network/ark/internal/core/domain"
|
||||||
"github.com/ark-network/ark/internal/core/ports"
|
"github.com/ark-network/ark/internal/core/ports"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/vulpemventures/go-elements/network"
|
"github.com/vulpemventures/go-elements/network"
|
||||||
@@ -62,7 +63,9 @@ func NewService(
|
|||||||
) (Service, error) {
|
) (Service, error) {
|
||||||
eventsCh := make(chan domain.RoundEvent)
|
eventsCh := make(chan domain.RoundEvent)
|
||||||
paymentRequests := newPaymentsMap(nil)
|
paymentRequests := newPaymentsMap(nil)
|
||||||
forfeitTxs := newForfeitTxsMap()
|
|
||||||
|
genesisHash, _ := chainhash.NewHashFromStr(onchainNetwork.GenesisBlockHash)
|
||||||
|
forfeitTxs := newForfeitTxsMap(genesisHash)
|
||||||
pubkey, err := walletSvc.GetPubkey(context.Background())
|
pubkey, err := walletSvc.GetPubkey(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch pubkey: %s", err)
|
return nil, fmt.Errorf("failed to fetch pubkey: %s", err)
|
||||||
@@ -169,10 +172,7 @@ func (s *service) FaucetVtxos(ctx context.Context, userPubkey *secp256k1.PublicK
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) SignVtxos(ctx context.Context, forfeitTxs []string) error {
|
func (s *service) SignVtxos(ctx context.Context, forfeitTxs []string) error {
|
||||||
if err := s.forfeitTxs.sign(forfeitTxs); err != nil {
|
return s.forfeitTxs.sign(forfeitTxs)
|
||||||
return fmt.Errorf("invalid forfeit tx: %s", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, error) {
|
func (s *service) ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, error) {
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ark-network/ark/common"
|
||||||
"github.com/ark-network/ark/internal/core/domain"
|
"github.com/ark-network/ark/internal/core/domain"
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/vulpemventures/go-elements/psetv2"
|
"github.com/vulpemventures/go-elements/psetv2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -136,22 +141,36 @@ type signedTx struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type forfeitTxsMap struct {
|
type forfeitTxsMap struct {
|
||||||
lock *sync.RWMutex
|
lock *sync.RWMutex
|
||||||
forfeitTxs map[string]*signedTx
|
forfeitTxs map[string]*signedTx
|
||||||
|
genesisBlockHash *chainhash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func newForfeitTxsMap() *forfeitTxsMap {
|
func newForfeitTxsMap(genesisBlockHash *chainhash.Hash) *forfeitTxsMap {
|
||||||
return &forfeitTxsMap{&sync.RWMutex{}, make(map[string]*signedTx)}
|
return &forfeitTxsMap{&sync.RWMutex{}, make(map[string]*signedTx), genesisBlockHash}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *forfeitTxsMap) push(txs []string) {
|
func (m *forfeitTxsMap) push(txs []string) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
faucetTxID, _ := hex.DecodeString(faucetVtxo.Txid)
|
||||||
|
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
ptx, _ := psetv2.NewPsetFromBase64(tx)
|
ptx, _ := psetv2.NewPsetFromBase64(tx)
|
||||||
utx, _ := ptx.UnsignedTx()
|
utx, _ := ptx.UnsignedTx()
|
||||||
m.forfeitTxs[utx.TxHash().String()] = &signedTx{tx, false}
|
|
||||||
|
signed := false
|
||||||
|
|
||||||
|
// find the faucet vtxos, and mark them as signed
|
||||||
|
for _, input := range ptx.Inputs {
|
||||||
|
if bytes.Equal(input.PreviousTxid, faucetTxID) {
|
||||||
|
signed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.forfeitTxs[utx.TxHash().String()] = &signedTx{tx, signed}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,17 +179,50 @@ func (m *forfeitTxsMap) sign(txs []string) error {
|
|||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
ptx, err := psetv2.NewPsetFromBase64(tx)
|
ptx, _ := psetv2.NewPsetFromBase64(tx)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid forfeit tx format")
|
|
||||||
}
|
|
||||||
utx, _ := ptx.UnsignedTx()
|
utx, _ := ptx.UnsignedTx()
|
||||||
txid := utx.TxHash().String()
|
txid := utx.TxHash().String()
|
||||||
if _, ok := m.forfeitTxs[txid]; !ok {
|
|
||||||
return fmt.Errorf("forfeit tx %s not found", txid)
|
if _, ok := m.forfeitTxs[txid]; ok {
|
||||||
|
for index, input := range ptx.Inputs {
|
||||||
|
if len(input.TapScriptSig) > 0 {
|
||||||
|
for _, tapScriptSig := range input.TapScriptSig {
|
||||||
|
leafHash, err := chainhash.NewHash(tapScriptSig.LeafHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
preimage, err := common.TaprootPreimage(
|
||||||
|
m.genesisBlockHash,
|
||||||
|
ptx,
|
||||||
|
index,
|
||||||
|
leafHash,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := schnorr.ParseSignature(tapScriptSig.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkey, err := schnorr.ParsePubKey(tapScriptSig.PubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sig.Verify(preimage, pubkey) {
|
||||||
|
m.forfeitTxs[txid].signed = true
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("invalid signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.forfeitTxs[txid].signed = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/ark-network/ark/common"
|
||||||
"github.com/ark-network/ark/internal/core/domain"
|
"github.com/ark-network/ark/internal/core/domain"
|
||||||
"github.com/ark-network/ark/internal/core/ports"
|
"github.com/ark-network/ark/internal/core/ports"
|
||||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
"github.com/vulpemventures/go-elements/address"
|
"github.com/vulpemventures/go-elements/address"
|
||||||
|
"github.com/vulpemventures/go-elements/elementsutil"
|
||||||
"github.com/vulpemventures/go-elements/network"
|
"github.com/vulpemventures/go-elements/network"
|
||||||
"github.com/vulpemventures/go-elements/payment"
|
"github.com/vulpemventures/go-elements/payment"
|
||||||
"github.com/vulpemventures/go-elements/psetv2"
|
"github.com/vulpemventures/go-elements/psetv2"
|
||||||
@@ -58,29 +60,11 @@ func getTxid(txStr string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *txBuilder) GetLeafOutputScript(userPubkey, aspPubkey *secp256k1.PublicKey) ([]byte, error) {
|
func (b *txBuilder) GetLeafOutputScript(userPubkey, aspPubkey *secp256k1.PublicKey) ([]byte, error) {
|
||||||
unspendableKeyBytes, _ := hex.DecodeString(unspendablePoint)
|
outputScript, _, err := b.getLeafTaprootTree(userPubkey, aspPubkey)
|
||||||
unspendableKey, _ := secp256k1.ParsePubKey(unspendableKeyBytes)
|
|
||||||
|
|
||||||
sweepTaprootLeaf, err := sweepTapLeaf(aspPubkey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return outputScript, nil
|
||||||
leafScript, err := checksigScript(userPubkey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
leafTaprootLeaf := taproot.NewBaseTapElementsLeaf(leafScript)
|
|
||||||
leafTaprootTree := taproot.AssembleTaprootScriptTree(leafTaprootLeaf, *sweepTaprootLeaf)
|
|
||||||
root := leafTaprootTree.RootNode.TapHash()
|
|
||||||
|
|
||||||
taprootKey := taproot.ComputeTaprootOutputKey(
|
|
||||||
unspendableKey,
|
|
||||||
root[:],
|
|
||||||
)
|
|
||||||
|
|
||||||
return taprootOutputScript(taprootKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildForfeitTxs implements ports.TxBuilder.
|
// BuildForfeitTxs implements ports.TxBuilder.
|
||||||
@@ -119,17 +103,46 @@ func (b *txBuilder) BuildForfeitTxs(
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lbtc, _ := elementsutil.AssetHashToBytes(b.net.AssetID)
|
||||||
|
|
||||||
forfeitTxs = make([]string, 0)
|
forfeitTxs = make([]string, 0)
|
||||||
|
|
||||||
for _, payment := range payments {
|
for _, payment := range payments {
|
||||||
for _, vtxo := range payment.Inputs {
|
for _, vtxo := range payment.Inputs {
|
||||||
|
vtxoAmount, err := elementsutil.ValueToBytes(vtxo.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkeyBytes, err := hex.DecodeString(vtxo.Pubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vtxoPubkey, err := secp256k1.ParsePubKey(pubkeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vtxoOutputScript, vtxoTaprootTree, err := b.getLeafTaprootTree(vtxoPubkey, aspPubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for _, connector := range connectorsAsInputs {
|
for _, connector := range connectorsAsInputs {
|
||||||
forfeitTx, err := createForfeitTx(
|
forfeitTx, err := createForfeitTx(
|
||||||
connector,
|
connector.input,
|
||||||
|
connector.witnessUtxo,
|
||||||
psetv2.InputArgs{
|
psetv2.InputArgs{
|
||||||
Txid: vtxo.Txid,
|
Txid: vtxo.Txid,
|
||||||
TxIndex: vtxo.VOut,
|
TxIndex: vtxo.VOut,
|
||||||
},
|
},
|
||||||
vtxo.Amount,
|
&transaction.TxOutput{
|
||||||
|
Asset: lbtc,
|
||||||
|
Value: vtxoAmount,
|
||||||
|
Script: vtxoOutputScript,
|
||||||
|
},
|
||||||
|
vtxoTaprootTree,
|
||||||
aspScript,
|
aspScript,
|
||||||
*b.net,
|
*b.net,
|
||||||
)
|
)
|
||||||
@@ -140,6 +153,7 @@ func (b *txBuilder) BuildForfeitTxs(
|
|||||||
forfeitTxs = append(forfeitTxs, forfeitTx)
|
forfeitTxs = append(forfeitTxs, forfeitTx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return connectors, forfeitTxs, nil
|
return connectors, forfeitTxs, nil
|
||||||
@@ -211,45 +225,79 @@ func (b *txBuilder) BuildPoolTx(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectorsToInputArgs(connectors []string) ([]psetv2.InputArgs, error) {
|
func (b *txBuilder) getLeafTaprootTree(userPubkey, aspPubkey *secp256k1.PublicKey) ([]byte, *taproot.IndexedElementsTapScriptTree, error) {
|
||||||
inputs := make([]psetv2.InputArgs, 0, len(connectors)+1)
|
sweepTaprootLeaf, err := sweepTapLeaf(aspPubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vtxoLeaf, err := common.VtxoScript(userPubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
leafTaprootTree := taproot.AssembleTaprootScriptTree(*vtxoLeaf, *sweepTaprootLeaf)
|
||||||
|
root := leafTaprootTree.RootNode.TapHash()
|
||||||
|
|
||||||
|
unspendableKeyBytes, _ := hex.DecodeString(unspendablePoint)
|
||||||
|
unspendableKey, _ := secp256k1.ParsePubKey(unspendableKeyBytes)
|
||||||
|
|
||||||
|
taprootKey := taproot.ComputeTaprootOutputKey(
|
||||||
|
unspendableKey,
|
||||||
|
root[:],
|
||||||
|
)
|
||||||
|
|
||||||
|
outputScript, err := taprootOutputScript(taprootKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputScript, leafTaprootTree, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type inputWithWitnessUtxo struct {
|
||||||
|
input psetv2.InputArgs
|
||||||
|
witnessUtxo *transaction.TxOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectorsToInputArgs(connectors []string) ([]inputWithWitnessUtxo, error) {
|
||||||
|
inputs := make([]inputWithWitnessUtxo, 0, len(connectors)+1)
|
||||||
for i, psetb64 := range connectors {
|
for i, psetb64 := range connectors {
|
||||||
txID, err := getTxID(psetb64)
|
pset, err := psetv2.NewPsetFromBase64(psetb64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utx, err := pset.UnsignedTx()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
txID := utx.TxHash().String()
|
||||||
|
|
||||||
input := psetv2.InputArgs{
|
input := psetv2.InputArgs{
|
||||||
Txid: txID,
|
Txid: txID,
|
||||||
TxIndex: 0,
|
TxIndex: 0,
|
||||||
}
|
}
|
||||||
inputs = append(inputs, input)
|
inputs = append(inputs, inputWithWitnessUtxo{
|
||||||
|
input: input,
|
||||||
|
witnessUtxo: utx.Outputs[0],
|
||||||
|
})
|
||||||
|
|
||||||
if i == len(connectors)-1 {
|
if i == len(connectors)-1 && len(utx.Outputs) > 1 {
|
||||||
input := psetv2.InputArgs{
|
input := psetv2.InputArgs{
|
||||||
Txid: txID,
|
Txid: txID,
|
||||||
TxIndex: 1,
|
TxIndex: 1,
|
||||||
}
|
}
|
||||||
inputs = append(inputs, input)
|
inputs = append(inputs, inputWithWitnessUtxo{
|
||||||
|
input: input,
|
||||||
|
witnessUtxo: utx.Outputs[1],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return inputs, nil
|
return inputs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTxID(psetBase64 string) (string, error) {
|
|
||||||
pset, err := psetv2.NewPsetFromBase64(psetBase64)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
utx, err := pset.UnsignedTx()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utx.TxHash().String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func numberOfVTXOs(payments []domain.Payment) uint64 {
|
func numberOfVTXOs(payments []domain.Payment) uint64 {
|
||||||
var sum uint64
|
var sum uint64
|
||||||
for _, payment := range payments {
|
for _, payment := range payments {
|
||||||
|
|||||||
@@ -300,14 +300,14 @@ func TestBuildCongestionTree(t *testing.T) {
|
|||||||
func TestBuildForfeitTxs(t *testing.T) {
|
func TestBuildForfeitTxs(t *testing.T) {
|
||||||
builder := txbuilder.NewTxBuilder(network.Liquid)
|
builder := txbuilder.NewTxBuilder(network.Liquid)
|
||||||
|
|
||||||
// TODO
|
// TODO: replace with fixture.
|
||||||
poolTx, err := createTestPoolTx(1000, 2)
|
poolTxHex, err := createTestPoolTx(1000, 2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tx, err := transaction.NewTxFromHex(poolTx)
|
poolTx, err := transaction.NewTxFromHex(poolTxHex)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
poolTxID := tx.TxHash().String()
|
poolTxid := poolTx.TxHash().String()
|
||||||
|
|
||||||
fixtures := []struct {
|
fixtures := []struct {
|
||||||
payments []domain.Payment
|
payments []domain.Payment
|
||||||
@@ -363,7 +363,7 @@ func TestBuildForfeitTxs(t *testing.T) {
|
|||||||
|
|
||||||
for _, f := range fixtures {
|
for _, f := range fixtures {
|
||||||
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
||||||
key, poolTx, f.payments,
|
key, poolTxHex, f.payments,
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, connectors, f.expectedNumOfConnectors)
|
require.Len(t, connectors, f.expectedNumOfConnectors)
|
||||||
@@ -381,7 +381,7 @@ func TestBuildForfeitTxs(t *testing.T) {
|
|||||||
require.Len(t, pset.Inputs, 1)
|
require.Len(t, pset.Inputs, 1)
|
||||||
require.Len(t, pset.Outputs, 2)
|
require.Len(t, pset.Outputs, 2)
|
||||||
|
|
||||||
expectedInputTxid := poolTxID
|
expectedInputTxid := poolTxid
|
||||||
expectedInputVout := uint32(1)
|
expectedInputVout := uint32(1)
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
tx, err := connectorsPsets[i-1].UnsignedTx()
|
tx, err := connectorsPsets[i-1].UnsignedTx()
|
||||||
@@ -406,7 +406,7 @@ func TestBuildForfeitTxs(t *testing.T) {
|
|||||||
// each forfeit tx should have 2 inputs and 2 outputs
|
// each forfeit tx should have 2 inputs and 2 outputs
|
||||||
for _, pset := range forfeitTxsPsets {
|
for _, pset := range forfeitTxsPsets {
|
||||||
require.Len(t, pset.Inputs, 2)
|
require.Len(t, pset.Inputs, 2)
|
||||||
require.Len(t, pset.Outputs, 1)
|
require.Len(t, pset.Outputs, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
package txbuilder
|
package txbuilder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
"github.com/vulpemventures/go-elements/elementsutil"
|
||||||
"github.com/vulpemventures/go-elements/network"
|
"github.com/vulpemventures/go-elements/network"
|
||||||
"github.com/vulpemventures/go-elements/psetv2"
|
"github.com/vulpemventures/go-elements/psetv2"
|
||||||
|
"github.com/vulpemventures/go-elements/taproot"
|
||||||
|
"github.com/vulpemventures/go-elements/transaction"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createForfeitTx(
|
func createForfeitTx(
|
||||||
connectorInput psetv2.InputArgs,
|
connectorInput psetv2.InputArgs,
|
||||||
|
connectorWitnessUtxo *transaction.TxOutput,
|
||||||
vtxoInput psetv2.InputArgs,
|
vtxoInput psetv2.InputArgs,
|
||||||
vtxoAmount uint64,
|
vtxoWitnessUtxo *transaction.TxOutput,
|
||||||
|
vtxoTaprootTree *taproot.IndexedElementsTapScriptTree,
|
||||||
aspScript []byte,
|
aspScript []byte,
|
||||||
net network.Network,
|
net network.Network,
|
||||||
) (forfeitTx string, err error) {
|
) (forfeitTx string, err error) {
|
||||||
@@ -22,7 +31,42 @@ func createForfeitTx(
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = updater.AddInputs([]psetv2.InputArgs{connectorInput, vtxoInput})
|
if err = updater.AddInputs([]psetv2.InputArgs{connectorInput, vtxoInput}); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = updater.AddInWitnessUtxo(0, connectorWitnessUtxo); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updater.AddInSighashType(0, txscript.SigHashAll); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = updater.AddInWitnessUtxo(1, vtxoWitnessUtxo); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updater.AddInSighashType(1, txscript.SigHashDefault); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
unspendableKeyBytes, _ := hex.DecodeString(unspendablePoint)
|
||||||
|
unspendableKey, _ := secp256k1.ParsePubKey(unspendableKeyBytes)
|
||||||
|
|
||||||
|
for _, proof := range vtxoTaprootTree.LeafMerkleProofs {
|
||||||
|
tapScript := psetv2.NewTapLeafScript(proof, unspendableKey)
|
||||||
|
if err := updater.AddInTapLeafScript(1, tapScript); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vtxoAmount, err := elementsutil.ValueFromBytes(vtxoWitnessUtxo.Value)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
connectorAmount, err := elementsutil.ValueFromBytes(connectorWitnessUtxo.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -30,9 +74,13 @@ func createForfeitTx(
|
|||||||
err = updater.AddOutputs([]psetv2.OutputArgs{
|
err = updater.AddOutputs([]psetv2.OutputArgs{
|
||||||
{
|
{
|
||||||
Asset: net.AssetID,
|
Asset: net.AssetID,
|
||||||
Amount: vtxoAmount,
|
Amount: vtxoAmount + connectorAmount - 30,
|
||||||
Script: aspScript,
|
Script: aspScript,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Asset: net.AssetID,
|
||||||
|
Amount: 30,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func parseTxs(txs []string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
if _, err := psetv2.NewPsetFromBase64(tx); err != nil {
|
if _, err := psetv2.NewPsetFromBase64(tx); err != nil {
|
||||||
return nil, fmt.Errorf("invalid tx format")
|
return nil, fmt.Errorf("invalid tx format %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return txs, nil
|
return txs, nil
|
||||||
|
|||||||
@@ -6,14 +6,16 @@ require (
|
|||||||
github.com/btcsuite/btcd v0.23.1
|
github.com/btcsuite/btcd v0.23.1
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
||||||
github.com/btcsuite/btcd/btcutil v1.1.3
|
github.com/btcsuite/btcd/btcutil v1.1.3
|
||||||
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
|
github.com/btcsuite/btcd/btcutil/psbt v1.1.4 // indirect
|
||||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
|
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
|
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
|
||||||
|
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
|
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
|
||||||
)
|
)
|
||||||
@@ -21,6 +23,6 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/vulpemventures/go-elements v0.5.1
|
github.com/vulpemventures/go-elements v0.5.2
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
|
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
|
||||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
|
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
|
||||||
github.com/vulpemventures/go-elements v0.5.1 h1:F83n7dScOnAnpyH9VgWLdC68Qcva+2EWVzzNuW2UKak=
|
github.com/vulpemventures/go-elements v0.5.2 h1:vIDzVpRXG5PnlzHA8tCnr2Tn7raIV5cHy7bRyDrbuM4=
|
||||||
github.com/vulpemventures/go-elements v0.5.1/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
|
github.com/vulpemventures/go-elements v0.5.2/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
|
||||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
|
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
|
||||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
|
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
|
||||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
|||||||
48
common/taproot.go
Normal file
48
common/taproot.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/vulpemventures/go-elements/psetv2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TaprootPreimage computes the hash for witness v1 input of a pset
|
||||||
|
// it implicitly assumes that the pset has witnessUtxo fields populated
|
||||||
|
func TaprootPreimage(
|
||||||
|
genesisBlockHash *chainhash.Hash,
|
||||||
|
pset *psetv2.Pset,
|
||||||
|
inputIndex int,
|
||||||
|
leafHash *chainhash.Hash,
|
||||||
|
) ([]byte, error) {
|
||||||
|
prevoutScripts := make([][]byte, 0)
|
||||||
|
prevoutAssets := make([][]byte, 0)
|
||||||
|
prevoutValues := make([][]byte, 0)
|
||||||
|
|
||||||
|
for i, input := range pset.Inputs {
|
||||||
|
if input.WitnessUtxo == nil {
|
||||||
|
return nil, fmt.Errorf("missing witness utxo on input #%d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
prevoutScripts = append(prevoutScripts, input.WitnessUtxo.Script)
|
||||||
|
prevoutAssets = append(prevoutAssets, input.WitnessUtxo.Asset)
|
||||||
|
prevoutValues = append(prevoutValues, input.WitnessUtxo.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
utx, err := pset.UnsignedTx()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
preimage := utx.HashForWitnessV1(
|
||||||
|
inputIndex,
|
||||||
|
prevoutScripts,
|
||||||
|
prevoutAssets,
|
||||||
|
prevoutValues,
|
||||||
|
pset.Inputs[inputIndex].SigHashType,
|
||||||
|
genesisBlockHash,
|
||||||
|
leafHash,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
return preimage[:], nil
|
||||||
|
}
|
||||||
126
noah/common.go
126
noah/common.go
@@ -9,13 +9,16 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||||
"github.com/ark-network/ark/common"
|
"github.com/ark-network/ark/common"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/vulpemventures/go-elements/network"
|
"github.com/vulpemventures/go-elements/network"
|
||||||
"github.com/vulpemventures/go-elements/payment"
|
"github.com/vulpemventures/go-elements/payment"
|
||||||
|
"github.com/vulpemventures/go-elements/psetv2"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,6 +88,7 @@ func privateKeyFromPassword() (*secp256k1.PrivateKey, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
fmt.Println("key unlocked")
|
||||||
|
|
||||||
cypher := NewAES128Cypher()
|
cypher := NewAES128Cypher()
|
||||||
privateKeyBytes, err := cypher.Decrypt(encryptedPrivateKey, password)
|
privateKeyBytes, err := cypher.Decrypt(encryptedPrivateKey, password)
|
||||||
@@ -333,3 +337,125 @@ func printJSON(resp interface{}) error {
|
|||||||
fmt.Println(string(jsonBytes))
|
fmt.Println(string(jsonBytes))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleRoundStream(
|
||||||
|
ctx *cli.Context,
|
||||||
|
client arkv1.ArkServiceClient,
|
||||||
|
paymentID string,
|
||||||
|
vtxosToSign []vtxo,
|
||||||
|
secKey *secp256k1.PrivateKey,
|
||||||
|
) (poolTxID string, err error) {
|
||||||
|
stream, err := client.GetEventStream(ctx.Context, &arkv1.GetEventStreamRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pingStop func()
|
||||||
|
pingReq := &arkv1.PingRequest{
|
||||||
|
PaymentId: paymentID,
|
||||||
|
}
|
||||||
|
for pingStop == nil {
|
||||||
|
pingStop = ping(ctx, client, pingReq)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer pingStop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
event, err := stream.Recv()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.GetRoundFailed() != nil {
|
||||||
|
pingStop()
|
||||||
|
return "", fmt.Errorf("round failed: %s", event.GetRoundFailed().GetReason())
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.GetRoundFinalization() != nil {
|
||||||
|
// stop pinging as soon as we receive some forfeit txs
|
||||||
|
pingStop()
|
||||||
|
forfeits := event.GetRoundFinalization().GetForfeitTxs()
|
||||||
|
signedForfeits := make([]string, 0)
|
||||||
|
|
||||||
|
fmt.Println("signing forfeit txs...")
|
||||||
|
|
||||||
|
explorer := NewExplorer()
|
||||||
|
|
||||||
|
for _, forfeit := range forfeits {
|
||||||
|
pset, err := psetv2.NewPsetFromBase64(forfeit)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, input := range pset.Inputs {
|
||||||
|
inputTxid := chainhash.Hash(input.PreviousTxid).String()
|
||||||
|
|
||||||
|
for _, coin := range vtxosToSign {
|
||||||
|
// check if it contains one of the input to sign
|
||||||
|
if inputTxid == coin.txid {
|
||||||
|
if err := signPset(pset, explorer, secKey); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
signedPset, err := pset.ToBase64()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
signedForfeits = append(signedForfeits, signedPset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no forfeit txs have been signed, start pinging again and wait for the next round
|
||||||
|
if len(signedForfeits) == 0 {
|
||||||
|
fmt.Println("no forfeit txs to sign, waiting for the next round...")
|
||||||
|
pingStop = nil
|
||||||
|
for pingStop == nil {
|
||||||
|
pingStop = ping(ctx, client, pingReq)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%d forfeit txs signed, finalizing payment...\n", len(signedForfeits))
|
||||||
|
_, err = client.FinalizePayment(ctx.Context, &arkv1.FinalizePaymentRequest{
|
||||||
|
SignedForfeitTxs: signedForfeits,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.GetRoundFinalized() != nil {
|
||||||
|
return event.GetRoundFinalized().GetPoolTxid(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("stream closed unexpectedly")
|
||||||
|
}
|
||||||
|
|
||||||
|
// send 1 ping message every 5 seconds to signal to the ark service that we are still alive
|
||||||
|
// returns a function that can be used to stop the pinging
|
||||||
|
func ping(ctx *cli.Context, client arkv1.ArkServiceClient, req *arkv1.PingRequest) func() {
|
||||||
|
_, err := client.Ping(ctx.Context, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(5 * time.Second)
|
||||||
|
|
||||||
|
go func(t *time.Ticker) {
|
||||||
|
for range t.C {
|
||||||
|
// nolint
|
||||||
|
client.Ping(ctx.Context, req)
|
||||||
|
}
|
||||||
|
}(ticker)
|
||||||
|
|
||||||
|
return ticker.Stop
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ replace github.com/ark-network/ark => ../asp
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ark-network/ark v0.0.0-00010101000000-000000000000
|
github.com/ark-network/ark v0.0.0-00010101000000-000000000000
|
||||||
github.com/ark-network/ark/common v0.0.0-00010101000000-000000000000
|
github.com/ark-network/ark/common v0.0.0
|
||||||
github.com/btcsuite/btcd v0.23.4
|
github.com/btcsuite/btcd v0.23.4
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3
|
||||||
@@ -31,7 +31,7 @@ require (
|
|||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/vulpemventures/go-elements v0.5.1
|
github.com/vulpemventures/go-elements v0.5.2
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
golang.org/x/net v0.19.0 // indirect
|
golang.org/x/net v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
|
|||||||
@@ -82,8 +82,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
|||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
|
|
||||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
@@ -93,8 +91,8 @@ github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
|||||||
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
|
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
|
||||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
|
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
|
||||||
github.com/vulpemventures/go-elements v0.5.1 h1:F83n7dScOnAnpyH9VgWLdC68Qcva+2EWVzzNuW2UKak=
|
github.com/vulpemventures/go-elements v0.5.2 h1:vIDzVpRXG5PnlzHA8tCnr2Tn7raIV5cHy7bRyDrbuM4=
|
||||||
github.com/vulpemventures/go-elements v0.5.1/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
|
github.com/vulpemventures/go-elements v0.5.2/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
|
||||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
|
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
|
||||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
|
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
@@ -11,7 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/vulpemventures/go-elements/address"
|
"github.com/vulpemventures/go-elements/address"
|
||||||
"github.com/vulpemventures/go-elements/psetv2"
|
"github.com/vulpemventures/go-elements/psetv2"
|
||||||
@@ -132,6 +130,11 @@ func collaborativeRedeem(ctx *cli.Context, addr string, amount uint64) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secKey, err := privateKeyFromPassword()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
registerResponse, err := client.RegisterPayment(ctx.Context, &arkv1.RegisterPaymentRequest{
|
registerResponse, err := client.RegisterPayment(ctx.Context, &arkv1.RegisterPaymentRequest{
|
||||||
Inputs: inputs,
|
Inputs: inputs,
|
||||||
})
|
})
|
||||||
@@ -147,81 +150,21 @@ func collaborativeRedeem(ctx *cli.Context, addr string, amount uint64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stream, err := client.GetEventStream(ctx.Context, &arkv1.GetEventStreamRequest{})
|
poolTxID, err := handleRoundStream(
|
||||||
|
ctx,
|
||||||
|
client,
|
||||||
|
registerResponse.GetId(),
|
||||||
|
selectedCoins,
|
||||||
|
secKey,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pingStop func()
|
if err := printJSON(map[string]interface{}{
|
||||||
pingReq := &arkv1.PingRequest{
|
"pool_txid": poolTxID,
|
||||||
PaymentId: registerResponse.GetId(),
|
}); err != nil {
|
||||||
}
|
return err
|
||||||
for pingStop == nil {
|
|
||||||
pingStop = ping(ctx, client, pingReq)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
event, err := stream.Recv()
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.GetRoundFailed() != nil {
|
|
||||||
return fmt.Errorf("round failed: %s", event.GetRoundFailed().GetReason())
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.GetRoundFinalization() != nil {
|
|
||||||
// stop pinging as soon as we receive some forfeit txs
|
|
||||||
pingStop()
|
|
||||||
forfeits := event.GetRoundFinalization().GetForfeitTxs()
|
|
||||||
signedForfeits := make([]string, 0)
|
|
||||||
|
|
||||||
for _, forfeit := range forfeits {
|
|
||||||
pset, err := psetv2.NewPsetFromBase64(forfeit)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it contains one of the input to sign
|
|
||||||
for _, input := range pset.Inputs {
|
|
||||||
inputTxid := chainhash.Hash(input.PreviousTxid).String()
|
|
||||||
|
|
||||||
for _, coin := range selectedCoins {
|
|
||||||
if inputTxid == coin.txid {
|
|
||||||
// TODO: sign the vtxo input
|
|
||||||
signedForfeits = append(signedForfeits, forfeit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no forfeit txs have been signed, start pinging again and wait for the next round
|
|
||||||
if len(signedForfeits) == 0 {
|
|
||||||
pingStop = nil
|
|
||||||
for pingStop == nil {
|
|
||||||
pingStop = ping(ctx, client, pingReq)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := client.FinalizePayment(ctx.Context, &arkv1.FinalizePaymentRequest{
|
|
||||||
SignedForfeitTxs: signedForfeits,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.GetRoundFinalized() != nil {
|
|
||||||
return printJSON(map[string]interface{}{
|
|
||||||
"pool_txid": event.GetRoundFinalized().GetPoolTxid(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
111
noah/send.go
111
noah/send.go
@@ -4,14 +4,10 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||||
"github.com/ark-network/ark/common"
|
"github.com/ark-network/ark/common"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/vulpemventures/go-elements/psetv2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type receiver struct {
|
type receiver struct {
|
||||||
@@ -133,6 +129,11 @@ func sendAction(ctx *cli.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secKey, err := privateKeyFromPassword()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
registerResponse, err := client.RegisterPayment(ctx.Context, &arkv1.RegisterPaymentRequest{
|
registerResponse, err := client.RegisterPayment(ctx.Context, &arkv1.RegisterPaymentRequest{
|
||||||
Inputs: inputs,
|
Inputs: inputs,
|
||||||
})
|
})
|
||||||
@@ -148,102 +149,22 @@ func sendAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stream, err := client.GetEventStream(ctx.Context, &arkv1.GetEventStreamRequest{})
|
poolTxID, err := handleRoundStream(
|
||||||
|
ctx,
|
||||||
|
client,
|
||||||
|
registerResponse.GetId(),
|
||||||
|
selectedCoins,
|
||||||
|
secKey,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pingStop func()
|
if err := printJSON(map[string]interface{}{
|
||||||
pingReq := &arkv1.PingRequest{
|
"pool_txid": poolTxID,
|
||||||
PaymentId: registerResponse.GetId(),
|
}); err != nil {
|
||||||
}
|
return err
|
||||||
for pingStop == nil {
|
|
||||||
pingStop = ping(ctx, client, pingReq)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
event, err := stream.Recv()
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.GetRoundFailed() != nil {
|
|
||||||
return fmt.Errorf("round failed: %s", event.GetRoundFailed().GetReason())
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.GetRoundFinalization() != nil {
|
|
||||||
// stop pinging as soon as we receive some forfeit txs
|
|
||||||
pingStop()
|
|
||||||
forfeits := event.GetRoundFinalization().GetForfeitTxs()
|
|
||||||
signedForfeits := make([]string, 0)
|
|
||||||
|
|
||||||
for _, forfeit := range forfeits {
|
|
||||||
pset, err := psetv2.NewPsetFromBase64(forfeit)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it contains one of the input to sign
|
|
||||||
for _, input := range pset.Inputs {
|
|
||||||
inputTxid := chainhash.Hash(input.PreviousTxid).String()
|
|
||||||
|
|
||||||
for _, coin := range selectedCoins {
|
|
||||||
if inputTxid == coin.txid {
|
|
||||||
// TODO: sign the vtxo input
|
|
||||||
signedForfeits = append(signedForfeits, forfeit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no forfeit txs have been signed, start pinging again and wait for the next round
|
|
||||||
if len(signedForfeits) == 0 {
|
|
||||||
pingStop = nil
|
|
||||||
for pingStop == nil {
|
|
||||||
pingStop = ping(ctx, client, pingReq)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := client.FinalizePayment(ctx.Context, &arkv1.FinalizePaymentRequest{
|
|
||||||
SignedForfeitTxs: signedForfeits,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.GetRoundFinalized() != nil {
|
|
||||||
return printJSON(map[string]interface{}{
|
|
||||||
"pool_txid": event.GetRoundFinalized().GetPoolTxid(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// send 1 ping message every 5 seconds to signal to the ark service that we are still alive
|
|
||||||
// returns a function that can be used to stop the pinging
|
|
||||||
func ping(ctx *cli.Context, client arkv1.ArkServiceClient, req *arkv1.PingRequest) func() {
|
|
||||||
_, err := client.Ping(ctx.Context, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
|
||||||
|
|
||||||
go func(t *time.Ticker) {
|
|
||||||
for range t.C {
|
|
||||||
// nolint
|
|
||||||
client.Ping(ctx.Context, req)
|
|
||||||
}
|
|
||||||
}(ticker)
|
|
||||||
|
|
||||||
return ticker.Stop
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -125,10 +125,7 @@ func signPset(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pubkey, err := getWalletPublicKey()
|
pubkey := prvKey.PubKey()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
vtxoLeaf, err := common.VtxoScript(pubkey)
|
vtxoLeaf, err := common.VtxoScript(pubkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -174,8 +171,6 @@ func signPset(
|
|||||||
if err := signer.SignTaprootInputTapscriptSig(i, tapScriptSig); err != nil {
|
if err := signer.SignTaprootInputTapscriptSig(i, tapScriptSig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user