Mostly it's about Go 1.22+ syntax with ranging over integers, but it also
prefers ranging over slices where possible (it makes code a little better to
read).
Notice that we have a number of dangerous loops where slices are mutated
during loop execution, many of these can't be converted since we need proper
length evalutation at every iteration.
Signed-off-by: Roman Khimov <roman@nspcc.ru>
This is a bad one.
$ ./bin/neo-go contract testinvokefunction -r https://rpc10.n3.nspcc.ru:10331 0xda65b600f7124ce6c79950c1772a36403104f2be getBlock 5762000
{
"state": "HALT",
"gasconsumed": "202812",
"script": "AtDrVwARwB8MCGdldEJsb2NrDBS+8gQxQDYqd8FQmcfmTBL3ALZl2kFifVtS",
"stack": [
{
"type": "Array",
"value": [
{
"type": "ByteString",
"value": "vq5IPTPEDRhz0JA4cQKIa6/o97pnJt/HfVkDRknd1rg="
},
{
"type": "Integer",
"value": "0"
},
{
"type": "ByteString",
"value": "zFYF3LGaTKdbqVX99shaBUzTq9YjXb0jaPMjk2jdSP4="
},
{
"type": "ByteString",
"value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
},
{
"type": "Integer",
"value": "1722060076994"
},
{
"type": "Integer",
"value": "5293295626238767595"
},
{
"type": "Integer",
"value": "5762000"
},
{
"type": "ByteString",
"value": "LIt05Fpxhl/kXMX3EAGIASyOSQs="
},
{
"type": "Integer",
"value": "0"
}
]
}
],
"exception": null,
"notifications": []
}
$ ./bin/neo-go contract testinvokefunction -r http://seed3.neo.org:10332 0xda65b600f7124ce6c79950c1772a36403104f2be getBlock 5762000
{
"state": "HALT",
"gasconsumed": "202812",
"script": "AtDrVwARwB8MCGdldEJsb2NrDBS+8gQxQDYqd8FQmcfmTBL3ALZl2kFifVtS",
"stack": [
{
"type": "Array",
"value": [
{
"type": "ByteString",
"value": "vq5IPTPEDRhz0JA4cQKIa6/o97pnJt/HfVkDRknd1rg="
},
{
"type": "Integer",
"value": "0"
},
{
"type": "ByteString",
"value": "zFYF3LGaTKdbqVX99shaBUzTq9YjXb0jaPMjk2jdSP4="
},
{
"type": "ByteString",
"value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
},
{
"type": "Integer",
"value": "1722060076994"
},
{
"type": "Integer",
"value": "5293295626238767595"
},
{
"type": "Integer",
"value": "5762000"
},
{
"type": "Integer",
"value": "6"
},
{
"type": "ByteString",
"value": "LIt05Fpxhl/kXMX3EAGIASyOSQs="
},
{
"type": "Integer",
"value": "0"
}
]
}
],
"exception": null,
"notifications": []
}
9 fields vs 10, notice the primary index right after the block number.
Back when ac527650eb initially added Ledger I've
used https://github.com/neo-project/neo/pull/2215 as a reference and it was
correct (no primary index). But then https://github.com/neo-project/neo/pull/2296
came into the C# codebase and while it looked like a pure refactoring it
actually did add the primary index as well and this wasn't noticed. It wasn't
noticed even when 3a4e0caeb8 had touched some
nearby code. In short, we had a completely wrong implementation of this call
for more than three years. But looks like it's not a very popular one.
Signed-off-by: Roman Khimov <roman@nspcc.ru>
Everywhere including examples, external interop APIs, bindings generators
code and in other valuable places. A couple of `interface{}` usages are
intentionally left in the CHANGELOG.md, documentation and tests.
It also brings ToStackItem to Block and Transaction, previously this was
avoided to separate block and transaction packages from VM. But turns out
`transaction` depends on `stackitem` already, so this makes little sense (but
can be shuffled in another way if needed).
Context.Container is still a hash.Hashable because we have a number of
occasions (header or MPT root verification) where there is no ToStackItem
implementation possible. Maybe they can go with `nil` Container, but I don't
want to have this risk for now.
The only user of (*Block).Trim() is in DAO and it already has a nice buffer
usually, so creating another one makes no sense. It also simplifies error
handling a lot.
Use circular buffer which is a bit more appropriate. The problem is that
priority queue accepts and stores equal items which wastes memory even in
normal usage scenario, but it's especially dangerous if the node is stuck for
some reason. In this case it'll accept from peers and put into queue the same
blocks again and again leaking memory up to OOM condition.
Notice that queue length calculation might be wrong in case circular buffer
wraps, but it's not very likely to happen (usually blocks not coming from the
queue are added by consensus and it's not very fast in doing so).
It's not network-tied any more, network is only needed to
sign/verify. Unfortunately we still have to keep network in consensus data
structures because of dbft library interface.
It's used in two places now:
* Blockchain.AddBlock()
This one does transaction duplication check of its own, doing it in
Verify() is just a waste of time. Merkle tree root hash value check is
still relevant though
* Block.DecodeBinary()
We're decoding blocks for the following purposes:
- on restore from dump
The block will be added to the chain via AddBlock() and that will do a
full check of it (if configured to do so)
- on retrieving the block from the DB (DAO)
We trust the DB, if it's gone wild, this check won't really help
- on receiving the block via P2P
It's gonna be put into block queue and then end up in AddBlock() which
will check it
- on receiving the block via RPC (submitblock)
It is to be passed into AddBlock()
- on receiving the block via RPC in a client
That's the only problematic case probably, but RPC client has to trust
the server and it can check for the signature if it really
cares. Or a separate in-client check might be added.
As we can see nothing really requires this verification to be done the way it
is now, AddBlock can just have a Merkle check and DecodeBinary can do fine
without it at all.
It's a no-op and there is nothing we can do about it, header contents could
only be checked against chain state, there is nothing to check for internal
consistency.
NewMerkleTree is a memory hog, we can do better than that:
BenchmarkMerkle/NewMerkleTree-8 13 88434670 ns/op 20828207 B/op 300035 allocs/op
BenchmarkMerkle/CalcMerkleRoot-8 15 69264150 ns/op 0 B/op 0 allocs/op
We make it explicit in the appropriate Block/Transaction structures, not via a
singleton as C# node does. I think this approach has a bit more potential and
allows better packages reuse for different purposes.
Two changes being done here, because they require a lot of updates to
tests. Now we're back into version 0 and we only have one type of
transaction.
It also removes GetType and GetScript interops, both are obsolete in Neo 3.
Our block.Block was JSONized in a bit different fashion than result.Block in
its NextConsensus and Index fields. It's not good for notifications because
third-party clients would probably expect to see the same format. Also, using
completely different Block representation is probably making our client a bit
weaker as this representation is harder to use with other neo-go components.
So use the same approach we took for Transactions and wrap block.Block which is
to be serialized in proper way.
Fix `Script` JSONization along the way, 3.0 node wraps it within `witnesses`.
1. Dropped `Base.ConsensusData` block field
2. Added `Block.ConsensusData` field with `Nonce` and `PrimaryIndex`
3. Removed "Neo.Header.GetConsensusData" and
"AntShares.Header.GetConsensusData" interops