Fixes#809.
Basically, there are three alternative approaches to fixing it:
* allowing both []byte and string for ByteArrayType value
minimal invasion into existing code, but ugly as hell and will probably
backfire at some point
* storing string values in ByteArrayType
incurs quite a number of type conversions (and associated data copying),
though note that these values are not changed usually, so dynamic
properties of []byte are almost irrelevant here
* storing only []byte values in ByteArrayType
makes it impossible to use them as map keys which can be solved in several
ways:
- via an interface (Marshalable)
which is good, but makes testing and comparing values in general harder,
because of keys mismatch
- using serialized Parameter as a key (in a string)
which will need some additional marshaling/unmarshaling
- converting MapType from map to a slice of key-value pairs
not a bad idea as we don't use this map as a map really, the type
itself is all about input/output for real VM types and this approach is
also a bit closer to JSON representation of the Map
C# pushes value and key to the stack of non-serialized items, so key gets
serialized first followed by value. Fixes#806.
Notice though that neither IDictionary in C#, nor map in Go have elements
ordered, so we can easily get a difference in KV pairs order and it would be
impossible to fix.
Old implementation could view 0x62 byte in
a script as a JMP instruction irregardless of whether it is
a real opcode or a part of a parameter of another instruction.
In this commit instructions are decoded together with parameters
during jump label rewriting.
Seeing some
blockQueue: failed adding block into the blockchain {"error": "failed to store notifications: not supported", "blockHeight": 713984, "nextIndex": 713985}
in logs is not very helpful.
Recursive execute() calls can affect gas calculation.
This commit makes execute() be called only for real opcodes
and moves duplicate logic for CALL/JMP into a separate function.
Big.Int Bytes()/SetBytes() methods are not symmetric.
Moreover we need to mimic C# node behavior:
- if a positive number has MSB set, 0x00 byte should be appended
to distinguish positive number from negatives
- negative numbers should serialize as two's-complement
Preseed the scriptHash value when we already know it. Eliminates this time
waste from the pprof graph, but doesn't really change anything in the 1.4M ->
1.5M 100K mainnet blocks import test.
These don't belong to VM as they compile some Go code and run it in a VM. One
may call them integration tests, but I prefer to attribute them to
compiler. Moving these tests into pkg/compiler also allows to properly count
the compiler coverage they add:
-ok github.com/CityOfZion/neo-go/pkg/compiler (cached) coverage: 69.7% of statements
+ok github.com/CityOfZion/neo-go/pkg/compiler (cached) coverage: 84.2% of statements
This change also fixes `contant` typo and removes fake packages exposed to the
public by moving foo/bar/foobar into the testdata directory.
This solves two problems:
* adds support for shortened SYSCALL form that uses IDs (similar to #434, but
for NEO 2.0, supporting both forms), which is important for compatibility
with C# node and mainnet chain that uses it from some height
* reworks interop plugging to use callbacks rather than appending to the map,
these map mangling functions are clearly visible in the VM profiling
statistics and we want spawning a VM to be fast, so it makes sense
optimizing it. This change moves most of the work to the init() phase
making VM setup cheaper.
Caveats:
* InteropNameToID accepts `[]byte` because that's the thing we have in
SYSCALL processing and that's the most often usecase for it, it leads to
some conversions in other places but that's acceptable because those are
either tests or init()
* three getInterop functions are: `getDefaultVMInterop`, `getSystemInterop`
and `getNeoInterop`
Our 100K (1.4M->1.5M) block import time improves by ~4% with this change.
Add `Roll` method to Stack that doesn't pop and push values and use it for
ROLL and ROT.
1.4M->1.5M 100K block import test before:
real 3m44,292s
user 5m43,494s
sys 0m34,741s
After:
real 3m40,449s
user 5m42,701s
sys 0m35,500s
Add `Swap` method to the Stack and use it for both SWAP and XSWAP. Avoid
element popping and pushing (and associated accounting costs).
1.4M->1.5M 100K block import test before:
real 3m51,885s
user 5m54,744s
sys 0m38,444s
After:
real 3m44,292s
user 5m43,494s
sys 0m34,741s
Creating a new BinReader for every instruction is a bit too much and it adds
about 1% overhead on block import (and actually is quite visible in the VM
profiling statistics). So use a bit more ugly but efficient method.
go vet is not happy about them:
pkg/io/binaryReader.go:92:21: method ReadByte() byte should have signature ReadByte() (byte, error)
pkg/io/binaryWriter.go:75:21: method WriteByte(u8 byte) should have signature WriteByte(byte) error
This seriously improves the serialization/deserialization performance for
several reasons:
* no time spent in `binary` reflection
* no memory allocations being made on every read/write
* uses fast ReadBytes everywhere it's appropriate
It also makes Fixed8 Serializable just for convenience.
This change (closely related to the neo-project/neo#1321 proposal) speeds up
1.4M mainnet blocks import by 30%. Basically, we're eliminating key decoding
for block's multisignature that has the same keys most of the time.
Things I don't like about this patch:
* yet another parameter for verifyHashAgainstScript()
* vm keys are not copied in/out
But it's rather simple and solves the problem for this particular case, so I
think it's worth it.
Before this patch on block import we could easily be spending more than 6
seconds out of 30 in Uint256 encoding for UnspentBalance, now it's completely
off the radar.
This allows easier reuse of opcodes and in some cases allows to eliminate
dependencies on the whole vm package, like in compiler that only needs opcodes
and doesn't care about VM for any other purpose.
And yes, they're opcodes because an instruction is a whole thing with
operands, that's what context.Next() returns.
When encountering already seen stack item we should fail
only if it is a collection. Duplicate Integers or ByteArrays are ok
because they can't lead to recursion.
Make inspect work with avms by default and with go files if told so. In the
end this makes our CLI interface more consistent and usable. Drop useless
CompileAndInspect() compiler method along the way.
VM should be responsible for code execution and in case anyone interested in additional logging or errors they could handle them like we do it iin cli.
When performing NEWARRAY on a Struct or NEWSTRUCT on a Array,
underlying slice needs to be copied, because when it's capacity
doesn't matches it's length, underlying storage will be used
for appends even if it is already pointed at by another slice.
Script can return non-bool results that can still be converted to bool
according to the usual VM rules. Unfortunately Bool() panics if this
conversion fails which is OK for things done in vm.execute(), but certainly
not for VerifyWitnesses(), thus there is a need for TryBool() that will just
return an error in this case.
It gives access to the internal value's Value() which is essential for interop
functions that need to get something from InteropItems. And it also simplifies
some already existing code along the way.
Same thing done in a2a8981979 for PUSHBYTES,
failing to read the amount of bytes specified should lead to FAULT. Also
makes readUint16() and readUint32() panic as this is the behavior we want in
these cases. Add some tests along the way.
Before:
NEO-GO-VM > loadgo h.go
READY: loaded 16 instructions
NEO-GO-VM > ip
instruction pointer at -1 (PUSH0)
After:
NEO-GO-VM > loadgo h.go
READY: loaded 16 instructions
NEO-GO-VM > ip
instruction pointer at -1 (NOP)
I think NOP is a little less scary.
Current NEO documentation lists them:
https://docs.neo.org/docs/en-us/tooldev/advanced/neo_vm.html
CALL_* instructions were left out because of conflict with golint (but they're
removed in NEO 3.0 anyway, so wasting time on them makes no sense).
Update autogenerated instruction_string.go accordingly.
The code that we have actually implements XTUCK and not TUCK. And it's a bit
broken, so fix it and add some tests. The most interesting one (that required
to touch stack code) is the one when we have 1 element on the stack and are
trying to tell XTUCK to push 2 elements deep.
Fixes things like:
* exported type/method/function X should have comment or be unexported
* comment on exported type/method/function X should be of the form "X ..."
(with optional leading article)
Refs. #213.
Fixes one more instruction being ran when VM FAULTs:
NEO-GO-VM > run
NEO-GO-VM > error encountered at instruction 6 (ROLL)
NEO-GO-VM > runtime error: invalid memory address or nil pointer dereference
FAULT
NEO-GO-VM > error encountered at instruction 7 (SETITEM)
NEO-GO-VM > interface conversion: interface {} is []vm.StackItem, not []uint8
Refs. #96.
Simplifies a lot of code and removes some duplication. Unfortunately I had to
move test_util random functions in same commit to avoid cycle
dependencies. One of these random functions was also used in core/transaction
testing, to simplify things I've just dropped it there and used a static
string (which is nice to have for a test anyway).
There is still sha256 left in wallet (but it needs to pass Hash structure into
the signing function).
Unfortunately d58fbe0c88 didn't really fix the
problem because tinfo.Type (the expression resulting type) actually is a bool
and we need to check its parameters. Also, there is need to fix the NEQ
operation.
neo-storm has developed more wrappers for syscall APIs, so they can and should
be used as a drop-in replacement for pkg/vm/api. Moving it out of vm, as it's
not exactly related to the VM itself.
These were interpreted completely wrong, they actually have two next bytes
indicating an offset. This patch is a quick fix, actually more work is needed
here to properly display various instructions.
This is wrong, see issue #294, but it makes our VM tests work (as VM is
missing EQUAL implementation), so until #294 is properly resolved we're better
have this kind of wrong code generation.
* Fixed bug in else stmts
* Fixed if else bug
* Back to %v for formatting instructions
Imported from CityOfZion/neo-storm (ea8440e1454207753c8d209ce7c2cf724fd4ea16).
* Fixed bug where void functions cleaned up their mess multiple times
* Compiler improvements
* Fixed Neo APIs naming convention
* Added NOP after syscall for matching AVM files with neo-python.
Imported from CityOfZion/neo-storm (f93a28ef87d272a74f821d45ae0c6735a8c610be).
* Draft of iterator and enumerator
* Added iterator API to the syscall mapping
* Added draft of the enumerator.go file
* Added enumerator interop API.
* Updated the changelog
Imported from CityOfZion/neo-storm (156093318b8612e810965bb1ea26e1babfb46cdd).