2018-03-04 13:56:49 +00:00
|
|
|
package transaction
|
|
|
|
|
|
|
|
import (
|
2020-01-29 13:38:58 +00:00
|
|
|
"errors"
|
2019-11-27 10:12:04 +00:00
|
|
|
"fmt"
|
|
|
|
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2018-03-04 13:56:49 +00:00
|
|
|
)
|
|
|
|
|
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
|
|
|
const (
|
|
|
|
// MaxTransactionSize is the upper limit size in bytes that a transaction can reach. It is
|
|
|
|
// set to be 102400.
|
|
|
|
MaxTransactionSize = 102400
|
|
|
|
)
|
|
|
|
|
2018-03-04 13:56:49 +00:00
|
|
|
// Transaction is a process recorded in the NEO blockchain.
|
|
|
|
type Transaction struct {
|
|
|
|
// The type of the transaction.
|
2018-03-23 20:36:59 +00:00
|
|
|
Type TXType `json:"type"`
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// The trading version which is currently 0.
|
2018-03-30 06:15:03 +00:00
|
|
|
Version uint8 `json:"version"`
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// Data specific to the type of the transaction.
|
|
|
|
// This is always a pointer to a <Type>Transaction.
|
2018-03-23 20:36:59 +00:00
|
|
|
Data TXer `json:"-"`
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// Transaction attributes.
|
2019-12-09 14:14:10 +00:00
|
|
|
Attributes []Attribute `json:"attributes"`
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// The inputs of the transaction.
|
2019-12-09 14:14:10 +00:00
|
|
|
Inputs []Input `json:"vin"`
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// The outputs of the transaction.
|
2019-12-09 14:14:10 +00:00
|
|
|
Outputs []Output `json:"vout"`
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// The scripts that comes with this transaction.
|
|
|
|
// Scripts exist out of the verification script
|
|
|
|
// and invocation script.
|
2019-12-09 14:14:10 +00:00
|
|
|
Scripts []Witness `json:"scripts"`
|
2018-03-14 09:36:59 +00:00
|
|
|
|
2019-09-23 17:13:44 +00:00
|
|
|
// Hash of the transaction (double SHA256).
|
2018-03-14 09:36:59 +00:00
|
|
|
hash util.Uint256
|
2018-03-17 11:53:21 +00:00
|
|
|
|
2019-09-23 17:13:44 +00:00
|
|
|
// Hash of the transaction used to verify it (single SHA256).
|
|
|
|
verificationHash util.Uint256
|
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
// Trimmed indicates this is a transaction from trimmed
|
|
|
|
// data.
|
2018-03-23 20:36:59 +00:00
|
|
|
Trimmed bool `json:"-"`
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewTrimmedTX returns a trimmed transaction with only its hash
|
|
|
|
// and Trimmed to true.
|
|
|
|
func NewTrimmedTX(hash util.Uint256) *Transaction {
|
|
|
|
return &Transaction{
|
|
|
|
hash: hash,
|
|
|
|
Trimmed: true,
|
|
|
|
}
|
2018-03-14 09:36:59 +00:00
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// Hash returns the hash of the transaction.
|
2018-03-14 09:36:59 +00:00
|
|
|
func (t *Transaction) Hash() util.Uint256 {
|
2018-03-25 10:45:54 +00:00
|
|
|
if t.hash.Equals(util.Uint256{}) {
|
2019-09-23 17:26:53 +00:00
|
|
|
if t.createHash() != nil {
|
|
|
|
panic("failed to compute hash!")
|
|
|
|
}
|
2018-03-25 10:45:54 +00:00
|
|
|
}
|
2018-03-14 09:36:59 +00:00
|
|
|
return t.hash
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:13:44 +00:00
|
|
|
// VerificationHash returns the hash of the transaction used to verify it.
|
|
|
|
func (t *Transaction) VerificationHash() util.Uint256 {
|
|
|
|
if t.verificationHash.Equals(util.Uint256{}) {
|
2019-09-23 17:26:53 +00:00
|
|
|
if t.createHash() != nil {
|
|
|
|
panic("failed to compute hash!")
|
|
|
|
}
|
2019-09-23 17:13:44 +00:00
|
|
|
}
|
|
|
|
return t.verificationHash
|
|
|
|
}
|
|
|
|
|
2018-03-04 13:56:49 +00:00
|
|
|
// AddOutput adds the given output to the transaction outputs.
|
|
|
|
func (t *Transaction) AddOutput(out *Output) {
|
2019-12-09 14:14:10 +00:00
|
|
|
t.Outputs = append(t.Outputs, *out)
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddInput adds the given input to the transaction inputs.
|
|
|
|
func (t *Transaction) AddInput(in *Input) {
|
2019-12-09 14:14:10 +00:00
|
|
|
t.Inputs = append(t.Inputs, *in)
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
|
2020-03-12 16:47:09 +00:00
|
|
|
// AddVerificationHash adds a script attribute for transaction verification.
|
|
|
|
func (t *Transaction) AddVerificationHash(addr util.Uint160) {
|
|
|
|
t.Attributes = append(t.Attributes, Attribute{
|
|
|
|
Usage: Script,
|
|
|
|
Data: addr.BytesBE(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-09-16 16:31:49 +00:00
|
|
|
// DecodeBinary implements Serializable interface.
|
|
|
|
func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
2019-12-12 17:17:50 +00:00
|
|
|
t.Type = TXType(br.ReadB())
|
|
|
|
t.Version = uint8(br.ReadB())
|
2019-09-16 16:31:49 +00:00
|
|
|
t.decodeData(br)
|
2018-03-04 13:56:49 +00:00
|
|
|
|
2019-11-14 07:50:03 +00:00
|
|
|
br.ReadArray(&t.Attributes)
|
|
|
|
br.ReadArray(&t.Inputs)
|
|
|
|
br.ReadArray(&t.Outputs)
|
2020-02-28 13:53:26 +00:00
|
|
|
for i := range t.Outputs {
|
|
|
|
if t.Outputs[i].Amount.LessThan(0) {
|
|
|
|
br.Err = errors.New("negative output")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2019-11-14 07:50:03 +00:00
|
|
|
br.ReadArray(&t.Scripts)
|
2018-03-04 13:56:49 +00:00
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
// Create the hash of the transaction at decode, so we dont need
|
|
|
|
// to do it anymore.
|
2019-09-16 16:31:49 +00:00
|
|
|
if br.Err == nil {
|
|
|
|
br.Err = t.createHash()
|
|
|
|
}
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
|
2019-09-16 16:31:49 +00:00
|
|
|
func (t *Transaction) decodeData(r *io.BinReader) {
|
2018-03-04 13:56:49 +00:00
|
|
|
switch t.Type {
|
|
|
|
case InvocationType:
|
2019-08-30 07:44:59 +00:00
|
|
|
t.Data = &InvocationTX{Version: t.Version}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*InvocationTX).DecodeBinary(r)
|
2018-03-04 13:56:49 +00:00
|
|
|
case MinerType:
|
|
|
|
t.Data = &MinerTX{}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*MinerTX).DecodeBinary(r)
|
2018-03-04 13:56:49 +00:00
|
|
|
case ClaimType:
|
|
|
|
t.Data = &ClaimTX{}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*ClaimTX).DecodeBinary(r)
|
2018-03-14 09:36:59 +00:00
|
|
|
case ContractType:
|
|
|
|
t.Data = &ContractTX{}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*ContractTX).DecodeBinary(r)
|
2018-03-17 11:53:21 +00:00
|
|
|
case RegisterType:
|
|
|
|
t.Data = &RegisterTX{}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*RegisterTX).DecodeBinary(r)
|
2018-03-17 11:53:21 +00:00
|
|
|
case IssueType:
|
|
|
|
t.Data = &IssueTX{}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*IssueTX).DecodeBinary(r)
|
2018-03-21 16:11:04 +00:00
|
|
|
case EnrollmentType:
|
|
|
|
t.Data = &EnrollmentTX{}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*EnrollmentTX).DecodeBinary(r)
|
2018-03-25 10:45:54 +00:00
|
|
|
case PublishType:
|
2019-08-27 16:58:50 +00:00
|
|
|
t.Data = &PublishTX{Version: t.Version}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*PublishTX).DecodeBinary(r)
|
2018-03-25 10:45:54 +00:00
|
|
|
case StateType:
|
|
|
|
t.Data = &StateTX{}
|
2019-09-16 16:31:49 +00:00
|
|
|
t.Data.(*StateTX).DecodeBinary(r)
|
2018-03-17 11:53:21 +00:00
|
|
|
default:
|
2019-11-27 10:12:04 +00:00
|
|
|
r.Err = fmt.Errorf("invalid TX type %x", t.Type)
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-16 16:31:49 +00:00
|
|
|
// EncodeBinary implements Serializable interface.
|
|
|
|
func (t *Transaction) EncodeBinary(bw *io.BinWriter) {
|
|
|
|
t.encodeHashableFields(bw)
|
2019-11-13 07:36:29 +00:00
|
|
|
bw.WriteArray(t.Scripts)
|
2018-03-14 09:36:59 +00:00
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// encodeHashableFields encodes the fields that are not used for
|
2018-03-14 09:36:59 +00:00
|
|
|
// signing the transaction, which are all fields except the scripts.
|
2019-09-16 16:31:49 +00:00
|
|
|
func (t *Transaction) encodeHashableFields(bw *io.BinWriter) {
|
2020-03-03 18:20:58 +00:00
|
|
|
noData := t.Type == ContractType
|
|
|
|
if t.Data == nil && !noData {
|
2020-01-29 13:38:58 +00:00
|
|
|
bw.Err = errors.New("transaction has no data")
|
|
|
|
return
|
|
|
|
}
|
2019-12-12 17:17:50 +00:00
|
|
|
bw.WriteB(byte(t.Type))
|
|
|
|
bw.WriteB(byte(t.Version))
|
2018-03-14 09:36:59 +00:00
|
|
|
|
|
|
|
// Underlying TXer.
|
2020-03-03 18:20:58 +00:00
|
|
|
if !noData {
|
|
|
|
t.Data.EncodeBinary(bw)
|
|
|
|
}
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// Attributes
|
2019-11-13 07:36:29 +00:00
|
|
|
bw.WriteArray(t.Attributes)
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// Inputs
|
2019-11-13 07:36:29 +00:00
|
|
|
bw.WriteArray(t.Inputs)
|
2018-03-04 13:56:49 +00:00
|
|
|
|
|
|
|
// Outputs
|
2019-11-13 07:36:29 +00:00
|
|
|
bw.WriteArray(t.Outputs)
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
|
|
|
|
2018-03-25 10:45:54 +00:00
|
|
|
// createHash creates the hash of the transaction.
|
|
|
|
func (t *Transaction) createHash() error {
|
2019-09-16 09:18:13 +00:00
|
|
|
buf := io.NewBufBinWriter()
|
2019-09-16 16:31:49 +00:00
|
|
|
t.encodeHashableFields(buf.BinWriter)
|
|
|
|
if buf.Err != nil {
|
|
|
|
return buf.Err
|
2018-03-25 10:45:54 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:13:44 +00:00
|
|
|
b := buf.Bytes()
|
|
|
|
t.verificationHash = hash.Sha256(b)
|
2019-12-12 15:01:30 +00:00
|
|
|
t.hash = hash.Sha256(t.verificationHash.BytesBE())
|
2018-03-25 10:45:54 +00:00
|
|
|
|
|
|
|
return nil
|
2018-03-04 13:56:49 +00:00
|
|
|
}
|
2018-03-21 16:11:04 +00:00
|
|
|
|
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
|
|
|
// GroupOutputByAssetID groups all TX outputs by their assetID.
|
|
|
|
func (t Transaction) GroupOutputByAssetID() map[util.Uint256][]*Output {
|
|
|
|
m := make(map[util.Uint256][]*Output)
|
2019-12-09 14:14:10 +00:00
|
|
|
for i := range t.Outputs {
|
|
|
|
hash := t.Outputs[i].AssetID
|
|
|
|
m[hash] = append(m[hash], &t.Outputs[i])
|
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2020-02-28 07:05:13 +00:00
|
|
|
// GetSignedPart returns a part of the transaction which must be signed.
|
|
|
|
func (t *Transaction) GetSignedPart() []byte {
|
|
|
|
buf := io.NewBufBinWriter()
|
|
|
|
t.encodeHashableFields(buf.BinWriter)
|
|
|
|
if buf.Err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return buf.Bytes()
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// Bytes converts the transaction to []byte
|
2019-02-20 17:39:32 +00:00
|
|
|
func (t *Transaction) Bytes() []byte {
|
2019-09-16 09:18:13 +00:00
|
|
|
buf := io.NewBufBinWriter()
|
2019-09-16 16:31:49 +00:00
|
|
|
t.EncodeBinary(buf.BinWriter)
|
|
|
|
if buf.Err != nil {
|
2019-02-20 17:39:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return buf.Bytes()
|
|
|
|
}
|