diff --git a/config.go b/config.go index e375ee2912..2c4937d4be 100644 --- a/config.go +++ b/config.go @@ -113,6 +113,7 @@ type config struct { MiningTimeOffset int `long:"miningtimeoffset" description:"Offset the mining timestamp of a block by this many seconds (positive values are in the past)"` DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list available subsystems"` Upnp bool `long:"upnp" description:"Use UPnP to map our listening port outside of NAT"` + MinRelayTxFee float64 `long:"minrelaytxfee" description:"The minimum transaction fee in DCR/kB to be considered a non-zero fee."` FreeTxRelayLimit float64 `long:"limitfreerelay" description:"Limit relay of transactions with no transaction fee to the given amount in thousands of bytes per minute"` NoRelayPriority bool `long:"norelaypriority" description:"Do not require free or low-fee transactions to have high priority for relaying"` MaxOrphanTxs int `long:"maxorphantx" description:"Max number of orphan transactions to keep in memory"` @@ -134,6 +135,7 @@ type config struct { oniondial func(string, string) (net.Conn, error) dial func(string, string) (net.Conn, error) miningAddrs []dcrutil.Address + minRelayTxFee dcrutil.Amount } // serviceOptions defines the configuration options for the daemon as a service on @@ -331,6 +333,7 @@ func loadConfig() (*config, []string, error) { DbType: defaultDbType, RPCKey: defaultRPCKeyFile, RPCCert: defaultRPCCertFile, + MinRelayTxFee: defaultMinRelayTxFee.ToCoin(), FreeTxRelayLimit: defaultFreeTxRelayLimit, BlockMinSize: defaultBlockMinSize, BlockMaxSize: defaultBlockMaxSize, @@ -611,6 +614,16 @@ func loadConfig() (*config, []string, error) { } } + // Validate the the minrelaytxfee. + cfg.minRelayTxFee, err = dcrutil.NewAmount(cfg.MinRelayTxFee) + if err != nil { + str := "%s: invalid minrelaytxfee: %v" + err := fmt.Errorf(str, funcName, err) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + // Limit the max block size to a sane value. if cfg.BlockMaxSize < blockMaxSizeMin || cfg.BlockMaxSize > blockMaxSizeMax { diff --git a/doc.go b/doc.go index af3b10e101..01377cd2e1 100644 --- a/doc.go +++ b/doc.go @@ -81,6 +81,8 @@ Application Options: the log level for individual subsystems -- Use show to list available subsystems (info) --upnp Use UPnP to map our listening port outside of NAT + --minrelaytxfee= The minimum transaction fee in DCR/kB to be + considered a non-zero fee. --limitfreerelay= Limit relay of transactions with no transaction fee to the given amount in thousands of bytes per minute (15) diff --git a/mempool.go b/mempool.go index 7b517c0f7a..1392949bf1 100644 --- a/mempool.go +++ b/mempool.go @@ -73,12 +73,12 @@ const ( // considered standard. maxStandardMultiSigKeys = 3 - // minTxRelayFee is the minimum fee in atoms that is required for a - // transaction to be treated as free for relay and mining purposes. It - // is also used to help determine if a transaction is considered dust - // and as a base for calculating minimum required fees per KB for larger - // transactions. This value is in Atom/1000 bytes. - minTxRelayFee = 1e6 + // defaultMinRelayTxFee is the minimum fee in atoms that is required + // for a transaction to be treated as free for relay and mining + // purposes. It is also used to help determine if a transaction is + // considered dust and as a base for calculating minimum required fees + // for larger transactions. This value is in Atoms/1000 bytes. + defaultMinRelayTxFee = dcrutil.Amount(1e6) // minTicketFeeMainNet is the minimum fee per KB in atoms that is // required for a ticket to enter the mempool on MainNet. @@ -478,7 +478,7 @@ func (mp *txMemPool) checkTransactionStandard(tx *dcrutil.Tx, txType stake.TxTyp // "dust". if scriptClass == txscript.NullDataTy { numNullDataOutputs++ - } else if isDust(txOut, minTxRelayFee) && + } else if isDust(txOut, cfg.minRelayTxFee) && txType != stake.TxTypeSStx { str := fmt.Sprintf("transaction output %d: payment "+ "of %d is dust", i, txOut.Value) @@ -1406,7 +1406,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, // high-priority transactions, don't require a fee for it. // This applies to non-stake transactions only. serializedSize := int64(tx.MsgTx().SerializeSize()) - minFee := calcMinRequiredTxRelayFee(serializedSize, minTxRelayFee) + minFee := calcMinRequiredTxRelayFee(serializedSize, cfg.minRelayTxFee) if txType == stake.TxTypeRegular { // Non-stake only if serializedSize >= (defaultBlockPrioritySize-1000) && txFee < minFee { str := fmt.Sprintf("transaction %v has %v fees which is under "+ @@ -1482,7 +1482,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, // then they can AllowHighFees = true if !allowHighFees { maxFee := calcMinRequiredTxRelayFee(serializedSize*maxRelayFeeMultiplier, - minTxRelayFee) + cfg.minRelayTxFee) if txFee > maxFee { err = fmt.Errorf("transaction %v has %v fee which is above the "+ "allowHighFee check threshold amount of %v", txHash, diff --git a/mining.go b/mining.go index 1bbe0cbaf3..863918f730 100644 --- a/mining.go +++ b/mining.go @@ -1531,14 +1531,15 @@ mempoolLoop: // Skip free transactions once the block is larger than the // minimum block size, except for stake transactions. - if sortedByFee && (prioItem.feePerKB < minTxRelayFee) && + if sortedByFee && + (prioItem.feePerKB < float64(cfg.minRelayTxFee)) && (tx.Tree() != dcrutil.TxTreeStake) && (blockPlusTxSize >= cfg.BlockMinSize) { minrLog.Tracef("Skipping tx %s with feePerKB %.2f "+ "< minTxRelayFee %d and block size %d >= "+ "minBlockSize %d", tx.Sha(), prioItem.feePerKB, - minTxRelayFee, blockPlusTxSize, + cfg.minRelayTxFee, blockPlusTxSize, cfg.BlockMinSize) logSkippedDeps(tx, deps) continue diff --git a/policy.go b/policy.go index 93c99ec0e3..245e4d627d 100644 --- a/policy.go +++ b/policy.go @@ -17,7 +17,7 @@ import ( // calcMinRequiredTxRelayFee returns the minimum transaction fee required for a // transaction with the passed serialized size to be accepted into the memory // pool and relayed. -func calcMinRequiredTxRelayFee(serializedSize, minRelayTxFee int64) int64 { +func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee dcrutil.Amount) int64 { // Calculate the minimum fee for a transaction to be allowed into the // mempool and relayed by scaling the base fee (which is the minimum // free transaction relay fee). minTxRelayFee is in Atom/KB, so @@ -25,10 +25,10 @@ func calcMinRequiredTxRelayFee(serializedSize, minRelayTxFee int64) int64 { // integer division is used so fees only increase on full kilobyte // boundaries. - minFee := (serializedSize * minRelayTxFee) / 1000 + minFee := (serializedSize * int64(minRelayTxFee)) / 1000 if minFee == 0 && minRelayTxFee > 0 { - minFee = minRelayTxFee + minFee = int64(minRelayTxFee) } // Set the minimum fee to the maximum possible value if the calculated diff --git a/policy_test.go b/policy_test.go index 10595433da..328a043219 100644 --- a/policy_test.go +++ b/policy_test.go @@ -25,19 +25,19 @@ func TestCalcMinRequiredTxRelayFee(t *testing.T) { { "zero value with default minimum relay fee", 0, - minTxRelayFee, - int64(minTxRelayFee), + defaultMinRelayTxFee, + int64(defaultMinRelayTxFee), }, { "1000 bytes with default minimum relay fee", 1000, - minTxRelayFee, - int64(minTxRelayFee), + defaultMinRelayTxFee, + int64(defaultMinRelayTxFee), }, { "max standard tx size with default minimum relay fee", maxStandardTxSize, - minTxRelayFee, + defaultMinRelayTxFee, 100000000, }, { @@ -49,7 +49,7 @@ func TestCalcMinRequiredTxRelayFee(t *testing.T) { } for _, test := range tests { - got := calcMinRequiredTxRelayFee(test.size, int64(test.relayFee)) + got := calcMinRequiredTxRelayFee(test.size, test.relayFee) if got != test.want { t.Errorf("TestCalcMinRequiredTxRelayFee test '%s' "+ "failed: got %v want %v", test.name, got, diff --git a/rpcserver.go b/rpcserver.go index d00cc3f0f9..ad30012e29 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3517,7 +3517,7 @@ func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (in Proxy: cfg.Proxy, Difficulty: getDifficultyRatio(blkHeader.Bits), TestNet: cfg.TestNet, - RelayFee: minTxRelayFee / dcrutil.AtomsPerCoin, + RelayFee: cfg.minRelayTxFee.ToCoin(), } return ret, nil diff --git a/sample-dcrd.conf b/sample-dcrd.conf index 814bc5f3cd..e174ebfa52 100644 --- a/sample-dcrd.conf +++ b/sample-dcrd.conf @@ -206,6 +206,9 @@ ; Mempool Settings - The following options ; ------------------------------------------------------------------------------ +; Set the minimum transaction fee to be considered a non-zero fee, +; minrelaytxfee=0.01 + ; Rate-limit free transactions to the value 15 * 1000 bytes per ; minute. ; limitfreerelay=15