@@ -28,6 +28,29 @@ const (
2828	DefaultAcceptPriceDeviationPpm  =  50_000 
2929)
3030
31+ // QueryError represents an error with additional context about the price 
32+ // oracle query that led to it. 
33+ type  QueryError  struct  {
34+ 	// Err is the error returned from a query attempt, possibly from a 
35+ 	// price oracle. 
36+ 	Err  error 
37+ 
38+ 	// Context is the context of the price oracle query that led to the 
39+ 	// error. 
40+ 	Context  string 
41+ }
42+ 
43+ // Error returns a human-readable version of the QueryError, implementing the 
44+ // main error interface. 
45+ func  (err  * QueryError ) Error () string  {
46+ 	// If there's no context, just fall back to the wrapped error. 
47+ 	if  err .Context  ==  ""  {
48+ 		return  err .Err .Error ()
49+ 	}
50+ 	// Otherwise prepend the context. 
51+ 	return  err .Context  +  ": "  +  err .Err .Error ()
52+ }
53+ 
3154// NegotiatorCfg holds the configuration for the negotiator. 
3255type  NegotiatorCfg  struct  {
3356	// PriceOracle is the price oracle that the negotiator will use to 
@@ -142,21 +165,29 @@ func (n *Negotiator) queryBuyFromPriceOracle(assetSpecifier asset.Specifier,
142165		counterparty , metadata , intent ,
143166	)
144167	if  err  !=  nil  {
145- 		return  nil , fmt .Errorf ("failed to query price oracle for " + 
146- 			"buy price: %w" , err )
168+ 		return  nil , & QueryError {
169+ 			Err :     err ,
170+ 			Context : "failed to query price oracle for buy price" ,
171+ 		}
147172	}
148173
149174	// Now we will check for an error in the response from the price oracle. 
150- 	// If present, we will simply  relay it. 
175+ 	// If present, we will relay it with context . 
151176	if  oracleResponse .Err  !=  nil  {
152- 		return  nil , oracleResponse .Err 
177+ 		return  nil , & QueryError {
178+ 			Err :     oracleResponse .Err ,
179+ 			Context : "failed to query price oracle for buy price" ,
180+ 		}
153181	}
154182
155183	// By this point, the price oracle did not return an error or a buy 
156184	// price. We will therefore return an error. 
157185	if  oracleResponse .AssetRate .Rate .ToUint64 () ==  0  {
158- 		return  nil , fmt .Errorf ("price oracle did not specify a "  + 
159- 			"buy price" )
186+ 		return  nil , & QueryError {
187+ 			Err : errors .New ("price oracle didn't specify "  + 
188+ 				"a price" ),
189+ 			Context : "failed to query price oracle for buy price" ,
190+ 		}
160191	}
161192
162193	// TODO(ffranr): Check that the buy price is reasonable. 
@@ -277,21 +308,29 @@ func (n *Negotiator) querySellFromPriceOracle(assetSpecifier asset.Specifier,
277308		counterparty , metadata , intent ,
278309	)
279310	if  err  !=  nil  {
280- 		return  nil , fmt .Errorf ("failed to query price oracle for " + 
281- 			"sell price: %w" , err )
311+ 		return  nil , & QueryError {
312+ 			Err :     err ,
313+ 			Context : "failed to query price oracle for sell price" ,
314+ 		}
282315	}
283316
284317	// Now we will check for an error in the response from the price oracle. 
285- 	// If present, we will simply  relay it. 
318+ 	// If present, we will relay it with context . 
286319	if  oracleResponse .Err  !=  nil  {
287- 		return  nil , oracleResponse .Err 
320+ 		return  nil , & QueryError {
321+ 			Err :     oracleResponse .Err ,
322+ 			Context : "failed to query price oracle for sell price" ,
323+ 		}
288324	}
289325
290326	// By this point, the price oracle did not return an error or a sell 
291327	// price. We will therefore return an error. 
292328	if  oracleResponse .AssetRate .Rate .Coefficient .ToUint64 () ==  0  {
293- 		return  nil , fmt .Errorf ("price oracle did not specify an "  + 
294- 			"asset to BTC rate" )
329+ 		return  nil , & QueryError {
330+ 			Err : errors .New ("price oracle didn't specify "  + 
331+ 				"a price" ),
332+ 			Context : "failed to query price oracle for sell price" ,
333+ 		}
295334	}
296335
297336	// TODO(ffranr): Check that the sell price is reasonable. 
@@ -501,28 +540,28 @@ func (n *Negotiator) HandleIncomingSellRequest(
501540// createCustomRejectErr creates a RejectErr with code 0 and a custom message 
502541// based on an error response from a price oracle. 
503542func  createCustomRejectErr (err  error ) rfqmsg.RejectErr  {
543+ 	var  queryError  * QueryError 
544+ 	// Check if the error we've received is the expected QueryError, and 
545+ 	// return an opaque rejection error if not. 
546+ 	if  ! errors .As (err , & queryError ) {
547+ 		return  rfqmsg .ErrUnknownReject 
548+ 	}
549+ 
504550	var  oracleError  * OracleError 
551+ 	// Check if the QueryError contains the expected OracleError, and 
552+ 	// return an opaque rejection error if not. 
553+ 	if  ! errors .As (queryError , & oracleError ) {
554+ 		return  rfqmsg .ErrUnknownReject 
555+ 	}
505556
506- 	if  errors .As (err , & oracleError ) {
507- 		// The error is of the expected type, so switch on the error 
508- 		// code returned by the oracle. If the code is benign, then the 
509- 		// RejectErr will simply relay the oracle's message. Otherwise, 
510- 		// we'll return an opaque rejection message. 
511- 		switch  oracleError .Code  {
512- 		// The rejection message will state that the oracle doesn't 
513- 		// support the asset. 
514- 		case  UnsupportedAssetOracleErrorCode :
515- 			msg  :=  oracleError .Msg 
516- 			return  rfqmsg .ErrRejectWithCustomMsg (msg )
517- 
518- 		// The rejection message will be opaque, with the error 
519- 		// unspecified. 
520- 		default :
521- 			return  rfqmsg .ErrUnknownReject 
522- 		}
523- 	} else  {
524- 		// The error is of an unexpected type, so just return an opaque 
525- 		// error message. 
557+ 	switch  oracleError .Code  {
558+ 	// The price oracle has indicated that it doesn't support the asset, 
559+ 	// so return a rejection error indicating that. 
560+ 	case  UnsupportedAssetOracleErrorCode :
561+ 		return  rfqmsg .ErrRejectWithCustomMsg (oracleError .Msg )
562+ 	// The error code is either unspecified or unknown, so return an 
563+ 	// opaque rejection error. 
564+ 	default :
526565		return  rfqmsg .ErrUnknownReject 
527566	}
528567}
0 commit comments