Skip to content

Commit c0dddb1

Browse files
committed
Merge branch 'v0-3-3-branch-726' into v0-3-3-branch
2 parents 62991a1 + d34256d commit c0dddb1

20 files changed

+1017
-99
lines changed

fn/func.go

+50
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@ func All[T any](xs []T, pred func(T) bool) bool {
130130
return true
131131
}
132132

133+
// AllMapItems returns true if the passed predicate returns true for all items
134+
// in the map.
135+
func AllMapItems[T any, K comparable](xs map[K]T, pred func(T) bool) bool {
136+
for i := range xs {
137+
if !pred(xs[i]) {
138+
return false
139+
}
140+
}
141+
142+
return true
143+
}
144+
133145
// Any returns true if the passed predicate returns true for any item in the
134146
// slice.
135147
func Any[T any](xs []T, pred func(T) bool) bool {
@@ -148,6 +160,30 @@ func None[T any](xs []T, pred func(T) bool) bool {
148160
return !Any(xs, pred)
149161
}
150162

163+
// AnyMapItem returns true if the passed predicate returns true for any item in
164+
// the map.
165+
func AnyMapItem[T any, K comparable](xs map[K]T, pred func(T) bool) bool {
166+
for i := range xs {
167+
if pred(xs[i]) {
168+
return true
169+
}
170+
}
171+
172+
return false
173+
}
174+
175+
// NotAny returns true if the passed predicate returns false for all items in
176+
// the slice.
177+
func NotAny[T any](xs []T, pred func(T) bool) bool {
178+
return !Any(xs, pred)
179+
}
180+
181+
// NotAnyMapItem returns true if the passed predicate returns false for all
182+
// items in the map.
183+
func NotAnyMapItem[T any, K comparable](xs map[K]T, pred func(T) bool) bool {
184+
return !AnyMapItem(xs, pred)
185+
}
186+
151187
// Count returns the number of items in the slice that match the predicate.
152188
func Count[T any](xs []T, pred func(T) bool) int {
153189
var count int
@@ -161,6 +197,20 @@ func Count[T any](xs []T, pred func(T) bool) int {
161197
return count
162198
}
163199

200+
// CountMapItems returns the number of items in the map that match the
201+
// predicate.
202+
func CountMapItems[T any, K comparable](xs map[K]T, pred func(T) bool) int {
203+
var count int
204+
205+
for i := range xs {
206+
if pred(xs[i]) {
207+
count++
208+
}
209+
}
210+
211+
return count
212+
}
213+
164214
// First returns the first item in the slice that matches the predicate, or an
165215
// error if none matches.
166216
func First[T any](xs []*T, pred func(*T) bool) (*T, error) {

fn/iter.go

+21-7
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ package fn
77
// This function can be used instead of the normal range loop to ensure that a
88
// loop scoping bug isn't introduced.
99
func ForEachErr[T any](s []T, f func(T) error) error {
10-
for _, item := range s {
11-
item := item
12-
13-
if err := f(item); err != nil {
10+
for i := range s {
11+
if err := f(s[i]); err != nil {
1412
return err
1513
}
1614
}
@@ -22,9 +20,17 @@ func ForEachErr[T any](s []T, f func(T) error) error {
2220
// This can be used to ensure that any normal for-loop don't run into bugs due
2321
// to loop variable scoping.
2422
func ForEach[T any](items []T, f func(T)) {
25-
for _, item := range items {
26-
item := item
27-
f(item)
23+
for i := range items {
24+
f(items[i])
25+
}
26+
}
27+
28+
// ForEachMapItem is a generic implementation of a for-each (map with side
29+
// effects). This can be used to ensure that any normal for-loop don't run into
30+
// bugs due to loop variable scoping.
31+
func ForEachMapItem[T any, K comparable](items map[K]T, f func(T)) {
32+
for i := range items {
33+
f(items[i])
2834
}
2935
}
3036

@@ -38,6 +44,14 @@ func Enumerate[T any](items []T, f func(int, T)) {
3844
}
3945
}
4046

47+
// EnumerateMap is a generic enumeration function. The closure will be called
48+
// for each key and item in the passed-in map.
49+
func EnumerateMap[T any, K comparable](items map[K]T, f func(K, T)) {
50+
for key := range items {
51+
f(key, items[key])
52+
}
53+
}
54+
4155
// MakeSlice is a generic function shorthand for making a slice out of a set
4256
// of elements. This can be used to avoid having to specify the type of the
4357
// slice as well as the types of the elements.

itest/addrs_test.go

+82
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
tap "github.com/lightninglabs/taproot-assets"
99
"github.com/lightninglabs/taproot-assets/fn"
1010
"github.com/lightninglabs/taproot-assets/internal/test"
11+
"github.com/lightninglabs/taproot-assets/proof"
1112
"github.com/lightninglabs/taproot-assets/tappsbt"
1213
"github.com/lightninglabs/taproot-assets/taprpc"
1314
wrpc "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"
@@ -512,6 +513,8 @@ func runMultiSendTest(ctxt context.Context, t *harnessTest, alice,
512513
require.NoError(t.t, err)
513514
}
514515

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

@@ -543,6 +546,85 @@ func sendProof(t *harnessTest, src, dst *tapdHarness, scriptKey []byte,
543546
return importResp
544547
}
545548

549+
// sendProofUniRPC manually exports a proof from the given source node and
550+
// imports it using the universe related InsertProof RPC on the destination
551+
// node.
552+
func sendProofUniRPC(t *harnessTest, src, dst *tapdHarness, scriptKey []byte,
553+
genInfo *taprpc.GenesisInfo) *unirpc.AssetProofResponse {
554+
555+
ctxb := context.Background()
556+
557+
var proofResp *taprpc.ProofFile
558+
waitErr := wait.NoError(func() error {
559+
resp, err := src.ExportProof(ctxb, &taprpc.ExportProofRequest{
560+
AssetId: genInfo.AssetId,
561+
ScriptKey: scriptKey,
562+
})
563+
if err != nil {
564+
return err
565+
}
566+
567+
proofResp = resp
568+
return nil
569+
}, defaultWaitTimeout)
570+
require.NoError(t.t, waitErr)
571+
572+
t.Logf("Importing proof %x using InsertProof", proofResp.RawProofFile)
573+
574+
f := proof.File{}
575+
err := f.Decode(bytes.NewReader(proofResp.RawProofFile))
576+
require.NoError(t.t, err)
577+
578+
lastProof, err := f.LastProof()
579+
require.NoError(t.t, err)
580+
581+
var lastProofBytes bytes.Buffer
582+
err = lastProof.Encode(&lastProofBytes)
583+
require.NoError(t.t, err)
584+
asset := lastProof.Asset
585+
586+
proofType := universe.ProofTypeTransfer
587+
if asset.IsGenesisAsset() {
588+
proofType = universe.ProofTypeIssuance
589+
}
590+
591+
uniID := universe.Identifier{
592+
AssetID: asset.ID(),
593+
ProofType: proofType,
594+
}
595+
if asset.GroupKey != nil {
596+
uniID.GroupKey = &asset.GroupKey.GroupPubKey
597+
}
598+
599+
rpcUniID, err := tap.MarshalUniID(uniID)
600+
require.NoError(t.t, err)
601+
602+
outpoint := &unirpc.Outpoint{
603+
HashStr: lastProof.AnchorTx.TxHash().String(),
604+
Index: int32(lastProof.InclusionProof.OutputIndex),
605+
}
606+
607+
importResp, err := dst.InsertProof(ctxb, &unirpc.AssetProof{
608+
Key: &unirpc.UniverseKey{
609+
Id: rpcUniID,
610+
LeafKey: &unirpc.AssetKey{
611+
Outpoint: &unirpc.AssetKey_Op{
612+
Op: outpoint,
613+
},
614+
ScriptKey: &unirpc.AssetKey_ScriptKeyBytes{
615+
ScriptKeyBytes: scriptKey,
616+
},
617+
},
618+
},
619+
AssetLeaf: &unirpc.AssetLeaf{
620+
Proof: lastProofBytes.Bytes(),
621+
},
622+
})
623+
require.NoError(t.t, err)
624+
625+
return importResp
626+
}
627+
546628
// sendAssetsToAddr spends the given input asset and sends the amount specified
547629
// in the address to the Taproot output derived from the address.
548630
func sendAssetsToAddr(t *harnessTest, sender *tapdHarness,

itest/send_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,67 @@ func testSendMultipleCoins(t *harnessTest) {
13741374
AssertNonInteractiveRecvComplete(t.t, secondTapd, 5)
13751375
}
13761376

1377+
// testSendNoCourierUniverseImport tests that we can send assets to a node that
1378+
// has no courier, and then manually transfer the proof to the receiving using
1379+
// the universe proof import RPC method.
1380+
func testSendNoCourierUniverseImport(t *harnessTest) {
1381+
ctxb := context.Background()
1382+
1383+
// First, we'll make a normal assets with enough units.
1384+
rpcAssets := MintAssetsConfirmBatch(
1385+
t.t, t.lndHarness.Miner.Client, t.tapd,
1386+
[]*mintrpc.MintAssetRequest{simpleAssets[0]},
1387+
)
1388+
1389+
firstAsset := rpcAssets[0]
1390+
genInfo := firstAsset.AssetGenesis
1391+
1392+
// Now that we have the asset created, we'll make a new node that'll
1393+
// serve as the node which'll receive the assets. We turn off the proof
1394+
// courier by supplying a dummy implementation.
1395+
secondTapd := setupTapdHarness(
1396+
t.t, t, t.lndHarness.Bob, t.universeServer,
1397+
func(params *tapdHarnessParams) {
1398+
params.proofCourier = &proof.MockProofCourier{}
1399+
},
1400+
)
1401+
defer func() {
1402+
require.NoError(t.t, secondTapd.stop(!*noDelete))
1403+
}()
1404+
1405+
// Next, we'll attempt to transfer some amount of assets[0] to the
1406+
// receiving node.
1407+
numUnitsSend := uint64(1200)
1408+
1409+
// Get a new address (which accepts the first asset) from the
1410+
// receiving node.
1411+
receiveAddr, err := secondTapd.NewAddr(ctxb, &taprpc.NewAddrRequest{
1412+
AssetId: genInfo.AssetId,
1413+
Amt: numUnitsSend,
1414+
})
1415+
require.NoError(t.t, err)
1416+
AssertAddrCreated(t.t, secondTapd, firstAsset, receiveAddr)
1417+
1418+
// Send the assets to the receiving node.
1419+
sendResp := sendAssetsToAddr(t, t.tapd, receiveAddr)
1420+
1421+
// Assert that the outbound transfer was confirmed.
1422+
expectedAmtAfterSend := firstAsset.Amount - numUnitsSend
1423+
ConfirmAndAssertOutboundTransfer(
1424+
t.t, t.lndHarness.Miner.Client, t.tapd, sendResp,
1425+
genInfo.AssetId,
1426+
[]uint64{expectedAmtAfterSend, numUnitsSend}, 0, 1,
1427+
)
1428+
1429+
// Since we disabled proof couriers, we need to manually transfer the
1430+
// proof from the sender to the receiver now. We use the universe RPC
1431+
// InsertProof method to do this.
1432+
sendProofUniRPC(t, t.tapd, secondTapd, receiveAddr.ScriptKey, genInfo)
1433+
1434+
// And now, the transfer should be completed on the receiver side too.
1435+
AssertNonInteractiveRecvComplete(t.t, secondTapd, 1)
1436+
}
1437+
13771438
// addProofTestVectorFromFile adds a proof test vector by extracting it from the
13781439
// proof file found at the given asset ID and script key.
13791440
func addProofTestVectorFromFile(t *testing.T, testName string,

itest/test_list_on_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ var testCases = []*testCase{
7676
test: testOfflineReceiverEventuallyReceives,
7777
proofCourierType: proof.HashmailCourierType,
7878
},
79+
{
80+
name: "addr send no proof courier with local universe import",
81+
test: testSendNoCourierUniverseImport,
82+
},
7983
{
8084
name: "basic send passive asset",
8185
test: testBasicSendPassiveAsset,
@@ -175,6 +179,10 @@ var testCases = []*testCase{
175179
name: "universe sync",
176180
test: testUniverseSync,
177181
},
182+
{
183+
name: "universe sync manual insert",
184+
test: testUniverseManualSync,
185+
},
178186
{
179187
name: "universe federation",
180188
test: testUniverseFederation,

0 commit comments

Comments
 (0)