From 7af2ab92d221b6eea6c82cf722c597d88ad534b0 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 14 Nov 2024 22:41:50 +0300 Subject: [PATCH 1/5] transaction: specify hash/size behavior better Signed-off-by: Roman Khimov --- pkg/core/transaction/transaction.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index e343443279..1cbe04b989 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -103,7 +103,11 @@ func New(script []byte, gas int64) *Transaction { } } -// Hash returns the hash of the transaction. +// Hash returns the hash of the transaction which is based on serialized +// representation of its fields. Notice that this hash is cached internally +// in [Transaction] for efficiency, so once you call this method it will not +// change even if you change any structure fields. If you need to update the +// hash use encoding/decoding or [Transaction.Copy]. func (t *Transaction) Hash() util.Uint256 { if !t.hashed { if t.createHash() != nil { @@ -208,7 +212,9 @@ func (t *Transaction) decodeBinaryNoSize(br *io.BinReader, buf []byte) { } } -// DecodeBinary implements the Serializable interface. +// DecodeBinary implements the [io.Serializable] interface. It also +// computes and caches transaction hash and size (see [Transaction.Hash] and +// [Transaction.Size]). func (t *Transaction) DecodeBinary(br *io.BinReader) { t.decodeBinaryNoSize(br, nil) @@ -294,7 +300,9 @@ func (t *Transaction) Bytes() []byte { return buf.Bytes() } -// NewTransactionFromBytes decodes byte array into *Transaction. +// NewTransactionFromBytes decodes byte array into [*Transaction]. It also +// computes and caches transaction hash and size (see [Transaction.Hash] and +// [Transaction.Size]). func NewTransactionFromBytes(b []byte) (*Transaction, error) { tx := &Transaction{} r := io.NewBinReaderFromBuf(b) @@ -315,7 +323,10 @@ func (t *Transaction) FeePerByte() int64 { return t.NetworkFee / int64(t.Size()) } -// Size returns size of the serialized transaction. +// Size returns size of the serialized transaction. This value is cached +// in the [Transaction], so once you obtain it no changes to fields will be +// reflected in value returned from this method, use encoding/decoding or +// [Transaction.Copy] if needed. func (t *Transaction) Size() int { if t.size == 0 { t.size = io.GetVarSize(t) @@ -469,8 +480,10 @@ func (t *Transaction) ToStackItem() stackitem.Item { }) } -// Copy creates a deep copy of the Transaction, including all slice fields. Cached values like -// 'hashed' and 'size' are reset to ensure the copy can be modified independently of the original. +// Copy creates a deep copy of the Transaction, including all slice fields. +// Cached values like hash and size are reset to ensure the copy can be +// modified independently of the original (see [Transaction.Hash] and +// [Transaction.Size]). func (t *Transaction) Copy() *Transaction { if t == nil { return nil From bbec25de434b8814576135dd719f4c9e14b64a18 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 14 Nov 2024 22:42:28 +0300 Subject: [PATCH 2/5] block: document hash caching better Signed-off-by: Roman Khimov --- pkg/core/block/header.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/core/block/header.go b/pkg/core/block/header.go index b56e1cbbae..efc6c5efe6 100644 --- a/pkg/core/block/header.go +++ b/pkg/core/block/header.go @@ -73,7 +73,10 @@ type baseAux struct { Witnesses []transaction.Witness `json:"witnesses"` } -// Hash returns the hash of the block. +// Hash returns the hash of the block. Notice that it is cached internally, +// so no matter how you change the [Header] after the first invocation of this +// method it won't change. To get an updated hash in case you're changing +// the [Header] please encode/decode it. func (b *Header) Hash() util.Uint256 { if b.hash.Equals(util.Uint256{}) { b.createHash() @@ -81,7 +84,8 @@ func (b *Header) Hash() util.Uint256 { return b.hash } -// DecodeBinary implements the Serializable interface. +// DecodeBinary implements the [io.Serializable] interface. Notice that it +// also automatically updates the internal hash cache, see [Header.Hash]. func (b *Header) DecodeBinary(br *io.BinReader) { b.decodeHashableFields(br) witnessCount := br.ReadVarUint() From ccbb198a5ba7a5c3fc015c30c75dedcc5cb7ba62 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 14 Nov 2024 22:43:04 +0300 Subject: [PATCH 3/5] core: document trimmed transactions better Eventually we can drop them, but they were present for a long long time. Signed-off-by: Roman Khimov --- pkg/core/block/block.go | 4 +++- pkg/core/transaction/transaction.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/core/block/block.go b/pkg/core/block/block.go index aae9c30c10..9b246a4d3a 100644 --- a/pkg/core/block/block.go +++ b/pkg/core/block/block.go @@ -35,7 +35,9 @@ type Block struct { // Transaction list. Transactions []*transaction.Transaction - // True if this block is created from trimmed data. + // True if this block is created from trimmed data (with a proper + // header and hashes-only transactions). Not a part of a real + // block and is used only by internal packages. Trimmed bool } diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 1cbe04b989..e903329e9b 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -75,7 +75,7 @@ type Transaction struct { hashed bool // Trimmed indicates this is a transaction from trimmed - // data. + // data, meaning it doesn't have anything but hash. Trimmed bool } From 933d522b827b5f4643f6e195fd4391a99900a315 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 14 Nov 2024 22:44:08 +0300 Subject: [PATCH 4/5] block: explain protocol extensions better Signed-off-by: Roman Khimov --- pkg/core/block/header.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/core/block/header.go b/pkg/core/block/header.go index efc6c5efe6..9ca72f1b61 100644 --- a/pkg/core/block/header.go +++ b/pkg/core/block/header.go @@ -16,7 +16,8 @@ import ( // VersionInitial is the default Neo block version. const VersionInitial uint32 = 0 -// Header holds the base info of a block. +// Header holds the base info of a block. Fields follow the P2P format of the +// N3 block header unless noted specifically. type Header struct { // Version of the block. Version uint32 @@ -46,8 +47,13 @@ type Header struct { Script transaction.Witness // StateRootEnabled specifies if the header contains state root. + // It's NeoGo-specific, and is not a part of a standard Neo N3 header, + // it's also never serialized into P2P payload. When it's false + // PrevStateRoot is always zero, when true it works as intended. StateRootEnabled bool - // PrevStateRoot is the state root of the previous block. + // PrevStateRoot is the state root of the previous block. This field + // is relevant only if StateRootEnabled is true which is a + // NeoGo-specific extension of the protocol. PrevStateRoot util.Uint256 // PrimaryIndex is the index of the primary consensus node for this block. PrimaryIndex byte From 9cc16d73f2e5eaa2b323c9b76fb330e20b1f89f2 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 14 Nov 2024 22:44:31 +0300 Subject: [PATCH 5/5] core: a bit better field explanations for header/transaction Signed-off-by: Roman Khimov --- pkg/core/block/header.go | 5 +++-- pkg/core/transaction/transaction.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/core/block/header.go b/pkg/core/block/header.go index 9ca72f1b61..2edd763dc5 100644 --- a/pkg/core/block/header.go +++ b/pkg/core/block/header.go @@ -19,7 +19,7 @@ const VersionInitial uint32 = 0 // Header holds the base info of a block. Fields follow the P2P format of the // N3 block header unless noted specifically. type Header struct { - // Version of the block. + // Version of the block, currently only 0. Version uint32 // hash of the previous block. @@ -43,7 +43,8 @@ type Header struct { // Contract address of the next miner NextConsensus util.Uint160 - // Script used to validate the block + // Witness scripts used for block validation. These scripts + // are not a part of a hashable field set. Script transaction.Witness // StateRootEnabled specifies if the header contains state root. diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index e903329e9b..61d6052706 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -34,13 +34,13 @@ var ErrInvalidWitnessNum = errors.New("number of signers doesn't match witnesses // Transaction is a process recorded in the Neo blockchain. type Transaction struct { - // The trading version which is currently 0. + // Version of the binary transaction format, currently only 0. Version uint8 // Random number to avoid hash collision. Nonce uint32 - // Fee to be burned. + // Fee to be used for script execution and burned. SystemFee int64 // Fee to be distributed to consensus nodes.