decred.org/dcrdex@v1.0.5/server/asset/common.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  	"context"
     8  	"fmt"
     9  	"time"
    10  
    11  	"decred.org/dcrdex/dex"
    12  )
    13  
    14  // BackendInfo provides auxiliary information about a backend.
    15  type BackendInfo struct {
    16  	SupportsDynamicTxFee bool
    17  }
    18  
    19  // CoinNotFoundError is to be returned from Contract, Redemption, and
    20  // FundingCoin when the specified transaction cannot be found. Used by the
    21  // server to handle network latency.
    22  const (
    23  	CoinNotFoundError = dex.ErrorKind("coin not found")
    24  	ErrRequestTimeout = dex.ErrorKind("request timeout")
    25  )
    26  
    27  // Backend is a blockchain backend. TODO: Plumb every method with a cancellable
    28  // request with a Context, which will prevent backend shutdown from cancelling
    29  // requests, but is more idiomatic these days.
    30  type Backend interface {
    31  	// It is expected that Connect from dex.Connector is called and returns
    32  	// before use of the asset, and that it is only called once for the life
    33  	// of the asset.
    34  	dex.Connector
    35  	// Contract returns a Contract only for outputs that would be spendable on
    36  	// the blockchain immediately. Contract data (e.g. the redeem script for
    37  	// UTXO assets) is required in order to calculate sigScript length and
    38  	// verify pubkeys.
    39  	Contract(coinID []byte, contractData []byte) (*Contract, error)
    40  	// TxData fetches the raw transaction data for the specified coin.
    41  	TxData(coinID []byte) ([]byte, error)
    42  	// ValidateSecret checks that the secret satisfies the contract.
    43  	ValidateSecret(secret, contractData []byte) bool
    44  	// Redemption returns a Coin for redemptionID, a transaction input, that
    45  	// spends contract ID, an output containing the swap contract.
    46  	Redemption(redemptionID, contractID, contractData []byte) (Coin, error)
    47  	// BlockChannel creates and returns a new channel on which to receive updates
    48  	// when new blocks are connected.
    49  	BlockChannel(size int) <-chan *BlockUpdate
    50  	// CheckSwapAddress checks that the given address is parseable, and suitable
    51  	// as a redeem address in a swap contract script or initiation.
    52  	CheckSwapAddress(string) bool
    53  	// ValidateCoinID checks the coinID to ensure it can be decoded, returning a
    54  	// human-readable string if it is valid.
    55  	// Note: ValidateCoinID is NOT used for funding coin IDs for account-based
    56  	// assets. This rule is only enforced by code patterns right now, but we may
    57  	// consider adding separate methods in the future.
    58  	ValidateCoinID(coinID []byte) (string, error)
    59  	// ValidateContract ensures that the swap contract is constructed properly
    60  	// for the asset.
    61  	ValidateContract(contract []byte) error
    62  	// FeeRate returns the current optimal fee rate in atoms / byte.
    63  	FeeRate(context.Context) (uint64, error)
    64  	// Synced should return true when the blockchain is synced and ready for
    65  	// fee rate estimation.
    66  	Synced() (bool, error)
    67  	// Info provides auxiliary information about a backend.
    68  	Info() *BackendInfo
    69  	// ValidateFeeRate checks that the transaction fees used to initiate the
    70  	// contract are sufficient.
    71  	ValidateFeeRate(coin Coin, reqFeeRate uint64) bool
    72  }
    73  
    74  // OutputTracker is implemented by backends for UTXO-based blockchains.
    75  // OutputTracker tracks the value and spend-status of transaction outputs.
    76  type OutputTracker interface {
    77  	// VerifyUnspentCoin attempts to verify a coin ID by decoding the coin ID
    78  	// and retrieving the corresponding Coin. If the coin is not found or no
    79  	// longer unspent, an asset.CoinNotFoundError is returned. Use FundingCoin
    80  	// for more UTXO data.
    81  	VerifyUnspentCoin(ctx context.Context, coinID []byte) error
    82  	// FundingCoin returns the unspent coin at the specified location. Coins
    83  	// with non-standard pkScripts or scripts that require zero signatures to
    84  	// redeem must return an error.
    85  	FundingCoin(ctx context.Context, coinID []byte, redeemScript []byte) (FundingCoin, error)
    86  	// ValidateOrderFunding validates that the supplied utxos are enough to fund an order.
    87  	ValidateOrderFunding(swapVal, valSum, inputCount, inputsSize, maxSwaps uint64, nfo *dex.Asset) bool
    88  }
    89  
    90  // AccountBalancer is implemented by backends for account-based blockchains.
    91  // An AccountBalancer reports the current balance for an account.
    92  type AccountBalancer interface {
    93  	// AccountBalance retrieves the current account balance.
    94  	AccountBalance(addr string) (uint64, error)
    95  	// ValidateSignature checks that the pubkey is correct for the address and
    96  	// that the signature shows ownership of the associated private key.
    97  	// IMPORTANT: As part of signature validation, the asset backend should
    98  	// validate the address against a STRICT standard. Case, prefixes, suffixes,
    99  	// etc. must be exactly the same order-to-order, since the address string
   100  	// is used as a key for various accounting operations throughout DEX.
   101  	ValidateSignature(addr string, pubkey, msg, sig []byte) error
   102  	// RedeemSize is the gas used for a single redemption.
   103  	RedeemSize() uint64
   104  	InitTxSize() uint64
   105  }
   106  
   107  // TokenBacker is implemented by Backends that support degenerate tokens.
   108  type TokenBacker interface {
   109  	TokenBackend(assetID uint32, configPath string) (Backend, error)
   110  }
   111  
   112  // Coin represents a transaction input or output.
   113  type Coin interface {
   114  	// Confirmations returns the number of confirmations for a Coin's
   115  	// transaction. Because a Coin can become invalid after once being
   116  	// considered valid, this condition should be checked for during
   117  	// confirmation counting and an error returned if this Coin is no longer
   118  	// ready to spend. An unmined transaction should have zero confirmations. A
   119  	// transaction in the current best block should have one confirmation. A
   120  	// negative number can be returned if error is not nil.
   121  	Confirmations(context.Context) (int64, error)
   122  	// ID is the coin ID.
   123  	ID() []byte
   124  	// TxID is a transaction identifier for the coin.
   125  	TxID() string
   126  	// String is a human readable representation of the Coin.
   127  	String() string
   128  	// Value is the coin value.
   129  	Value() uint64
   130  	// FeeRate returns the transaction fee rate, in atoms/byte equivalent.
   131  	FeeRate() uint64
   132  }
   133  
   134  // FundingCoin is some unspent value on the blockchain.
   135  type FundingCoin interface {
   136  	Coin() Coin
   137  	// Auth checks that the owner of the provided pubkeys can spend the
   138  	// FundingCoin. The signatures (sigs) generated with the private keys
   139  	// corresponding to pubkeys must validate against the pubkeys and signing
   140  	// message (msg).
   141  	Auth(pubkeys, sigs [][]byte, msg []byte) error
   142  	// SpendSize returns the size of the serialized input that spends this
   143  	// FundingCoin.
   144  	SpendSize() uint32
   145  }
   146  
   147  // Contract is an atomic swap contract.
   148  type Contract struct {
   149  	Coin
   150  	// SwapAddress is the receiving address of the swap contract.
   151  	SwapAddress string
   152  	// ContractData is essential data about this swap. For example, the redeem
   153  	// script for UTXO contracts, or a secret hash that keys swaps for account-
   154  	// based contracts.
   155  	ContractData []byte
   156  	// SecretHash is the secret key hash used for this swap. This should be used
   157  	// to validate a counterparty contract on another chain.
   158  	SecretHash []byte
   159  	// LockTime is the refund locktime.
   160  	LockTime time.Time
   161  	// TxData is raw transaction data. This data is provided for some assets
   162  	// to aid in SPV compatibility.
   163  	TxData []byte
   164  }
   165  
   166  // BlockUpdate is sent over the update channel when a tip change is detected.
   167  type BlockUpdate struct {
   168  	Err   error
   169  	Reorg bool // TODO: Not used. Remove and update through btc and dcr. Don't really need the BlockUpdate struct at all.
   170  }
   171  
   172  // ConnectionError error should be sent over the block update channel if a
   173  // connection error is detected by the Backend.
   174  type ConnectionError error
   175  
   176  // NewConnectionError is a constructor for a ConnectionError.
   177  func NewConnectionError(s string, a ...any) ConnectionError {
   178  	return ConnectionError(fmt.Errorf(s, a...))
   179  }
   180  
   181  // BackedAsset is a dex.Asset with a Backend.
   182  type BackedAsset struct {
   183  	dex.Asset
   184  	Backend Backend
   185  }