github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/network/payload/notary_request.go (about)

     1  package payload
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
     8  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
     9  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    10  	"github.com/nspcc-dev/neo-go/pkg/io"
    11  	"github.com/nspcc-dev/neo-go/pkg/util"
    12  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    13  )
    14  
    15  // P2PNotaryRequest contains main and fallback transactions for the Notary service.
    16  type P2PNotaryRequest struct {
    17  	MainTransaction     *transaction.Transaction `json:"maintx"`
    18  	FallbackTransaction *transaction.Transaction `json:"fallbacktx"`
    19  
    20  	Witness transaction.Witness
    21  
    22  	hash util.Uint256
    23  }
    24  
    25  // NewP2PNotaryRequestFromBytes decodes a P2PNotaryRequest from the given bytes.
    26  func NewP2PNotaryRequestFromBytes(b []byte) (*P2PNotaryRequest, error) {
    27  	req := &P2PNotaryRequest{}
    28  	br := io.NewBinReaderFromBuf(b)
    29  	req.DecodeBinary(br)
    30  	if br.Err != nil {
    31  		return nil, br.Err
    32  	}
    33  	if br.Len() != 0 {
    34  		return nil, errors.New("additional data after the payload")
    35  	}
    36  	return req, nil
    37  }
    38  
    39  // Bytes returns serialized P2PNotaryRequest payload.
    40  func (r *P2PNotaryRequest) Bytes() ([]byte, error) {
    41  	buf := io.NewBufBinWriter()
    42  	r.EncodeBinary(buf.BinWriter)
    43  	if buf.Err != nil {
    44  		return nil, buf.Err
    45  	}
    46  	return buf.Bytes(), nil
    47  }
    48  
    49  // Hash returns payload's hash.
    50  func (r *P2PNotaryRequest) Hash() util.Uint256 {
    51  	if r.hash.Equals(util.Uint256{}) {
    52  		if r.createHash() != nil {
    53  			panic("failed to compute hash!")
    54  		}
    55  	}
    56  	return r.hash
    57  }
    58  
    59  // createHash creates hash of the payload.
    60  func (r *P2PNotaryRequest) createHash() error {
    61  	buf := io.NewBufBinWriter()
    62  	r.encodeHashableFields(buf.BinWriter)
    63  	r.hash = hash.Sha256(buf.Bytes())
    64  	return nil
    65  }
    66  
    67  // DecodeBinaryUnsigned reads payload from the w excluding signature.
    68  func (r *P2PNotaryRequest) decodeHashableFields(br *io.BinReader) {
    69  	r.MainTransaction = &transaction.Transaction{}
    70  	r.FallbackTransaction = &transaction.Transaction{}
    71  	r.MainTransaction.DecodeBinary(br)
    72  	r.FallbackTransaction.DecodeBinary(br)
    73  	if br.Err == nil {
    74  		br.Err = r.isValid()
    75  	}
    76  	if br.Err == nil {
    77  		br.Err = r.createHash()
    78  	}
    79  }
    80  
    81  // DecodeBinary implements the io.Serializable interface.
    82  func (r *P2PNotaryRequest) DecodeBinary(br *io.BinReader) {
    83  	r.decodeHashableFields(br)
    84  	if br.Err == nil {
    85  		r.Witness.DecodeBinary(br)
    86  	}
    87  }
    88  
    89  // encodeHashableFields writes payload to the w excluding signature.
    90  func (r *P2PNotaryRequest) encodeHashableFields(bw *io.BinWriter) {
    91  	r.MainTransaction.EncodeBinary(bw)
    92  	r.FallbackTransaction.EncodeBinary(bw)
    93  }
    94  
    95  // EncodeBinary implements the Serializable interface.
    96  func (r *P2PNotaryRequest) EncodeBinary(bw *io.BinWriter) {
    97  	r.encodeHashableFields(bw)
    98  	r.Witness.EncodeBinary(bw)
    99  }
   100  
   101  func (r *P2PNotaryRequest) isValid() error {
   102  	nKeysMain := r.MainTransaction.GetAttributes(transaction.NotaryAssistedT)
   103  	if len(nKeysMain) == 0 {
   104  		return errors.New("main transaction should have NotaryAssisted attribute")
   105  	}
   106  	if nKeysMain[0].Value.(*transaction.NotaryAssisted).NKeys == 0 {
   107  		return errors.New("main transaction should have NKeys > 0")
   108  	}
   109  	if len(r.FallbackTransaction.Signers) != 2 {
   110  		return errors.New("fallback transaction should have two signers")
   111  	}
   112  	if len(r.FallbackTransaction.Scripts[0].InvocationScript) != 66 ||
   113  		len(r.FallbackTransaction.Scripts[0].VerificationScript) != 0 ||
   114  		!bytes.HasPrefix(r.FallbackTransaction.Scripts[0].InvocationScript, []byte{byte(opcode.PUSHDATA1), keys.SignatureLen}) {
   115  		return errors.New("fallback transaction has invalid dummy Notary witness")
   116  	}
   117  	if !r.FallbackTransaction.HasAttribute(transaction.NotValidBeforeT) {
   118  		return errors.New("fallback transactions should have NotValidBefore attribute")
   119  	}
   120  	conflicts := r.FallbackTransaction.GetAttributes(transaction.ConflictsT)
   121  	if len(conflicts) != 1 {
   122  		return errors.New("fallback transaction should have one Conflicts attribute")
   123  	}
   124  	if conflicts[0].Value.(*transaction.Conflicts).Hash != r.MainTransaction.Hash() {
   125  		return errors.New("fallback transaction does not conflict with the main transaction")
   126  	}
   127  	nKeysFallback := r.FallbackTransaction.GetAttributes(transaction.NotaryAssistedT)
   128  	if len(nKeysFallback) == 0 {
   129  		return errors.New("fallback transaction should have NotaryAssisted attribute")
   130  	}
   131  	if nKeysFallback[0].Value.(*transaction.NotaryAssisted).NKeys != 0 {
   132  		return errors.New("fallback transaction should have NKeys = 0")
   133  	}
   134  	if r.MainTransaction.ValidUntilBlock != r.FallbackTransaction.ValidUntilBlock {
   135  		return errors.New("both main and fallback transactions should have the same ValidUntil value")
   136  	}
   137  	return nil
   138  }
   139  
   140  // Copy creates a deep copy of P2PNotaryRequest. It creates deep copy of the MainTransaction,
   141  // FallbackTransaction and Witness, including all slice fields. Cached values like
   142  // 'hashed' and 'size' of the transactions are reset to ensure the copy can be modified
   143  // independently of the original.
   144  func (r *P2PNotaryRequest) Copy() *P2PNotaryRequest {
   145  	return &P2PNotaryRequest{
   146  		MainTransaction:     r.MainTransaction.Copy(),
   147  		FallbackTransaction: r.FallbackTransaction.Copy(),
   148  		Witness:             r.Witness.Copy(),
   149  	}
   150  }