github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/transaction/transaction.go (about) 1 package transaction 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "math" 10 "math/big" 11 "math/rand" 12 13 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 14 "github.com/nspcc-dev/neo-go/pkg/encoding/address" 15 "github.com/nspcc-dev/neo-go/pkg/io" 16 "github.com/nspcc-dev/neo-go/pkg/util" 17 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 18 ) 19 20 const ( 21 // MaxScriptLength is the limit for transaction's script length. 22 MaxScriptLength = math.MaxUint16 23 // MaxTransactionSize is the upper limit size in bytes that a transaction can reach. It is 24 // set to be 102400. 25 MaxTransactionSize = 102400 26 // MaxAttributes is maximum number of attributes including signers that can be contained 27 // within a transaction. It is set to be 16. 28 MaxAttributes = 16 29 ) 30 31 // ErrInvalidWitnessNum returns when the number of witnesses does not match signers. 32 var ErrInvalidWitnessNum = errors.New("number of signers doesn't match witnesses") 33 34 // Transaction is a process recorded in the Neo blockchain. 35 type Transaction struct { 36 // The trading version which is currently 0. 37 Version uint8 38 39 // Random number to avoid hash collision. 40 Nonce uint32 41 42 // Fee to be burned. 43 SystemFee int64 44 45 // Fee to be distributed to consensus nodes. 46 NetworkFee int64 47 48 // Maximum blockchain height exceeding which 49 // transaction should fail verification. E.g. if VUB=N, then transaction 50 // can be accepted to block with index N, but not to block with index N+1. 51 ValidUntilBlock uint32 52 53 // Code to run in NeoVM for this transaction. 54 Script []byte 55 56 // Transaction attributes. 57 Attributes []Attribute 58 59 // Transaction signers list (starts with Sender). 60 Signers []Signer 61 62 // The scripts that comes with this transaction. 63 // Scripts exist out of the verification script 64 // and invocation script. 65 Scripts []Witness 66 67 // size is transaction's serialized size. 68 size int 69 70 // Hash of the transaction (double SHA256). 71 hash util.Uint256 72 73 // Whether hash is correct. 74 hashed bool 75 76 // Trimmed indicates this is a transaction from trimmed 77 // data. 78 Trimmed bool 79 } 80 81 // NewTrimmedTX returns a trimmed transaction with only its hash 82 // and Trimmed to true. 83 func NewTrimmedTX(hash util.Uint256) *Transaction { 84 return &Transaction{ 85 hash: hash, 86 hashed: true, 87 Trimmed: true, 88 } 89 } 90 91 // New returns a new transaction to execute given script and pay given system 92 // fee. 93 func New(script []byte, gas int64) *Transaction { 94 return &Transaction{ 95 Version: 0, 96 Nonce: rand.Uint32(), 97 Script: script, 98 SystemFee: gas, 99 Attributes: []Attribute{}, 100 Signers: []Signer{}, 101 Scripts: []Witness{}, 102 } 103 } 104 105 // Hash returns the hash of the transaction. 106 func (t *Transaction) Hash() util.Uint256 { 107 if !t.hashed { 108 if t.createHash() != nil { 109 panic("failed to compute hash!") 110 } 111 } 112 return t.hash 113 } 114 115 // HasAttribute returns true iff t has an attribute of type typ. 116 func (t *Transaction) HasAttribute(typ AttrType) bool { 117 for i := range t.Attributes { 118 if t.Attributes[i].Type == typ { 119 return true 120 } 121 } 122 return false 123 } 124 125 // GetAttributes returns the list of transaction's attributes of the given type. 126 // Returns nil in case if attributes not found. 127 func (t *Transaction) GetAttributes(typ AttrType) []Attribute { 128 var result []Attribute 129 for _, attr := range t.Attributes { 130 if attr.Type == typ { 131 result = append(result, attr) 132 } 133 } 134 return result 135 } 136 137 // decodeHashableFields decodes the fields that are used for signing the 138 // transaction, which are all fields except the scripts. 139 func (t *Transaction) decodeHashableFields(br *io.BinReader, buf []byte) { 140 var start, end int 141 142 if buf != nil { 143 start = len(buf) - br.Len() 144 } 145 t.Version = uint8(br.ReadB()) 146 t.Nonce = br.ReadU32LE() 147 t.SystemFee = int64(br.ReadU64LE()) 148 t.NetworkFee = int64(br.ReadU64LE()) 149 t.ValidUntilBlock = br.ReadU32LE() 150 nsigners := br.ReadVarUint() 151 if br.Err != nil { 152 return 153 } 154 if nsigners > MaxAttributes { 155 br.Err = errors.New("too many signers") 156 return 157 } else if nsigners == 0 { 158 br.Err = errors.New("missing signers") 159 return 160 } 161 t.Signers = make([]Signer, nsigners) 162 for i := 0; i < int(nsigners); i++ { 163 t.Signers[i].DecodeBinary(br) 164 } 165 nattrs := br.ReadVarUint() 166 if nattrs > MaxAttributes-nsigners { 167 br.Err = errors.New("too many attributes") 168 return 169 } 170 t.Attributes = make([]Attribute, nattrs) 171 for i := 0; i < int(nattrs); i++ { 172 t.Attributes[i].DecodeBinary(br) 173 } 174 t.Script = br.ReadVarBytes(MaxScriptLength) 175 if br.Err == nil { 176 br.Err = t.isValid() 177 } 178 if buf != nil { 179 end = len(buf) - br.Len() 180 t.hash = hash.Sha256(buf[start:end]) 181 t.hashed = true 182 } 183 } 184 185 func (t *Transaction) decodeBinaryNoSize(br *io.BinReader, buf []byte) { 186 t.decodeHashableFields(br, buf) 187 if br.Err != nil { 188 return 189 } 190 nscripts := br.ReadVarUint() 191 if nscripts > MaxAttributes { 192 br.Err = errors.New("too many witnesses") 193 return 194 } else if int(nscripts) != len(t.Signers) { 195 br.Err = fmt.Errorf("%w: %d vs %d", ErrInvalidWitnessNum, len(t.Signers), nscripts) 196 return 197 } 198 t.Scripts = make([]Witness, nscripts) 199 for i := 0; i < int(nscripts); i++ { 200 t.Scripts[i].DecodeBinary(br) 201 } 202 203 // Create the hash of the transaction at decode, so we dont need 204 // to do it anymore. 205 if br.Err == nil && buf == nil { 206 br.Err = t.createHash() 207 } 208 } 209 210 // DecodeBinary implements the Serializable interface. 211 func (t *Transaction) DecodeBinary(br *io.BinReader) { 212 t.decodeBinaryNoSize(br, nil) 213 214 if br.Err == nil { 215 _ = t.Size() 216 } 217 } 218 219 // EncodeBinary implements the Serializable interface. 220 func (t *Transaction) EncodeBinary(bw *io.BinWriter) { 221 t.encodeHashableFields(bw) 222 bw.WriteVarUint(uint64(len(t.Scripts))) 223 for i := range t.Scripts { 224 t.Scripts[i].EncodeBinary(bw) 225 } 226 } 227 228 // encodeHashableFields encodes the fields that are not used for 229 // signing the transaction, which are all fields except the scripts. 230 func (t *Transaction) encodeHashableFields(bw *io.BinWriter) { 231 bw.WriteB(byte(t.Version)) 232 bw.WriteU32LE(t.Nonce) 233 bw.WriteU64LE(uint64(t.SystemFee)) 234 bw.WriteU64LE(uint64(t.NetworkFee)) 235 bw.WriteU32LE(t.ValidUntilBlock) 236 bw.WriteVarUint(uint64(len(t.Signers))) 237 for i := range t.Signers { 238 t.Signers[i].EncodeBinary(bw) 239 } 240 bw.WriteVarUint(uint64(len(t.Attributes))) 241 for i := range t.Attributes { 242 t.Attributes[i].EncodeBinary(bw) 243 } 244 bw.WriteVarBytes(t.Script) 245 } 246 247 // EncodeHashableFields returns serialized transaction's fields which are hashed. 248 func (t *Transaction) EncodeHashableFields() ([]byte, error) { 249 bw := io.NewBufBinWriter() 250 t.encodeHashableFields(bw.BinWriter) 251 if bw.Err != nil { 252 return nil, bw.Err 253 } 254 return bw.Bytes(), nil 255 } 256 257 // createHash creates the hash of the transaction. 258 func (t *Transaction) createHash() error { 259 shaHash := sha256.New() 260 bw := io.NewBinWriterFromIO(shaHash) 261 t.encodeHashableFields(bw) 262 if bw.Err != nil { 263 return bw.Err 264 } 265 266 shaHash.Sum(t.hash[:0]) 267 t.hashed = true 268 return nil 269 } 270 271 // DecodeHashableFields decodes a part of transaction which should be hashed. 272 func (t *Transaction) DecodeHashableFields(buf []byte) error { 273 r := io.NewBinReaderFromBuf(buf) 274 t.decodeHashableFields(r, buf) 275 if r.Err != nil { 276 return r.Err 277 } 278 // Ensure all the data was read. 279 if r.Len() != 0 { 280 return errors.New("additional data after the signed part") 281 } 282 t.Scripts = make([]Witness, 0) 283 return nil 284 } 285 286 // Bytes converts the transaction to []byte. 287 func (t *Transaction) Bytes() []byte { 288 buf := io.NewBufBinWriter() 289 t.EncodeBinary(buf.BinWriter) 290 if buf.Err != nil { 291 return nil 292 } 293 return buf.Bytes() 294 } 295 296 // NewTransactionFromBytes decodes byte array into *Transaction. 297 func NewTransactionFromBytes(b []byte) (*Transaction, error) { 298 tx := &Transaction{} 299 r := io.NewBinReaderFromBuf(b) 300 tx.decodeBinaryNoSize(r, b) 301 if r.Err != nil { 302 return nil, r.Err 303 } 304 if r.Len() != 0 { 305 return nil, errors.New("additional data after the transaction") 306 } 307 tx.size = len(b) 308 return tx, nil 309 } 310 311 // FeePerByte returns NetworkFee of the transaction divided by 312 // its size. 313 func (t *Transaction) FeePerByte() int64 { 314 return t.NetworkFee / int64(t.Size()) 315 } 316 317 // Size returns size of the serialized transaction. 318 func (t *Transaction) Size() int { 319 if t.size == 0 { 320 t.size = io.GetVarSize(t) 321 } 322 return t.size 323 } 324 325 // Sender returns the sender of the transaction which is always on the first place 326 // in the transaction's signers list. 327 func (t *Transaction) Sender() util.Uint160 { 328 if len(t.Signers) == 0 { 329 panic("transaction does not have signers") 330 } 331 return t.Signers[0].Account 332 } 333 334 // transactionJSON is a wrapper for Transaction and 335 // used for correct marhalling of transaction.Data. 336 type transactionJSON struct { 337 TxID util.Uint256 `json:"hash"` 338 Size int `json:"size"` 339 Version uint8 `json:"version"` 340 Nonce uint32 `json:"nonce"` 341 Sender string `json:"sender"` 342 SystemFee int64 `json:"sysfee,string"` 343 NetworkFee int64 `json:"netfee,string"` 344 ValidUntilBlock uint32 `json:"validuntilblock"` 345 Attributes []Attribute `json:"attributes"` 346 Signers []Signer `json:"signers"` 347 Script []byte `json:"script"` 348 Scripts []Witness `json:"witnesses"` 349 } 350 351 // MarshalJSON implements the json.Marshaler interface. 352 func (t *Transaction) MarshalJSON() ([]byte, error) { 353 tx := transactionJSON{ 354 TxID: t.Hash(), 355 Size: t.Size(), 356 Version: t.Version, 357 Nonce: t.Nonce, 358 Sender: address.Uint160ToString(t.Sender()), 359 ValidUntilBlock: t.ValidUntilBlock, 360 Attributes: t.Attributes, 361 Signers: t.Signers, 362 Script: t.Script, 363 Scripts: t.Scripts, 364 SystemFee: t.SystemFee, 365 NetworkFee: t.NetworkFee, 366 } 367 return json.Marshal(tx) 368 } 369 370 // UnmarshalJSON implements the json.Unmarshaler interface. 371 func (t *Transaction) UnmarshalJSON(data []byte) error { 372 tx := new(transactionJSON) 373 if err := json.Unmarshal(data, tx); err != nil { 374 return err 375 } 376 t.Version = tx.Version 377 t.Nonce = tx.Nonce 378 t.ValidUntilBlock = tx.ValidUntilBlock 379 t.Attributes = tx.Attributes 380 t.Signers = tx.Signers 381 t.Scripts = tx.Scripts 382 t.SystemFee = tx.SystemFee 383 t.NetworkFee = tx.NetworkFee 384 t.Script = tx.Script 385 if t.Hash() != tx.TxID { 386 return errors.New("txid doesn't match transaction hash") 387 } 388 if t.Size() != tx.Size { 389 return errors.New("'size' doesn't match transaction size") 390 } 391 392 return t.isValid() 393 } 394 395 // Various errors for transaction validation. 396 var ( 397 ErrInvalidVersion = errors.New("only version 0 is supported") 398 ErrNegativeSystemFee = errors.New("negative system fee") 399 ErrNegativeNetworkFee = errors.New("negative network fee") 400 ErrTooBigFees = errors.New("too big fees: int64 overflow") 401 ErrEmptySigners = errors.New("signers array should contain sender") 402 ErrNonUniqueSigners = errors.New("transaction signers should be unique") 403 ErrInvalidAttribute = errors.New("invalid attribute") 404 ErrEmptyScript = errors.New("no script") 405 ) 406 407 // isValid checks whether decoded/unmarshalled transaction has all fields valid. 408 func (t *Transaction) isValid() error { 409 if t.Version > 0 { 410 return ErrInvalidVersion 411 } 412 if t.SystemFee < 0 { 413 return ErrNegativeSystemFee 414 } 415 if t.NetworkFee < 0 { 416 return ErrNegativeNetworkFee 417 } 418 if t.NetworkFee+t.SystemFee < t.SystemFee { 419 return ErrTooBigFees 420 } 421 if len(t.Signers) == 0 { 422 return ErrEmptySigners 423 } 424 for i := 0; i < len(t.Signers); i++ { 425 for j := i + 1; j < len(t.Signers); j++ { 426 if t.Signers[i].Account.Equals(t.Signers[j].Account) { 427 return ErrNonUniqueSigners 428 } 429 } 430 } 431 attrs := map[AttrType]bool{} 432 for i := range t.Attributes { 433 typ := t.Attributes[i].Type 434 if !typ.allowMultiple() { 435 if attrs[typ] { 436 return fmt.Errorf("%w: multiple '%s' attributes", ErrInvalidAttribute, typ.String()) 437 } 438 attrs[typ] = true 439 } 440 } 441 if len(t.Script) == 0 { 442 return ErrEmptyScript 443 } 444 return nil 445 } 446 447 // HasSigner returns true in case if hash is present in the list of signers. 448 func (t *Transaction) HasSigner(hash util.Uint160) bool { 449 for _, h := range t.Signers { 450 if h.Account.Equals(hash) { 451 return true 452 } 453 } 454 return false 455 } 456 457 // ToStackItem converts Transaction to stackitem.Item. 458 func (t *Transaction) ToStackItem() stackitem.Item { 459 return stackitem.NewArray([]stackitem.Item{ 460 stackitem.NewByteArray(t.Hash().BytesBE()), 461 stackitem.NewBigInteger(big.NewInt(int64(t.Version))), 462 stackitem.NewBigInteger(big.NewInt(int64(t.Nonce))), 463 stackitem.NewByteArray(t.Sender().BytesBE()), 464 stackitem.NewBigInteger(big.NewInt(int64(t.SystemFee))), 465 stackitem.NewBigInteger(big.NewInt(int64(t.NetworkFee))), 466 stackitem.NewBigInteger(big.NewInt(int64(t.ValidUntilBlock))), 467 stackitem.NewByteArray(t.Script), 468 }) 469 } 470 471 // Copy creates a deep copy of the Transaction, including all slice fields. Cached values like 472 // 'hashed' and 'size' are reset to ensure the copy can be modified independently of the original. 473 func (t *Transaction) Copy() *Transaction { 474 if t == nil { 475 return nil 476 } 477 cp := *t 478 if t.Attributes != nil { 479 cp.Attributes = make([]Attribute, len(t.Attributes)) 480 for i, attr := range t.Attributes { 481 cp.Attributes[i] = *attr.Copy() 482 } 483 } 484 if t.Signers != nil { 485 cp.Signers = make([]Signer, len(t.Signers)) 486 for i, signer := range t.Signers { 487 cp.Signers[i] = *signer.Copy() 488 } 489 } 490 if t.Scripts != nil { 491 cp.Scripts = make([]Witness, len(t.Scripts)) 492 for i, script := range t.Scripts { 493 cp.Scripts[i] = script.Copy() 494 } 495 } 496 cp.Script = bytes.Clone(t.Script) 497 498 cp.hashed = false 499 cp.size = 0 500 cp.hash = util.Uint256{} 501 return &cp 502 }