@@ -67,6 +67,56 @@ func createFundedPacketWithInputs(ctx context.Context, exporter proof.Exporter,
67
67
return nil , err
68
68
}
69
69
70
+ // Make sure we'll recognize local script keys in the virtual packet
71
+ // later on in the process by annotating them with the full descriptor
72
+ // information.
73
+ if err := annotateLocalScriptKeys (ctx , vPkt , addrBook ); err != nil {
74
+ return nil , err
75
+ }
76
+
77
+ // If we don't spend the full value, we need to create a change output.
78
+ changeAmount := totalInputAmt - fundDesc .Amount
79
+ err = createChangeOutput (ctx , vPkt , keyRing , fullValue , changeAmount )
80
+ if err != nil {
81
+ return nil , err
82
+ }
83
+
84
+ // Before we can prepare output assets for our send, we need to generate
85
+ // a new internal key for the anchor outputs. We assume any output that
86
+ // hasn't got an internal key set is going to a local anchor, and we
87
+ // provide the internal key for that.
88
+ for idx := range vPkt .Outputs {
89
+ vOut := vPkt .Outputs [idx ]
90
+ if vOut .AnchorOutputInternalKey != nil {
91
+ continue
92
+ }
93
+
94
+ newInternalKey , err := keyRing .DeriveNextKey (
95
+ ctx , asset .TaprootAssetsKeyFamily ,
96
+ )
97
+ if err != nil {
98
+ return nil , err
99
+ }
100
+ vOut .SetAnchorInternalKey (
101
+ newInternalKey , vPkt .ChainParams .HDCoinType ,
102
+ )
103
+ }
104
+
105
+ if err := tapsend .PrepareOutputAssets (ctx , vPkt ); err != nil {
106
+ return nil , fmt .Errorf ("unable to prepare outputs: %w" , err )
107
+ }
108
+
109
+ return & FundedVPacket {
110
+ VPackets : []* tappsbt.VPacket {vPkt },
111
+ InputCommitments : inputCommitments ,
112
+ }, nil
113
+ }
114
+
115
+ // annotateLocalScriptKeys annotates the local script keys in the given virtual
116
+ // packet with the full descriptor information.
117
+ func annotateLocalScriptKeys (ctx context.Context , vPkt * tappsbt.VPacket ,
118
+ addrBook AddrBook ) error {
119
+
70
120
// We want to know if we are sending to ourselves. We detect that by
71
121
// looking at the key descriptor of the script key. Because that is not
72
122
// part of addresses and might not be specified by the user through the
@@ -90,14 +140,24 @@ func createFundedPacketWithInputs(ctx context.Context, exporter proof.Exporter,
90
140
continue
91
141
92
142
default :
93
- return nil , fmt .Errorf ("cannot fetch script key: %w" ,
94
- err )
143
+ return fmt .Errorf ("cannot fetch script key: %w" , err )
95
144
}
96
145
}
97
146
147
+ return nil
148
+ }
149
+
150
+ // createChangeOutput creates a change output for the given virtual packet if
151
+ // it isn't fully spent.
152
+ func createChangeOutput (ctx context.Context , vPkt * tappsbt.VPacket ,
153
+ keyRing KeyRing , fullValue bool , changeAmount uint64 ) error {
154
+
98
155
// We expect some change back, or have passive assets to commit to, so
99
156
// let's make sure we create a transfer output.
100
- var changeOut * tappsbt.VOutput
157
+ var (
158
+ changeOut * tappsbt.VOutput
159
+ err error
160
+ )
101
161
if ! fullValue {
102
162
// Do we need to add a change output?
103
163
changeOut , err = vPkt .SplitRootOutput ()
@@ -127,15 +187,15 @@ func createFundedPacketWithInputs(ctx context.Context, exporter proof.Exporter,
127
187
// to attach the passive assets to.
128
188
unSpendable , err := changeOut .ScriptKey .IsUnSpendable ()
129
189
if err != nil {
130
- return nil , fmt .Errorf ("cannot determine if script " +
131
- "key is spendable: %w" , err )
190
+ return fmt .Errorf ("cannot determine if script key is " +
191
+ "spendable: %w" , err )
132
192
}
133
- if unSpendable && ! fullValue {
193
+ if unSpendable {
134
194
changeScriptKey , err := keyRing .DeriveNextKey (
135
195
ctx , asset .TaprootAssetsKeyFamily ,
136
196
)
137
197
if err != nil {
138
- return nil , err
198
+ return err
139
199
}
140
200
141
201
// We'll assume BIP-0086 everywhere, and use the tweaked
@@ -148,7 +208,7 @@ func createFundedPacketWithInputs(ctx context.Context, exporter proof.Exporter,
148
208
// For existing change outputs, we'll just update the amount
149
209
// since we might not have known what coin would've been
150
210
// selected and how large the change would turn out to be.
151
- changeOut .Amount = totalInputAmt - fundDesc . Amount
211
+ changeOut .Amount = changeAmount
152
212
153
213
// The asset version of the output should be the max of the set
154
214
// of input versions. We need to set this now as in
@@ -170,35 +230,7 @@ func createFundedPacketWithInputs(ctx context.Context, exporter proof.Exporter,
170
230
changeOut .AssetVersion = fn .Reduce (vPkt .Inputs , maxVersion )
171
231
}
172
232
173
- // Before we can prepare output assets for our send, we need to generate
174
- // a new internal key for the anchor outputs. We assume any output that
175
- // hasn't got an internal key set is going to a local anchor, and we
176
- // provide the internal key for that.
177
- for idx := range vPkt .Outputs {
178
- vOut := vPkt .Outputs [idx ]
179
- if vOut .AnchorOutputInternalKey != nil {
180
- continue
181
- }
182
-
183
- newInternalKey , err := keyRing .DeriveNextKey (
184
- ctx , asset .TaprootAssetsKeyFamily ,
185
- )
186
- if err != nil {
187
- return nil , err
188
- }
189
- vOut .SetAnchorInternalKey (
190
- newInternalKey , vPkt .ChainParams .HDCoinType ,
191
- )
192
- }
193
-
194
- if err := tapsend .PrepareOutputAssets (ctx , vPkt ); err != nil {
195
- return nil , fmt .Errorf ("unable to prepare outputs: %w" , err )
196
- }
197
-
198
- return & FundedVPacket {
199
- VPackets : []* tappsbt.VPacket {vPkt },
200
- InputCommitments : inputCommitments ,
201
- }, nil
233
+ return nil
202
234
}
203
235
204
236
// setVPacketInputs sets the inputs of the given vPkt to the given send eligible
0 commit comments