|
5 | 5 | "context" |
6 | 6 | "errors" |
7 | 7 | "fmt" |
| 8 | + "net/url" |
8 | 9 | "strings" |
9 | 10 | "sync" |
10 | 11 | "time" |
@@ -1093,13 +1094,138 @@ func (p *ChainPorter) importLocalAddresses(ctx context.Context, |
1093 | 1094 | return nil |
1094 | 1095 | } |
1095 | 1096 |
|
| 1097 | +// pingCourier attempts to establish a connection to the given proof courier |
| 1098 | +// address. If the connection is successful, the courier is closed and the |
| 1099 | +// function returns nil. If the connection fails, an error is returned. |
| 1100 | +// This function is blocking. |
| 1101 | +func (p *ChainPorter) pingCourier(ctx context.Context, addr url.URL) error { |
| 1102 | + log.Debugf("Attempting to ping proof courier (addr=%s)", addr.String()) |
| 1103 | + |
| 1104 | + // Connect to the proof courier service with an eager (non-lazy) |
| 1105 | + // connection attempt, blocking until the connection either succeeds, |
| 1106 | + // fails, or times out. |
| 1107 | + courier, err := p.cfg.ProofCourierDispatcher.NewCourier( |
| 1108 | + ctx, &addr, false, |
| 1109 | + ) |
| 1110 | + if err != nil { |
| 1111 | + return fmt.Errorf("unable to initiate proof courier "+ |
| 1112 | + "service handle (addr=%s): %w", addr.String(), err) |
| 1113 | + } |
| 1114 | + |
| 1115 | + return courier.Close() |
| 1116 | +} |
| 1117 | + |
| 1118 | +// pingProofCouriers performs a blocking connectivity check for each applicable |
| 1119 | +// proof courier. |
| 1120 | +func (p *ChainPorter) pingProofCouriers(proofCourierAddrs []url.URL) error { |
| 1121 | + // Construct minimal set of unique proof couriers to ping. |
| 1122 | + var couriers []url.URL |
| 1123 | + |
| 1124 | + for idx := range proofCourierAddrs { |
| 1125 | + addr := proofCourierAddrs[idx] |
| 1126 | + |
| 1127 | + // Check if the address is a duplicate (already in the list of |
| 1128 | + // couriers). |
| 1129 | + for i := range couriers { |
| 1130 | + if addr.String() == couriers[i].String() { |
| 1131 | + // Skip duplicate addresses. |
| 1132 | + continue |
| 1133 | + } |
| 1134 | + } |
| 1135 | + |
| 1136 | + couriers = append(couriers, addr) |
| 1137 | + } |
| 1138 | + |
| 1139 | + // Ping each proof courier in parallel to ensure they are reachable. |
| 1140 | + ctx, cancel := p.WithCtxQuit() |
| 1141 | + defer cancel() |
| 1142 | + instanceErrors, err := fn.ParSliceErrCollect( |
| 1143 | + ctx, couriers, p.pingCourier, |
| 1144 | + ) |
| 1145 | + if err != nil { |
| 1146 | + return fmt.Errorf("failed execute proof courier(s) parallel "+ |
| 1147 | + "ping: %w", err) |
| 1148 | + } |
| 1149 | + |
| 1150 | + // If any errors occurred while pinging proof couriers, log them all |
| 1151 | + // here. |
| 1152 | + for idx := range instanceErrors { |
| 1153 | + addr := couriers[idx] |
| 1154 | + instanceErr := instanceErrors[idx] |
| 1155 | + |
| 1156 | + log.Errorf("Failed to pinging proof courier (addr=%s): %v", |
| 1157 | + addr.String(), instanceErr) |
| 1158 | + } |
| 1159 | + |
| 1160 | + // If any errors occurred while pinging proof couriers, return an error. |
| 1161 | + if len(instanceErrors) > 0 { |
| 1162 | + return fmt.Errorf("failed to ping proof courier(s) "+ |
| 1163 | + "(error_count=%d)", len(instanceErrors)) |
| 1164 | + } |
| 1165 | + |
| 1166 | + return nil |
| 1167 | +} |
| 1168 | + |
| 1169 | +// prelimCheckAddrParcel performs preliminary validation on the given address |
| 1170 | +// parcel. These early checks run before any coin locking or transaction |
| 1171 | +// broadcasting occurs. |
| 1172 | +func (p *ChainPorter) prelimCheckAddrParcel(addrParcel AddressParcel) error { |
| 1173 | + // Currently, the only preliminary check is to ensure that the proof |
| 1174 | + // couriers are reachable. If the skip flag is set, we skip this |
| 1175 | + // check and exit early. |
| 1176 | + if addrParcel.skipProofCourierPingCheck { |
| 1177 | + log.Debugf("Flag skipProofCourierPingCheck activated. " + |
| 1178 | + "Skipping check. ") |
| 1179 | + return nil |
| 1180 | + } |
| 1181 | + |
| 1182 | + // Ping the proof couriers to verify that they are reachable. |
| 1183 | + // This early check ensures a proof can be reliably delivered |
| 1184 | + // to the counterparty before broadcasting a transaction or |
| 1185 | + // locking local funds. |
| 1186 | + var proofCourierAddrs []url.URL |
| 1187 | + for idx := range addrParcel.destAddrs { |
| 1188 | + tapAddr := addrParcel.destAddrs[idx] |
| 1189 | + |
| 1190 | + proofCourierAddrs = append( |
| 1191 | + proofCourierAddrs, tapAddr.ProofCourierAddr, |
| 1192 | + ) |
| 1193 | + } |
| 1194 | + |
| 1195 | + err := p.pingProofCouriers(proofCourierAddrs) |
| 1196 | + if err != nil { |
| 1197 | + return fmt.Errorf("failed proof courier(s) connection "+ |
| 1198 | + "check: %w", err) |
| 1199 | + } |
| 1200 | + |
| 1201 | + return nil |
| 1202 | +} |
| 1203 | + |
1096 | 1204 | // stateStep attempts to step through the state machine to complete a Taproot |
1097 | 1205 | // Asset transfer. |
1098 | 1206 | func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) { |
1099 | 1207 | switch currentPkg.SendState { |
1100 | | - // At this point we have the initial package information populated, so |
1101 | | - // we'll perform coin selection to see if the send request is even |
1102 | | - // possible at all. |
| 1208 | + // The initial state entered when the state machine begins processing a |
| 1209 | + // new address parcel. In this state, basic validation is performed, |
| 1210 | + // such as verifying connectivity to any required proof courier service. |
| 1211 | + case SendStateStartHandleAddrParcel: |
| 1212 | + // Ensure that the parcel is a valid address parcel. |
| 1213 | + addrParcel, ok := currentPkg.Parcel.(*AddressParcel) |
| 1214 | + if !ok { |
| 1215 | + return nil, fmt.Errorf("unable to cast parcel to " + |
| 1216 | + "address parcel") |
| 1217 | + } |
| 1218 | + |
| 1219 | + err := p.prelimCheckAddrParcel(*addrParcel) |
| 1220 | + if err != nil { |
| 1221 | + return nil, fmt.Errorf("failed to perform prelim "+ |
| 1222 | + "checks on address parcel: %w", err) |
| 1223 | + } |
| 1224 | + |
| 1225 | + currentPkg.SendState = SendStateVirtualCommitmentSelect |
| 1226 | + return ¤tPkg, nil |
| 1227 | + |
| 1228 | + // Perform coin selection for the address parcel. |
1103 | 1229 | case SendStateVirtualCommitmentSelect: |
1104 | 1230 | ctx, cancel := p.WithCtxQuitNoTimeout() |
1105 | 1231 | defer cancel() |
|
0 commit comments