decred.org/dcrdex@v1.0.5/server/asset/driver.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package asset
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"decred.org/dcrdex/dex"
    10  )
    11  
    12  var (
    13  	drivers     = make(map[uint32]Driver)
    14  	tokens      = make(map[uint32]TokenDriver)
    15  	childTokens = make(map[uint32]map[uint32]*dex.Token)
    16  )
    17  
    18  // driverBase defines a base set of driver methods common to both base-chain and
    19  // degenerate assets.
    20  type driverBase interface {
    21  	DecodeCoinID(coinID []byte) (string, error)
    22  	// Version returns the Backend's version number, which is used to signal
    23  	// when major changes are made to internal details such as coin ID encoding
    24  	// and contract structure that must be common to a client's.
    25  	Version() uint32
    26  	// UnitInfo returns the dex.UnitInfo for the asset.
    27  	UnitInfo() dex.UnitInfo
    28  	// Name is the name for the asset.
    29  	Name() string
    30  }
    31  
    32  // minValuer can be implemented by assets with minimum dust sizes that vary
    33  // with fee rate. If an asset driver does not implement minValuer, the min
    34  // value is assumed to be 1 atom for both bond and lot sizes.
    35  type minValuer interface {
    36  	MinBondSize(maxFeeRate uint64) uint64
    37  	MinLotSize(maxFeeRate uint64) uint64
    38  }
    39  
    40  // Driver is the interface required of all base chain assets.
    41  type Driver interface {
    42  	driverBase
    43  	// Setup should create a Backend, but not start the backend connection.
    44  	Setup(*BackendConfig) (Backend, error)
    45  }
    46  
    47  // TokenDriver is the interface required of all token assets.
    48  type TokenDriver interface {
    49  	driverBase
    50  	TokenInfo() *dex.Token
    51  }
    52  
    53  func baseDriver(assetID uint32) (driverBase, bool) {
    54  	if drv, found := drivers[assetID]; found {
    55  		return drv, true
    56  	}
    57  	if drv, found := tokens[assetID]; found {
    58  		return drv, true
    59  	}
    60  	return nil, false
    61  }
    62  
    63  // DecodeCoinID creates a human-readable representation of a coin ID for a named
    64  // asset with a corresponding driver registered with this package.
    65  func DecodeCoinID(assetID uint32, coinID []byte) (string, error) {
    66  	drv, ok := baseDriver(assetID)
    67  	if !ok {
    68  		return "", fmt.Errorf("unknown asset driver %d", assetID)
    69  	}
    70  	return drv.DecodeCoinID(coinID)
    71  }
    72  
    73  // asset with a corresponding driver registered with this package.
    74  func UnitInfo(assetID uint32) (dex.UnitInfo, error) {
    75  	drv, ok := baseDriver(assetID)
    76  	if !ok {
    77  		return dex.UnitInfo{}, fmt.Errorf("unknown asset %d (%q) in UnitInfo", assetID, dex.BipIDSymbol(assetID))
    78  	}
    79  	return drv.UnitInfo(), nil
    80  
    81  }
    82  
    83  // Register should be called by the init function of an asset's package.
    84  func Register(assetID uint32, drv Driver) {
    85  	if drv == nil {
    86  		panic("asset: Register driver is nil")
    87  	}
    88  	if _, ok := baseDriver(assetID); ok {
    89  		panic(fmt.Sprintf("asset: Register called twice for asset driver %d", assetID))
    90  	}
    91  	if drv.UnitInfo().Conventional.ConversionFactor == 0 {
    92  		panic(fmt.Sprintf("asset: Driver registered with unit conversion factor = 0: %q", assetID))
    93  	}
    94  	drivers[assetID] = drv
    95  }
    96  
    97  // RegisterToken is called to register a token. The parent asset should be
    98  // registered first.
    99  func RegisterToken(assetID uint32, drv TokenDriver) {
   100  	if drv == nil {
   101  		panic("asset: Register driver is nil")
   102  	}
   103  	if _, ok := tokens[assetID]; ok {
   104  		panic(fmt.Sprintf("asset: RegisterToken called twice for asset driver %d", assetID))
   105  	}
   106  	token := drv.TokenInfo()
   107  	if token == nil {
   108  		panic(fmt.Sprintf("nil *Token for asset %d", assetID))
   109  	}
   110  	if _, exists := drivers[token.ParentID]; !exists {
   111  		panic(fmt.Sprintf("no parent (%d) registered for %s", token.ParentID, token.Name))
   112  	}
   113  	if drv.UnitInfo().Conventional.ConversionFactor == 0 {
   114  		panic(fmt.Sprintf("asset: TokenDriver registered with unit conversion factor = 0: %q", assetID))
   115  	}
   116  	children := childTokens[token.ParentID]
   117  	if children == nil {
   118  		children = make(map[uint32]*dex.Token, 1)
   119  		childTokens[token.ParentID] = children
   120  	}
   121  	children[assetID] = token
   122  	tokens[assetID] = drv
   123  }
   124  
   125  type BackendConfig struct {
   126  	AssetID    uint32
   127  	ConfigPath string
   128  	Logger     dex.Logger
   129  	Net        dex.Network
   130  	RelayAddr  string
   131  }
   132  
   133  // Setup sets up the named asset. The RPC connection parameters are obtained
   134  // from the asset's configuration file located at configPath. Setup is only
   135  // called for base chain assets, not tokens.
   136  func Setup(cfg *BackendConfig) (Backend, error) {
   137  	drv, ok := drivers[cfg.AssetID]
   138  	if !ok {
   139  		return nil, fmt.Errorf("asset: unknown asset driver %d", cfg.AssetID)
   140  	}
   141  	return drv.Setup(cfg)
   142  }
   143  
   144  // Version retrieves the version of the named asset's Backend implementation.
   145  func Version(assetID uint32) (uint32, error) {
   146  	drv, ok := baseDriver(assetID)
   147  	if !ok {
   148  		return 0, fmt.Errorf("asset: unknown asset driver %d", assetID)
   149  	}
   150  	return drv.Version(), nil
   151  }
   152  
   153  // IsToken checks if the asset ID is for a token and returns the token's parent
   154  // ID.
   155  func IsToken(assetID uint32) (is bool, parentID uint32) {
   156  	token, is := tokens[assetID]
   157  	if !is {
   158  		return
   159  	}
   160  	return true, token.TokenInfo().ParentID
   161  }
   162  
   163  // Tokens returns the child tokens registered for a base chain asset.
   164  func Tokens(assetID uint32) map[uint32]*dex.Token {
   165  	m := make(map[uint32]*dex.Token, len(childTokens[assetID]))
   166  	for k, v := range childTokens[assetID] {
   167  		m[k] = v
   168  	}
   169  	return m
   170  }
   171  
   172  // Minimums returns the minimimum lot size and bond size for a registered asset.
   173  func Minimums(assetID uint32, maxFeeRate uint64) (minLotSize, minBondSize uint64, found bool) {
   174  	baseChainID := assetID
   175  	if token, is := tokens[assetID]; is {
   176  		baseChainID = token.TokenInfo().ParentID
   177  	}
   178  	drv, found := drivers[baseChainID]
   179  	if !found {
   180  		return 0, 0, false
   181  	}
   182  	m, is := drv.(minValuer)
   183  	if !is {
   184  		return 1, 1, true
   185  	}
   186  	return m.MinLotSize(maxFeeRate), m.MinBondSize(maxFeeRate), true
   187  }
   188  
   189  // RegisteredAsset is information about a registered asset.
   190  type RegisteredAsset struct {
   191  	AssetID  uint32
   192  	Symbol   string
   193  	Name     string
   194  	UnitInfo dex.UnitInfo
   195  }
   196  
   197  // Assets returns a information about registered assets.
   198  func Assets() []*RegisteredAsset {
   199  	assets := make([]*RegisteredAsset, 0, len(drivers)+len(tokens))
   200  	for assetID, drv := range drivers {
   201  		assets = append(assets, &RegisteredAsset{
   202  			AssetID:  assetID,
   203  			Symbol:   dex.BipIDSymbol(assetID),
   204  			Name:     drv.Name(),
   205  			UnitInfo: drv.UnitInfo(),
   206  		})
   207  	}
   208  	for assetID, drv := range tokens {
   209  		assets = append(assets, &RegisteredAsset{
   210  			AssetID:  assetID,
   211  			Symbol:   dex.BipIDSymbol(assetID),
   212  			Name:     drv.Name(),
   213  			UnitInfo: drv.UnitInfo(),
   214  		})
   215  	}
   216  	return assets
   217  }