Skip to content

Commit

Permalink
Merge pull request lightninglabs#730 from lightninglabs/proof-courier…
Browse files Browse the repository at this point in the history
…-shortcut

[2/2] proof: allow proof courier to short cut with local archive
  • Loading branch information
Roasbeef authored Feb 6, 2024
2 parents 1ca17f6 + ca77ce5 commit 99b8f86
Show file tree
Hide file tree
Showing 44 changed files with 2,350 additions and 1,179 deletions.
52 changes: 52 additions & 0 deletions asset/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import (
"io"
"reflect"
"strings"
"time"
"unicode"
"unicode/utf8"

"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient"
Expand Down Expand Up @@ -1621,3 +1623,53 @@ func ValidateAssetName(name string) error {

return nil
}

// ChainAsset is a wrapper around the base asset struct that includes
// information detailing where in the chain the asset is currently anchored.
type ChainAsset struct {
*Asset

// IsSpent indicates whether the above asset was previously spent.
IsSpent bool

// AnchorTx is the transaction that anchors this chain asset.
AnchorTx *wire.MsgTx

// AnchorBlockHash is the blockhash that mined the anchor tx.
AnchorBlockHash chainhash.Hash

// AnchorBlockHeight is the height of the block that mined the anchor
// tx.
AnchorBlockHeight uint32

// AnchorOutpoint is the outpoint that commits to the asset.
AnchorOutpoint wire.OutPoint

// AnchorInternalKey is the raw internal key that was used to create the
// anchor Taproot output key.
AnchorInternalKey *btcec.PublicKey

// AnchorMerkleRoot is the Taproot merkle root hash of the anchor output
// the asset was committed to. If there is no Tapscript sibling, this is
// equal to the Taproot Asset root commitment hash.
AnchorMerkleRoot []byte

// AnchorTapscriptSibling is the serialized preimage of a Tapscript
// sibling, if there was one. If this is empty, then the
// AnchorTapscriptSibling hash is equal to the Taproot root hash of the
// anchor output.
AnchorTapscriptSibling []byte

// AnchorLeaseOwner is the identity of the application that currently
// has a lease on this UTXO. If empty/nil, then the UTXO is not
// currently leased. A lease means that the UTXO is being
// reserved/locked to be spent in an upcoming transaction and that it
// should not be available for coin selection through any of the wallet
// RPCs.
AnchorLeaseOwner [32]byte

// AnchorLeaseExpiry is the expiry of the lease. If the expiry is nil or
// the time is in the past, then the lease is not valid and the UTXO is
// available for coin selection.
AnchorLeaseExpiry *time.Time
}
3 changes: 2 additions & 1 deletion cmd/tapcli/universe.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/hex"
"fmt"

"github.com/btcsuite/btcd/wire"
tap "github.com/lightninglabs/taproot-assets"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/proof"
Expand Down Expand Up @@ -456,7 +457,7 @@ func parseAssetKey(ctx *cli.Context) (*unirpc.AssetKey, error) {
return nil, fmt.Errorf("outpoint and script key must be set")
}

outpoint, err := tap.UnmarshalOutpoint(ctx.String(outpointName))
outpoint, err := wire.NewOutPointFromString(ctx.String(outpointName))
if err != nil {
return nil, err
}
Expand Down
21 changes: 20 additions & 1 deletion itest/addrs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"

"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/wire"
tap "github.com/lightninglabs/taproot-assets"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/internal/test"
Expand Down Expand Up @@ -515,16 +516,34 @@ func runMultiSendTest(ctxt context.Context, t *harnessTest, alice,

// sendProof manually exports a proof from the given source node and imports it
// using the development only ImportProof RPC on the destination node.
func sendProof(t *harnessTest, src, dst *tapdHarness, scriptKey []byte,
func sendProof(t *harnessTest, src, dst *tapdHarness,
sendResp *taprpc.SendAssetResponse, scriptKey []byte,
genInfo *taprpc.GenesisInfo) *tapdevrpc.ImportProofResponse {

ctxb := context.Background()

// We need to find the outpoint of the asset we sent to the address.
var outpoint *taprpc.OutPoint
for _, out := range sendResp.Transfer.Outputs {
if bytes.Equal(out.ScriptKey, scriptKey) {
wireOutPoint, err := wire.NewOutPointFromString(
out.Anchor.Outpoint,
)
require.NoError(t.t, err)

outpoint = &taprpc.OutPoint{
Txid: wireOutPoint.Hash[:],
OutputIndex: wireOutPoint.Index,
}
}
}

var proofResp *taprpc.ProofFile
waitErr := wait.NoError(func() error {
resp, err := src.ExportProof(ctxb, &taprpc.ExportProofRequest{
AssetId: genInfo.AssetId,
ScriptKey: scriptKey,
Outpoint: outpoint,
})
if err != nil {
return err
Expand Down
18 changes: 9 additions & 9 deletions itest/psbt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func testPsbtScriptHashLockSend(t *harnessTest) {
// This is an interactive/PSBT based transfer, so we do need to manually
// send the proof from the sender to the receiver because the proof
// courier address gets lost in the address->PSBT conversion.
_ = sendProof(t, bob, alice, aliceAddr.ScriptKey, genInfo)
_ = sendProof(t, bob, alice, sendResp, aliceAddr.ScriptKey, genInfo)
AssertNonInteractiveRecvComplete(t.t, alice, 1)

aliceAssets, err := alice.ListAssets(ctxb, &taprpc.ListAssetRequest{
Expand Down Expand Up @@ -258,7 +258,7 @@ func testPsbtScriptCheckSigSend(t *harnessTest) {
// This is an interactive/PSBT based transfer, so we do need to manually
// send the proof from the sender to the receiver because the proof
// courier address gets lost in the address->PSBT conversion.
_ = sendProof(t, bob, alice, aliceAddr.ScriptKey, genInfo)
_ = sendProof(t, bob, alice, sendResp, aliceAddr.ScriptKey, genInfo)
AssertNonInteractiveRecvComplete(t.t, alice, 1)

aliceAssets, err := alice.ListAssets(ctxb, &taprpc.ListAssetRequest{
Expand Down Expand Up @@ -434,7 +434,7 @@ func runPsbtInteractiveFullValueSendTest(ctxt context.Context, t *harnessTest,
// This is an interactive transfer, so we do need to manually
// send the proof from the sender to the receiver.
_ = sendProof(
t, sender, receiver,
t, sender, receiver, sendResp,
receiverScriptKey.PubKey.SerializeCompressed(), genInfo,
)

Expand Down Expand Up @@ -647,7 +647,7 @@ func runPsbtInteractiveSplitSendTest(ctxt context.Context, t *harnessTest,
// This is an interactive transfer, so we do need to manually
// send the proof from the sender to the receiver.
_ = sendProof(
t, sender, receiver,
t, sender, receiver, sendResp,
receiverScriptKey.PubKey.SerializeCompressed(), genInfo,
)

Expand Down Expand Up @@ -769,7 +769,7 @@ func testPsbtInteractiveTapscriptSibling(t *harnessTest) {
// This is an interactive transfer, so we do need to manually send the
// proof from the sender to the receiver.
_ = sendProof(
t, alice, bob,
t, alice, bob, sendResp,
receiverScriptKey.PubKey.SerializeCompressed(), genInfo,
)

Expand Down Expand Up @@ -916,11 +916,11 @@ func testPsbtMultiSend(t *harnessTest) {
// This is an interactive transfer, so we do need to manually send the
// proof from the sender to the receiver.
_ = sendProof(
t, sender, receiver,
t, sender, receiver, sendResp,
receiverScriptKey1.PubKey.SerializeCompressed(), genInfo,
)
_ = sendProof(
t, sender, receiver,
t, sender, receiver, sendResp,
receiverScriptKey2.PubKey.SerializeCompressed(), genInfo,
)

Expand Down Expand Up @@ -1158,7 +1158,7 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
// This is an interactive transfer. Therefore, we will manually transfer
// the proof from the sender to the receiver.
_ = sendProof(
t, secondaryTapd, primaryTapd,
t, secondaryTapd, primaryTapd, sendResp,
primaryNodeScriptKey.PubKey.SerializeCompressed(), genInfo,
)

Expand Down Expand Up @@ -1233,7 +1233,7 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
// This is an interactive transfer. Therefore, we will manually transfer
// the proof from the sender to the receiver.
_ = sendProof(
t, secondaryTapd, primaryTapd,
t, secondaryTapd, primaryTapd, sendResp,
primaryNodeScriptKey.PubKey.SerializeCompressed(), genInfo,
)

Expand Down
2 changes: 1 addition & 1 deletion itest/round_trip_send_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func testRoundTripSend(t *harnessTest) {
// recipient's output is the second one.
bobToAliceOutput := transferResp.Transfers[0].Outputs[1]
bobToAliceAnchor := bobToAliceOutput.Anchor
outpoint, err := ParseOutPoint(bobToAliceAnchor.Outpoint)
outpoint, err := wire.NewOutPointFromString(bobToAliceAnchor.Outpoint)
require.NoError(t.t, err)

internalKey, err := btcec.ParsePubKey(bobToAliceAnchor.InternalKey)
Expand Down
12 changes: 7 additions & 5 deletions itest/test_list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ var testCases = []*testCase{
test: testReOrgMintAndSend,
},
{
name: "basic send unidirectional",
name: "basic send unidirectional hashmail courier",
test: testBasicSendUnidirectional,
proofCourierType: proof.HashmailCourierType,
},
Expand All @@ -54,7 +54,8 @@ var testCases = []*testCase{
test: testRestartReceiverCheckBalance,
},
{
name: "resume pending package send",
name: "resume pending package send hashmail " +
"courier",
test: testResumePendingPackageSend,
proofCourierType: proof.HashmailCourierType,
},
Expand All @@ -72,7 +73,8 @@ var testCases = []*testCase{
test: testReattemptFailedReceiveUniCourier,
},
{
name: "offline receiver eventually receives",
name: "offline receiver eventually receives " +
"hashmail courier",
test: testOfflineReceiverEventuallyReceives,
proofCourierType: proof.HashmailCourierType,
},
Expand All @@ -81,7 +83,7 @@ var testCases = []*testCase{
test: testSendNoCourierUniverseImport,
},
{
name: "basic send passive asset",
name: "basic send passive asset hashmail courier",
test: testBasicSendPassiveAsset,
proofCourierType: proof.HashmailCourierType,
},
Expand Down Expand Up @@ -123,7 +125,7 @@ var testCases = []*testCase{
test: testMintMultiAssetGroups,
},
{
name: "sending multi asset groups",
name: "sending multi asset groups hashmail courier",
test: testMultiAssetGroupSend,
proofCourierType: proof.HashmailCourierType,
},
Expand Down
5 changes: 3 additions & 2 deletions itest/universe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/wire"
tap "github.com/lightninglabs/taproot-assets"
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/fn"
Expand Down Expand Up @@ -150,7 +151,7 @@ func testUniverseSync(t *harnessTest) {
// query for that asset with the compressed script key.
firstAssetID := rpcSimpleAssets[0].AssetGenesis.AssetId
firstScriptKey := hex.EncodeToString(rpcSimpleAssets[0].ScriptKey)
firstOutpoint, err := tap.UnmarshalOutpoint(
firstOutpoint, err := wire.NewOutPointFromString(
rpcSimpleAssets[0].ChainAnchor.AnchorOutpoint,
)
require.NoError(t.t, err)
Expand Down Expand Up @@ -326,7 +327,7 @@ func testUniverseManualSync(t *harnessTest) {

// We should also be able to fetch an asset from Bob's Universe, and
// query for that asset with the compressed script key.
firstOutpoint, err := tap.UnmarshalOutpoint(
firstOutpoint, err := wire.NewOutPointFromString(
firstAsset.ChainAnchor.AnchorOutpoint,
)
require.NoError(t.t, err)
Expand Down
29 changes: 1 addition & 28 deletions itest/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package itest

import (
"context"
"fmt"
"strconv"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -48,34 +45,10 @@ func CopyRequests(
return copied
}

// ParseOutPoint
func ParseOutPoint(s string) (*wire.OutPoint, error) {
split := strings.Split(s, ":")
if len(split) != 2 {
return nil, fmt.Errorf("expecting outpoint to be in format " +
"of: txid:index")
}

index, err := strconv.ParseInt(split[1], 10, 32)
if err != nil {
return nil, fmt.Errorf("unable to decode output index: %v", err)
}

txid, err := chainhash.NewHashFromStr(split[0])
if err != nil {
return nil, fmt.Errorf("unable to parse hex string: %v", err)
}

return &wire.OutPoint{
Hash: *txid,
Index: uint32(index),
}, nil
}

// ParseGenInfo converts a taprpc.GenesisInfo into its asset.Genesis
// counterpart.
func ParseGenInfo(t *testing.T, genInfo *taprpc.GenesisInfo) *asset.Genesis {
genPoint, err := ParseOutPoint(genInfo.GenesisPoint)
genPoint, err := wire.NewOutPointFromString(genInfo.GenesisPoint)
require.NoError(t, err)

parsedGenesis := asset.Genesis{
Expand Down
Loading

0 comments on commit 99b8f86

Please sign in to comment.