logging.go raw

   1  package peer
   2  
   3  import (
   4  	"fmt"
   5  	"strings"
   6  	"time"
   7  	
   8  	"github.com/p9c/p9/pkg/chainhash"
   9  	"github.com/p9c/p9/pkg/txscript"
  10  	"github.com/p9c/p9/pkg/wire"
  11  )
  12  
  13  const (
  14  	// maxRejectReasonLen is the maximum length of a sanitized reject reason that will be logged.
  15  	maxRejectReasonLen = 250
  16  )
  17  
  18  // formatLockTime returns a transaction lock time as a human-readable string.
  19  func formatLockTime(lockTime uint32) string {
  20  	// The lock time field of a transaction is either a block height at which the transaction is finalized or a
  21  	// timestamp depending on if the value is before the lockTimeThreshold. When it is under the threshold it is a block
  22  	// height.
  23  	if lockTime < txscript.LockTimeThreshold {
  24  		return fmt.Sprintf("height %d", lockTime)
  25  	}
  26  	
  27  	return time.Unix(int64(lockTime), 0).String()
  28  }
  29  
  30  // invSummary returns an inventory message as a human-readable string.
  31  func invSummary(invList []*wire.InvVect) string {
  32  	// No inventory.
  33  	invLen := len(invList)
  34  	if invLen == 0 {
  35  		return "empty"
  36  	}
  37  	
  38  	// One inventory item.
  39  	if invLen == 1 {
  40  		iv := invList[0]
  41  		switch iv.Type {
  42  		case wire.InvTypeError:
  43  			return fmt.Sprintf("error %s", iv.Hash)
  44  		// case wire.InvTypeWitnessBlock:
  45  		// 	return fmt.Sprintf("witness block %s", iv.Hash)
  46  		case wire.InvTypeBlock:
  47  			return fmt.Sprintf("block %s", iv.Hash)
  48  		// case wire.InvTypeWitnessTx:
  49  		// 	return fmt.Sprintf("witness tx %s", iv.Hash)
  50  		case wire.InvTypeTx:
  51  			return fmt.Sprintf("tx %s", iv.Hash)
  52  		}
  53  		
  54  		return fmt.Sprintf("unknown (%d) %s", uint32(iv.Type), iv.Hash)
  55  	}
  56  	
  57  	// More than one inv item.
  58  	return fmt.Sprintf("size %d", invLen)
  59  }
  60  
  61  // locatorSummary returns a block locator as a human-readable string.
  62  func locatorSummary(locator []*chainhash.Hash, stopHash *chainhash.Hash) string {
  63  	if len(locator) > 0 {
  64  		return fmt.Sprintf("locator %s, stop %s", locator[0], stopHash)
  65  	}
  66  	
  67  	return fmt.Sprintf("no locator, stop %s", stopHash)
  68  	
  69  }
  70  
  71  // sanitizeString strips any characters which are even remotely dangerous, such as html control characters, from the
  72  // passed string. It also limits it to the passed maximum size, which can be 0 for unlimited. When the string is
  73  // limited, it will also add "..." to the string to indicate it was truncated.
  74  func sanitizeString(str string, maxLength uint) string {
  75  	const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" +
  76  		"Z01234567890 .,;_/:?@"
  77  	
  78  	// Strip any characters not in the safeChars string removed.
  79  	str = strings.Map(func(r rune) rune {
  80  		if strings.ContainsRune(safeChars, r) {
  81  			return r
  82  		}
  83  		return -1
  84  	}, str,
  85  	)
  86  	
  87  	// Limit the string to the max allowed length.
  88  	if maxLength > 0 && uint(len(str)) > maxLength {
  89  		str = str[:maxLength]
  90  		str = str + "..."
  91  	}
  92  	return str
  93  }
  94  
  95  // messageSummary returns a human-readable string which summarizes a message. Not all messages have or need a summary.
  96  // This is used for debug logging.
  97  func messageSummary(msg wire.Message) string {
  98  	switch msg := msg.(type) {
  99  	case *wire.MsgVersion:
 100  		return fmt.Sprintf("agent %s, pver %d, block %d",
 101  			msg.UserAgent, msg.ProtocolVersion, msg.LastBlock,
 102  		)
 103  	
 104  	case *wire.MsgVerAck:
 105  		// No summary.
 106  	
 107  	case *wire.MsgGetAddr:
 108  		// No summary.
 109  	
 110  	case *wire.MsgAddr:
 111  		return fmt.Sprintf("%d addr", len(msg.AddrList))
 112  	
 113  	case *wire.MsgPing:
 114  		// No summary - perhaps add Nonce.
 115  	
 116  	case *wire.MsgPong:
 117  		// No summary - perhaps add Nonce.
 118  	
 119  	case *wire.MsgAlert:
 120  		// No summary.
 121  	
 122  	case *wire.MsgMemPool:
 123  		// No summary.
 124  	
 125  	case *wire.MsgTx:
 126  		return fmt.Sprintf("hash %s, %d inputs, %d outputs, lock %s",
 127  			msg.TxHash(), len(msg.TxIn), len(msg.TxOut),
 128  			formatLockTime(msg.LockTime),
 129  		)
 130  	
 131  	case *wire.Block:
 132  		header := &msg.Header
 133  		return fmt.Sprintf("hash %s, ver %d, %d tx, %s", msg.BlockHash(),
 134  			header.Version, len(msg.Transactions), header.Timestamp,
 135  		)
 136  	
 137  	case *wire.MsgInv:
 138  		return invSummary(msg.InvList)
 139  	
 140  	case *wire.MsgNotFound:
 141  		return invSummary(msg.InvList)
 142  	
 143  	case *wire.MsgGetData:
 144  		return invSummary(msg.InvList)
 145  	
 146  	case *wire.MsgGetBlocks:
 147  		return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop)
 148  	
 149  	case *wire.MsgGetHeaders:
 150  		return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop)
 151  	
 152  	case *wire.MsgHeaders:
 153  		return fmt.Sprintf("num %d", len(msg.Headers))
 154  	
 155  	case *wire.MsgGetCFHeaders:
 156  		return fmt.Sprintf("start_height=%d, stop_hash=%v",
 157  			msg.StartHeight, msg.StopHash,
 158  		)
 159  	
 160  	case *wire.MsgCFHeaders:
 161  		return fmt.Sprintf("stop_hash=%v, num_filter_hashes=%d",
 162  			msg.StopHash, len(msg.FilterHashes),
 163  		)
 164  	
 165  	case *wire.MsgReject:
 166  		// Ensure the variable length strings don't contain any characters which are even remotely dangerous such as
 167  		// HTML control characters, etc. Also limit them to sane length for logging.
 168  		rejCommand := sanitizeString(msg.Cmd, wire.CommandSize)
 169  		rejReason := sanitizeString(msg.Reason, maxRejectReasonLen)
 170  		summary := fmt.Sprintf("cmd %v, code %v, reason %v", rejCommand,
 171  			msg.Code, rejReason,
 172  		)
 173  		if rejCommand == wire.CmdBlock || rejCommand == wire.CmdTx {
 174  			summary += fmt.Sprintf(", hash %v", msg.Hash)
 175  		}
 176  		return summary
 177  	}
 178  	
 179  	// No summary for other messages.
 180  	return ""
 181  }
 182