btcd/wire/msgreject.go
2023-12-28 18:37:43 -08:00

193 lines
5.5 KiB
Go

// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// RejectCode represents a numeric value by which a remote peer indicates
// why a message was rejected.
type RejectCode uint8
// These constants define the various supported reject codes.
const (
RejectMalformed RejectCode = 0x01
RejectInvalid RejectCode = 0x10
RejectObsolete RejectCode = 0x11
RejectDuplicate RejectCode = 0x12
RejectNonstandard RejectCode = 0x40
RejectDust RejectCode = 0x41
RejectInsufficientFee RejectCode = 0x42
RejectCheckpoint RejectCode = 0x43
)
// Map of reject codes back strings for pretty printing.
var rejectCodeStrings = map[RejectCode]string{
RejectMalformed: "REJECT_MALFORMED",
RejectInvalid: "REJECT_INVALID",
RejectObsolete: "REJECT_OBSOLETE",
RejectDuplicate: "REJECT_DUPLICATE",
RejectNonstandard: "REJECT_NONSTANDARD",
RejectDust: "REJECT_DUST",
RejectInsufficientFee: "REJECT_INSUFFICIENTFEE",
RejectCheckpoint: "REJECT_CHECKPOINT",
}
// String returns the RejectCode in human-readable form.
func (code RejectCode) String() string {
if s, ok := rejectCodeStrings[code]; ok {
return s
}
return fmt.Sprintf("Unknown RejectCode (%d)", uint8(code))
}
// MsgReject implements the Message interface and represents a bitcoin reject
// message.
//
// This message was not added until protocol version RejectVersion.
type MsgReject struct {
// Cmd is the command for the message which was rejected such as
// as CmdBlock or CmdTx. This can be obtained from the Command function
// of a Message.
Cmd string
// RejectCode is a code indicating why the command was rejected. It
// is encoded as a uint8 on the wire.
Code RejectCode
// Reason is a human-readable string with specific details (over and
// above the reject code) about why the command was rejected.
Reason string
// Hash identifies a specific block or transaction that was rejected
// and therefore only applies the MsgBlock and MsgTx messages.
Hash chainhash.Hash
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgReject) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
if pver < RejectVersion {
str := fmt.Sprintf("reject message invalid for protocol "+
"version %d", pver)
return messageError("MsgReject.BtcDecode", str)
}
// Command that was rejected.
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
cmd, err := readVarStringBuf(r, pver, buf)
if err != nil {
return err
}
msg.Cmd = cmd
// Code indicating why the command was rejected.
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
msg.Code = RejectCode(buf[0])
// Human readable string with specific details (over and above the
// reject code above) about why the command was rejected.
reason, err := readVarStringBuf(r, pver, buf)
if err != nil {
return err
}
msg.Reason = reason
// CmdBlock and CmdTx messages have an additional hash field that
// identifies the specific block or transaction.
if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
_, err := io.ReadFull(r, msg.Hash[:])
if err != nil {
return err
}
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgReject) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
if pver < RejectVersion {
str := fmt.Sprintf("reject message invalid for protocol "+
"version %d", pver)
return messageError("MsgReject.BtcEncode", str)
}
// Command that was rejected.
buf := binarySerializer.Borrow()
defer binarySerializer.Return(buf)
err := writeVarStringBuf(w, pver, msg.Cmd, buf)
if err != nil {
return err
}
// Code indicating why the command was rejected.
buf[0] = byte(msg.Code)
if _, err := w.Write(buf[:1]); err != nil {
return err
}
// Human readable string with specific details (over and above the
// reject code above) about why the command was rejected.
err = writeVarStringBuf(w, pver, msg.Reason, buf)
if err != nil {
return err
}
// CmdBlock and CmdTx messages have an additional hash field that
// identifies the specific block or transaction.
if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
_, err := w.Write(msg.Hash[:])
if err != nil {
return err
}
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgReject) Command() string {
return CmdReject
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgReject) MaxPayloadLength(pver uint32) uint32 {
plen := uint32(0)
// The reject message did not exist before protocol version
// RejectVersion.
if pver >= RejectVersion {
// Unfortunately the bitcoin protocol does not enforce a sane
// limit on the length of the reason, so the max payload is the
// overall maximum message payload.
plen = MaxMessagePayload
}
return plen
}
// NewMsgReject returns a new bitcoin reject message that conforms to the
// Message interface. See MsgReject for details.
func NewMsgReject(command string, code RejectCode, reason string) *MsgReject {
return &MsgReject{
Cmd: command,
Code: code,
Reason: reason,
}
}