2018-02-01 20:28:45 +00:00
package core
2018-02-04 19:54:51 +00:00
import (
2018-03-09 15:55:25 +00:00
"fmt"
2019-11-15 14:52:47 +00:00
"math/big"
2019-09-30 14:39:42 +00:00
"sort"
2020-02-04 15:43:21 +00:00
"sync"
2018-03-09 15:55:25 +00:00
"sync/atomic"
2018-02-04 19:54:51 +00:00
"time"
2020-03-25 15:30:21 +00:00
"github.com/nspcc-dev/neo-go/pkg/config"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/block"
2020-04-07 09:41:12 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/dao"
2020-04-08 10:35:39 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/interop"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
2020-03-19 15:52:37 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/native"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
2020-06-04 14:19:30 +00:00
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
2020-06-04 18:11:27 +00:00
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/io"
2020-06-10 14:21:26 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
2020-06-03 12:55:06 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
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
"github.com/pkg/errors"
2019-12-30 07:43:05 +00:00
"go.uber.org/zap"
2018-02-04 19:54:51 +00:00
)
2019-10-22 14:56:03 +00:00
// Tuning parameters.
2018-02-01 20:28:45 +00:00
const (
2018-03-09 15:55:25 +00:00
headerBatchCount = 2000
2020-04-14 09:54:57 +00:00
version = "0.1.0"
2019-09-30 14:27:41 +00:00
2020-07-13 15:24:58 +00:00
defaultMemPoolSize = 50000
verificationGasLimit = 100000000 // 1 GAS
2018-02-01 20:28:45 +00:00
)
2020-02-04 15:43:21 +00:00
var (
// ErrAlreadyExists is returned when trying to add some already existing
// transaction into the pool (not specifying whether it exists in the
// chain or mempool).
ErrAlreadyExists = errors . New ( "already exists" )
// ErrOOM is returned when adding transaction to the memory pool because
// it reached its full capacity.
ErrOOM = errors . New ( "no space left in the memory pool" )
2020-02-18 17:16:38 +00:00
// ErrPolicy is returned on attempt to add transaction that doesn't
// comply with node's configured policy into the mempool.
ErrPolicy = errors . New ( "not allowed by policy" )
2020-03-04 10:06:18 +00:00
// ErrInvalidBlockIndex is returned when trying to add block with index
// other than expected height of the blockchain.
ErrInvalidBlockIndex error = errors . New ( "invalid block index" )
2020-02-04 15:43:21 +00:00
)
2018-02-01 20:28:45 +00:00
var (
2020-06-23 14:31:09 +00:00
genAmount = [ ] int { 6 , 5 , 4 , 3 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }
2018-03-25 10:45:54 +00:00
decrementInterval = 2000000
2018-04-13 10:14:08 +00:00
persistInterval = 1 * time . Second
2018-02-01 20:28:45 +00:00
)
2020-05-07 19:04:10 +00:00
// Blockchain represents the blockchain. It maintans internal state representing
// the state of the ledger that can be accessed in various ways and changed by
// adding new blocks or headers.
2018-02-01 20:28:45 +00:00
type Blockchain struct {
2018-03-25 10:45:54 +00:00
config config . ProtocolConfiguration
2020-02-04 15:43:21 +00:00
// The only way chain state changes is by adding blocks, so we can't
// allow concurrent block additions. It differs from the next lock in
// that it's only for AddBlock method itself, the chain state is
// protected by the lock below, but holding it during all of AddBlock
// is too expensive (because the state only changes when persisting
// change cache).
addLock sync . Mutex
// This lock ensures blockchain immutability for operations that need
// that while performing their tasks. It's mostly used as a read lock
// with the only writer being the block addition logic.
lock sync . RWMutex
2019-11-25 17:39:11 +00:00
// Data access object for CRUD operations around storage.
2020-04-07 09:41:12 +00:00
dao * dao . Simple
2019-09-26 15:14:00 +00:00
2018-03-09 15:55:25 +00:00
// Current index/height of the highest block.
// Read access should always be called by BlockHeight().
2019-09-26 15:14:00 +00:00
// Write access should only happen in storeBlock().
2018-03-09 15:55:25 +00:00
blockHeight uint32
2018-02-04 19:54:51 +00:00
2019-12-23 16:18:12 +00:00
// Current top Block wrapped in an atomic.Value for safe access.
topBlock atomic . Value
2019-09-24 15:51:20 +00:00
// Current persisted block count.
persistedHeight uint32
2018-03-17 11:53:21 +00:00
// Number of headers stored in the chain file.
2018-02-06 06:43:32 +00:00
storedHeaderCount uint32
2020-02-25 13:15:17 +00:00
generationAmount [ ] int
decrementInterval int
2019-10-22 14:56:03 +00:00
// All operations on headerList must be called from an
2018-03-17 11:53:21 +00:00
// headersOp to be routine safe.
headerList * HeaderHashList
2018-03-09 15:55:25 +00:00
// Only for operating on the headerList.
headersOp chan headersOpFunc
headersOpDone chan struct { }
2018-03-10 12:04:06 +00:00
2019-11-07 17:47:48 +00:00
// Stop synchronization mechanisms.
stopCh chan struct { }
runToExitCh chan struct { }
2020-01-15 12:10:05 +00:00
memPool mempool . Pool
2019-12-10 16:13:29 +00:00
core: protect (*Blockchain).keyCache with a lock
Avoid failures like this:
fatal error: concurrent map writes
goroutine 103 [running]:
runtime.throw(0xca6fe3, 0x15)
/usr/lib64/go/1.12/src/runtime/panic.go:617 +0x72 fp=0xc000687988 sp=0xc000687958 pc=0x42d8d2
runtime.mapassign(0xb9d4a0, 0xc0000b1f80, 0xc000687a6c, 0x13bd940)
/usr/lib64/go/1.12/src/runtime/map.go:590 +0x5e3 fp=0xc000687a10 sp=0xc000687988 pc=0x40e2a3
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHashAgainstScript(0xc0000f8000, 0x1300d1f5a3d348be, 0x78607089e4fe9fab, 0xc0a21e4f71, 0xc000d16070, 0x1e2b8881f8178e92, 0xd589cfa965287bb8, 0x24a4e4f292ebc797, 0xb90d3120d878e619, 0xc0001d01e0, ...)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1866 +0x371 fp=0xc000687ad8 sp=0xc000687a10 pc=0x984ed1
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHeaderWitnesses(0xc0000f8000, 0xc000d16000, 0xc00042d950, 0x1, 0xc00008a6c0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1914 +0x209 fp=0xc000687c90 sp=0xc000687ad8 pc=0x985999
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHeader(0xc0000f8000, 0xc000d16000, 0xc00042d950, 0xf0ee0049d611101f, 0xf8967398a8f9351b)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1254 +0x235 fp=0xc000687d70 sp=0xc000687c90 pc=0x97e4e5
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).addHeaders(0xc0000f8000, 0x1, 0xc000d12000, 0x7d0, 0x7d0, 0x0, 0x1)
/home/rik/dev/neo-go/pkg/core/blockchain.go:369 +0x266 fp=0xc000687e68 sp=0xc000687d70 pc=0x973f46
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddHeaders(0xc0000f8000, 0xc000d12000, 0x7d0, 0x7d0, 0x0, 0xc0003640c0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:338 +0x51 fp=0xc000687eb0 sp=0xc000687e68 pc=0x973cb1
github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleHeadersCmd(0xc00022c140, 0xdd0ca0, 0xc000374000, 0xc00000c020)
/home/rik/dev/neo-go/pkg/network/server.go:431 +0x74 fp=0xc000687fc0 sp=0xc000687eb0 pc=0x9ce5e4
runtime.goexit()
/usr/lib64/go/1.12/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000687fc8 sp=0xc000687fc0 pc=0x45ca51
created by github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage
/home/rik/dev/neo-go/pkg/network/server.go:705 +0xa4a
...
2020-03-04 17:26:18 +00:00
// This lock protects concurrent access to keyCache.
keyCacheLock sync . RWMutex
2019-12-10 16:13:29 +00:00
// cache for block verification keys.
keyCache map [ util . Uint160 ] map [ string ] * keys . PublicKey
2019-12-30 07:43:05 +00:00
2020-06-23 15:15:55 +00:00
sbValidators keys . PublicKeys
2019-12-30 07:43:05 +00:00
log * zap . Logger
2020-02-06 15:47:03 +00:00
lastBatch * storage . MemBatch
2020-03-19 15:52:37 +00:00
contracts native . Contracts
2020-05-12 14:20:41 +00:00
// Notification subsystem.
events chan bcEvent
subCh chan interface { }
unsubCh chan interface { }
}
// bcEvent is an internal event generated by the Blockchain and then
// broadcasted to other parties. It joins the new block and associated
// invocation logs, all the other events visible from outside can be produced
// from this combination.
type bcEvent struct {
block * block . Block
appExecResults [ ] * state . AppExecResult
2018-02-01 20:28:45 +00:00
}
2018-03-09 15:55:25 +00:00
type headersOpFunc func ( headerList * HeaderHashList )
2018-02-06 06:43:32 +00:00
2019-10-22 14:56:03 +00:00
// NewBlockchain returns a new blockchain object the will use the
2020-05-07 19:04:10 +00:00
// given Store as its underlying storage. For it to work correctly you need
// to spawn a goroutine for its Run method after this initialization.
2019-12-30 07:43:05 +00:00
func NewBlockchain ( s storage . Store , cfg config . ProtocolConfiguration , log * zap . Logger ) ( * Blockchain , error ) {
if log == nil {
return nil , errors . New ( "empty logger" )
}
2020-01-22 14:28:02 +00:00
if cfg . MemPoolSize <= 0 {
cfg . MemPoolSize = defaultMemPoolSize
log . Info ( "mempool size is not set or wrong, setting default value" , zap . Int ( "MemPoolSize" , cfg . MemPoolSize ) )
}
2020-06-23 15:15:55 +00:00
validators , err := validatorsFromConfig ( cfg )
if err != nil {
return nil , err
}
2018-03-09 15:55:25 +00:00
bc := & Blockchain {
2018-03-25 10:45:54 +00:00
config : cfg ,
2020-06-18 09:00:51 +00:00
dao : dao . NewSimple ( s , cfg . Magic ) ,
2018-03-09 15:55:25 +00:00
headersOp : make ( chan headersOpFunc ) ,
headersOpDone : make ( chan struct { } ) ,
2019-11-07 17:47:48 +00:00
stopCh : make ( chan struct { } ) ,
runToExitCh : make ( chan struct { } ) ,
2020-01-22 14:28:02 +00:00
memPool : mempool . NewMemPool ( cfg . MemPoolSize ) ,
2019-12-10 16:13:29 +00:00
keyCache : make ( map [ util . Uint160 ] map [ string ] * keys . PublicKey ) ,
2020-06-23 15:15:55 +00:00
sbValidators : validators ,
2019-12-30 07:43:05 +00:00
log : log ,
2020-05-12 14:20:41 +00:00
events : make ( chan bcEvent ) ,
subCh : make ( chan interface { } ) ,
unsubCh : make ( chan interface { } ) ,
2020-02-25 13:15:17 +00:00
generationAmount : genAmount ,
decrementInterval : decrementInterval ,
2020-03-19 15:52:37 +00:00
contracts : * native . NewContracts ( ) ,
2018-03-09 15:55:25 +00:00
}
2018-02-06 06:43:32 +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
if err := bc . init ( ) ; err != nil {
2018-03-17 11:53:21 +00:00
return nil , err
}
return bc , nil
2018-02-04 19:54:51 +00:00
}
2018-03-17 11:53:21 +00:00
func ( bc * Blockchain ) init ( ) error {
2018-04-09 16:58:09 +00:00
// If we could not find the version in the Store, we know that there is nothing stored.
2019-11-25 17:39:11 +00:00
ver , err := bc . dao . GetVersion ( )
2018-04-09 16:58:09 +00:00
if err != nil {
2019-12-30 07:43:05 +00:00
bc . log . Info ( "no storage version found! creating genesis block" )
2019-11-25 17:39:11 +00:00
if err = bc . dao . PutVersion ( version ) ; err != nil {
2019-01-25 11:20:35 +00:00
return err
}
2019-09-11 17:28:49 +00:00
genesisBlock , err := createGenesisBlock ( bc . config )
if err != nil {
return err
}
bc . headerList = NewHeaderHashList ( genesisBlock . Hash ( ) )
2019-11-25 17:39:11 +00:00
err = bc . dao . PutCurrentHeader ( hashAndIndexToBytes ( genesisBlock . Hash ( ) , genesisBlock . Index ) )
2019-11-06 14:56:06 +00:00
if err != nil {
return err
}
2019-09-26 15:14:00 +00:00
return bc . storeBlock ( genesisBlock )
2018-04-09 16:58:09 +00:00
}
if ver != version {
return fmt . Errorf ( "storage version mismatch betweeen %s and %s" , version , ver )
2018-03-25 10:45:54 +00:00
}
// At this point there was no version found in the storage which
// implies a creating fresh storage with the version specified
// and the genesis block as first block.
2019-12-30 07:43:05 +00:00
bc . log . Info ( "restoring blockchain" , zap . String ( "version" , version ) )
2018-03-25 10:45:54 +00:00
2019-11-25 17:39:11 +00:00
bHeight , err := bc . dao . GetCurrentBlockHeight ( )
2018-03-25 10:45:54 +00:00
if err != nil {
2018-03-17 11:53:21 +00:00
return err
}
2018-04-09 16:58:09 +00:00
bc . blockHeight = bHeight
2019-09-24 15:51:20 +00:00
bc . persistedHeight = bHeight
2020-05-29 14:20:00 +00:00
if err = bc . dao . InitMPT ( bHeight ) ; err != nil {
return errors . Wrapf ( err , "can't init MPT at height %d" , bHeight )
}
2018-03-17 11:53:21 +00:00
2019-11-25 17:39:11 +00:00
hashes , err := bc . dao . GetHeaderHashes ( )
2018-03-17 11:53:21 +00:00
if err != nil {
return err
}
2018-03-25 10:45:54 +00:00
2019-09-11 17:28:49 +00:00
bc . headerList = NewHeaderHashList ( hashes ... )
bc . storedHeaderCount = uint32 ( len ( hashes ) )
2018-03-17 11:53:21 +00:00
2019-11-25 17:39:11 +00:00
currHeaderHeight , currHeaderHash , err := bc . dao . GetCurrentHeaderHeight ( )
2018-03-17 11:53:21 +00:00
if err != nil {
return err
}
2019-11-06 14:58:19 +00:00
if bc . storedHeaderCount == 0 && currHeaderHeight == 0 {
bc . headerList . Add ( currHeaderHash )
}
2018-03-17 11:53:21 +00:00
2018-04-09 16:58:09 +00:00
// There is a high chance that the Node is stopped before the next
2018-03-17 11:53:21 +00:00
// batch of 2000 headers was stored. Via the currentHeaders stored we can sync
// that with stored blocks.
2019-11-29 11:22:31 +00:00
if currHeaderHeight >= bc . storedHeaderCount {
2018-03-17 11:53:21 +00:00
hash := currHeaderHash
2019-10-21 05:37:01 +00:00
var targetHash util . Uint256
if bc . headerList . Len ( ) > 0 {
targetHash = bc . headerList . Get ( bc . headerList . Len ( ) - 1 )
} else {
genesisBlock , err := createGenesisBlock ( bc . config )
if err != nil {
return err
}
targetHash = genesisBlock . Hash ( )
2019-11-06 15:00:14 +00:00
bc . headerList . Add ( targetHash )
2019-10-21 05:37:01 +00:00
}
2020-01-14 12:32:07 +00:00
headers := make ( [ ] * block . Header , 0 )
2018-03-17 11:53:21 +00:00
for hash != targetHash {
2019-02-20 17:39:32 +00:00
header , err := bc . GetHeader ( hash )
2018-03-17 11:53:21 +00:00
if err != nil {
return fmt . Errorf ( "could not get header %s: %s" , hash , err )
}
headers = append ( headers , header )
hash = header . PrevHash
}
headerSliceReverse ( headers )
2019-09-25 14:52:46 +00:00
for _ , h := range headers {
if ! h . Verify ( ) {
return fmt . Errorf ( "bad header %d/%s in the storage" , h . Index , h . Hash ( ) )
}
bc . headerList . Add ( h . Hash ( ) )
2018-03-17 11:53:21 +00:00
}
}
return nil
2018-03-09 15:55:25 +00:00
}
2020-05-07 19:04:10 +00:00
// Run runs chain loop, it needs to be run as goroutine and executing it is
// critical for correct Blockchain operation.
2019-11-07 17:47:48 +00:00
func ( bc * Blockchain ) Run ( ) {
2018-03-17 11:53:21 +00:00
persistTimer := time . NewTimer ( persistInterval )
2019-09-16 15:52:47 +00:00
defer func ( ) {
persistTimer . Stop ( )
2019-10-21 07:04:58 +00:00
if err := bc . persist ( ) ; err != nil {
2019-12-30 07:43:05 +00:00
bc . log . Warn ( "failed to persist" , zap . Error ( err ) )
2019-09-26 15:14:00 +00:00
}
2020-04-07 09:41:12 +00:00
if err := bc . dao . Store . Close ( ) ; err != nil {
2019-12-30 07:43:05 +00:00
bc . log . Warn ( "failed to close db" , zap . Error ( err ) )
2019-09-16 15:52:47 +00:00
}
2019-11-07 17:47:48 +00:00
close ( bc . runToExitCh )
2019-09-16 15:52:47 +00:00
} ( )
2020-05-12 14:20:41 +00:00
go bc . notificationDispatcher ( )
2018-03-09 15:55:25 +00:00
for {
select {
2019-11-07 17:47:48 +00:00
case <- bc . stopCh :
2019-02-19 11:48:48 +00:00
return
2018-03-09 15:55:25 +00:00
case op := <- bc . headersOp :
2018-03-17 11:53:21 +00:00
op ( bc . headerList )
2018-03-09 15:55:25 +00:00
bc . headersOpDone <- struct { } { }
2018-03-14 09:36:59 +00:00
case <- persistTimer . C :
2019-09-18 15:21:16 +00:00
go func ( ) {
2019-10-21 07:04:58 +00:00
err := bc . persist ( )
2019-09-22 17:06:52 +00:00
if err != nil {
2019-12-30 07:43:05 +00:00
bc . log . Warn ( "failed to persist blockchain" , zap . Error ( err ) )
2019-09-22 17:06:52 +00:00
}
2020-02-24 14:17:25 +00:00
persistTimer . Reset ( persistInterval )
2019-09-18 15:21:16 +00:00
} ( )
2018-03-09 15:55:25 +00:00
}
2018-02-04 19:54:51 +00:00
}
}
2020-05-12 14:20:41 +00:00
// notificationDispatcher manages subscription to events and broadcasts new events.
func ( bc * Blockchain ) notificationDispatcher ( ) {
var (
// These are just sets of subscribers, though modelled as maps
// for ease of management (not a lot of subscriptions is really
// expected, but maps are convenient for adding/deleting elements).
blockFeed = make ( map [ chan <- * block . Block ] bool )
txFeed = make ( map [ chan <- * transaction . Transaction ] bool )
notificationFeed = make ( map [ chan <- * state . NotificationEvent ] bool )
executionFeed = make ( map [ chan <- * state . AppExecResult ] bool )
)
for {
select {
case <- bc . stopCh :
return
case sub := <- bc . subCh :
switch ch := sub . ( type ) {
case chan <- * block . Block :
blockFeed [ ch ] = true
case chan <- * transaction . Transaction :
txFeed [ ch ] = true
case chan <- * state . NotificationEvent :
notificationFeed [ ch ] = true
case chan <- * state . AppExecResult :
executionFeed [ ch ] = true
default :
panic ( fmt . Sprintf ( "bad subscription: %T" , sub ) )
}
case unsub := <- bc . unsubCh :
switch ch := unsub . ( type ) {
case chan <- * block . Block :
delete ( blockFeed , ch )
case chan <- * transaction . Transaction :
delete ( txFeed , ch )
case chan <- * state . NotificationEvent :
delete ( notificationFeed , ch )
case chan <- * state . AppExecResult :
delete ( executionFeed , ch )
default :
panic ( fmt . Sprintf ( "bad unsubscription: %T" , unsub ) )
}
case event := <- bc . events :
// We don't want to waste time looping through transactions when there are no
// subscribers.
if len ( txFeed ) != 0 || len ( notificationFeed ) != 0 || len ( executionFeed ) != 0 {
2020-06-18 11:19:55 +00:00
aer := event . appExecResults [ 0 ]
if ! aer . TxHash . Equals ( event . block . Hash ( ) ) {
panic ( "inconsistent application execution results" )
}
for ch := range executionFeed {
ch <- aer
}
for i := range aer . Events {
for ch := range notificationFeed {
ch <- & aer . Events [ i ]
}
}
aerIdx := 1
2020-05-12 14:20:41 +00:00
for _ , tx := range event . block . Transactions {
2020-06-05 13:07:04 +00:00
aer := event . appExecResults [ aerIdx ]
if ! aer . TxHash . Equals ( tx . Hash ( ) ) {
panic ( "inconsistent application execution results" )
}
aerIdx ++
for ch := range executionFeed {
ch <- aer
}
2020-07-27 14:57:53 +00:00
if aer . VMState == vm . HaltState {
2020-06-05 13:07:04 +00:00
for i := range aer . Events {
for ch := range notificationFeed {
ch <- & aer . Events [ i ]
2020-05-12 14:20:41 +00:00
}
}
}
for ch := range txFeed {
ch <- tx
}
}
}
for ch := range blockFeed {
ch <- event . block
}
}
}
}
2019-11-07 17:47:48 +00:00
// Close stops Blockchain's internal loop, syncs changes to persistent storage
// and closes it. The Blockchain is no longer functional after the call to Close.
func ( bc * Blockchain ) Close ( ) {
core: prevent panic on forced exit
If we're to close the Blockchain while it's storing a block this might happen:
panic: assignment to entry in nil map
goroutine 63 [running]:
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).put(...)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:53
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemCachedStore).Persist(0xc000673a40, 0x0, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/storage/memcached_store.go:118 +0x245
github.com/nspcc-dev/neo-go/pkg/core/dao.(*Simple).Persist(0xc003dbdcc0, 0xc003b18900, 0xc002505538, 0xc001c510e0)
/home/rik/dev/neo-go/pkg/core/dao/dao.go:543 +0x2e
github.com/nspcc-dev/neo-go/pkg/core/dao.(*Cached).Persist(0xc003b189c0, 0xc000240000, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/dao/cacheddao.go:169 +0x756
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).storeBlock(0xc0001f4000, 0xc003611040, 0x0, 0x10)
/home/rik/dev/neo-go/pkg/core/blockchain.go:647 +0xfa2
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddBlock(0xc0001f4000, 0xc003611040, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:447 +0xee
github.com/nspcc-dev/neo-go/pkg/network.(*blockQueue).run(0xc0006f4120)
/home/rik/dev/neo-go/pkg/network/blockqueue.go:48 +0x158
created by github.com/nspcc-dev/neo-go/pkg/network.(*Server).Start
/home/rik/dev/neo-go/pkg/network/server.go:179 +0x2b5
2020-06-18 18:35:17 +00:00
// If there is a block addition in progress, wait for it to finish and
// don't allow new ones.
bc . addLock . Lock ( )
2019-11-07 17:47:48 +00:00
close ( bc . stopCh )
<- bc . runToExitCh
core: prevent panic on forced exit
If we're to close the Blockchain while it's storing a block this might happen:
panic: assignment to entry in nil map
goroutine 63 [running]:
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).put(...)
/home/rik/dev/neo-go/pkg/core/storage/memory_store.go:53
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemCachedStore).Persist(0xc000673a40, 0x0, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/storage/memcached_store.go:118 +0x245
github.com/nspcc-dev/neo-go/pkg/core/dao.(*Simple).Persist(0xc003dbdcc0, 0xc003b18900, 0xc002505538, 0xc001c510e0)
/home/rik/dev/neo-go/pkg/core/dao/dao.go:543 +0x2e
github.com/nspcc-dev/neo-go/pkg/core/dao.(*Cached).Persist(0xc003b189c0, 0xc000240000, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/dao/cacheddao.go:169 +0x756
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).storeBlock(0xc0001f4000, 0xc003611040, 0x0, 0x10)
/home/rik/dev/neo-go/pkg/core/blockchain.go:647 +0xfa2
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddBlock(0xc0001f4000, 0xc003611040, 0x0, 0x0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:447 +0xee
github.com/nspcc-dev/neo-go/pkg/network.(*blockQueue).run(0xc0006f4120)
/home/rik/dev/neo-go/pkg/network/blockqueue.go:48 +0x158
created by github.com/nspcc-dev/neo-go/pkg/network.(*Server).Start
/home/rik/dev/neo-go/pkg/network/server.go:179 +0x2b5
2020-06-18 18:35:17 +00:00
bc . addLock . Unlock ( )
2019-11-07 17:47:48 +00:00
}
2019-09-26 15:14:00 +00:00
// AddBlock accepts successive block for the Blockchain, verifies it and
// stores internally. Eventually it will be persisted to the backing storage.
2020-01-14 12:32:07 +00:00
func ( bc * Blockchain ) AddBlock ( block * block . Block ) error {
2020-02-04 15:43:21 +00:00
bc . addLock . Lock ( )
defer bc . addLock . Unlock ( )
2019-09-26 15:14:00 +00:00
expectedHeight := bc . BlockHeight ( ) + 1
if expectedHeight != block . Index {
2020-03-04 10:06:18 +00:00
return ErrInvalidBlockIndex
2018-03-09 15:55:25 +00:00
}
2020-02-29 14:52:09 +00:00
headerLen := bc . headerListLen ( )
if int ( block . Index ) == headerLen {
err := bc . addHeaders ( bc . config . VerifyBlocks , block . Header ( ) )
if err != nil {
return err
}
}
2019-10-11 14:46:47 +00:00
if bc . config . VerifyBlocks {
2019-10-15 09:52:10 +00:00
err := block . Verify ( )
2019-10-11 08:40:54 +00:00
if err != nil {
2019-11-27 09:23:18 +00:00
return fmt . Errorf ( "block %s is invalid: %s" , block . Hash ( ) . StringLE ( ) , err )
2019-09-30 14:35:11 +00:00
}
2019-10-11 14:46:47 +00:00
if bc . config . VerifyTransactions {
for _ , tx := range block . Transactions {
err := bc . VerifyTx ( tx , block )
if err != nil {
2019-11-27 09:23:18 +00:00
return fmt . Errorf ( "transaction %s failed to verify: %s" , tx . Hash ( ) . StringLE ( ) , err )
2019-10-11 14:46:47 +00:00
}
2019-09-30 14:35:11 +00:00
}
}
2018-02-04 19:54:51 +00:00
}
2019-09-26 15:14:00 +00:00
return bc . storeBlock ( block )
2018-02-04 19:54:51 +00:00
}
2019-10-22 14:56:03 +00:00
// AddHeaders processes the given headers and add them to the
2020-03-03 12:34:03 +00:00
// HeaderHashList. It expects headers to be sorted by index.
2020-02-29 14:52:09 +00:00
func ( bc * Blockchain ) AddHeaders ( headers ... * block . Header ) error {
return bc . addHeaders ( bc . config . VerifyBlocks , headers ... )
}
2020-03-03 12:34:03 +00:00
// addHeaders is an internal implementation of AddHeaders (`verify` parameter
// tells it to verify or not verify given headers).
2020-02-29 14:52:09 +00:00
func ( bc * Blockchain ) addHeaders ( verify bool , headers ... * block . Header ) ( err error ) {
2018-03-09 15:55:25 +00:00
var (
start = time . Now ( )
2020-04-07 09:41:12 +00:00
batch = bc . dao . Store . Batch ( )
2018-03-09 15:55:25 +00:00
)
2020-03-03 12:34:03 +00:00
if len ( headers ) > 0 {
var i int
curHeight := bc . HeaderHeight ( )
for i = range headers {
if headers [ i ] . Index > curHeight {
break
}
}
headers = headers [ i : ]
}
2020-02-29 14:52:09 +00:00
if len ( headers ) == 0 {
return nil
} else if verify {
// Verify that the chain of the headers is consistent.
var lastHeader * block . Header
if lastHeader , err = bc . GetHeader ( headers [ 0 ] . PrevHash ) ; err != nil {
return fmt . Errorf ( "previous header was not found: %v" , err )
}
for _ , h := range headers {
if err = bc . verifyHeader ( h , lastHeader ) ; err != nil {
return
}
lastHeader = h
}
}
2018-03-09 15:55:25 +00:00
bc . headersOp <- func ( headerList * HeaderHashList ) {
2019-10-07 16:55:33 +00:00
oldlen := headerList . Len ( )
2018-03-09 15:55:25 +00:00
for _ , h := range headers {
if int ( h . Index - 1 ) >= headerList . Len ( ) {
err = fmt . Errorf (
2018-03-17 11:53:21 +00:00
"height of received header %d is higher then the current header %d" ,
2018-03-09 15:55:25 +00:00
h . Index , headerList . Len ( ) ,
)
return
}
if int ( h . Index ) < headerList . Len ( ) {
continue
}
if ! h . Verify ( ) {
err = fmt . Errorf ( "header %v is invalid" , h )
return
}
if err = bc . processHeader ( h , batch , headerList ) ; err != nil {
return
}
2018-02-04 19:54:51 +00:00
}
2019-10-07 16:55:33 +00:00
if oldlen != headerList . Len ( ) {
2019-11-06 13:09:45 +00:00
updateHeaderHeightMetric ( headerList . Len ( ) - 1 )
2020-04-07 09:41:12 +00:00
if err = bc . dao . Store . PutBatch ( batch ) ; err != nil {
2018-03-09 15:55:25 +00:00
return
}
2019-12-30 07:43:05 +00:00
bc . log . Debug ( "done processing headers" ,
zap . Int ( "headerIndex" , headerList . Len ( ) - 1 ) ,
zap . Uint32 ( "blockHeight" , bc . BlockHeight ( ) ) ,
zap . Duration ( "took" , time . Since ( start ) ) )
2018-02-06 06:43:32 +00:00
}
2018-02-04 19:54:51 +00:00
}
2018-03-09 15:55:25 +00:00
<- bc . headersOpDone
return err
2018-02-06 06:43:32 +00:00
}
2018-02-04 19:54:51 +00:00
2018-03-09 15:55:25 +00:00
// processHeader processes the given header. Note that this is only thread safe
// if executed in headers operation.
2020-01-14 12:32:07 +00:00
func ( bc * Blockchain ) processHeader ( h * block . Header , batch storage . Batch , headerList * HeaderHashList ) error {
2018-03-09 15:55:25 +00:00
headerList . Add ( h . Hash ( ) )
2018-02-04 19:54:51 +00:00
2019-09-16 09:18:13 +00:00
buf := io . NewBufBinWriter ( )
2018-03-09 15:55:25 +00:00
for int ( h . Index ) - headerBatchCount >= int ( bc . storedHeaderCount ) {
2019-09-16 09:18:13 +00:00
if err := headerList . Write ( buf . BinWriter , int ( bc . storedHeaderCount ) , headerBatchCount ) ; err != nil {
2018-03-09 15:55:25 +00:00
return err
}
2018-03-17 11:53:21 +00:00
key := storage . AppendPrefixInt ( storage . IXHeaderHashList , int ( bc . storedHeaderCount ) )
2018-03-14 09:36:59 +00:00
batch . Put ( key , buf . Bytes ( ) )
2018-03-09 15:55:25 +00:00
bc . storedHeaderCount += headerBatchCount
buf . Reset ( )
2018-02-04 19:54:51 +00:00
}
2018-03-09 15:55:25 +00:00
buf . Reset ( )
2019-09-16 16:31:49 +00:00
h . EncodeBinary ( buf . BinWriter )
if buf . Err != nil {
return buf . Err
2018-02-04 19:54:51 +00:00
}
2019-11-27 09:23:18 +00:00
key := storage . AppendPrefix ( storage . DataBlock , h . Hash ( ) . BytesLE ( ) )
2018-03-14 09:36:59 +00:00
batch . Put ( key , buf . Bytes ( ) )
2018-03-17 11:53:21 +00:00
batch . Put ( storage . SYSCurrentHeader . Bytes ( ) , hashAndIndexToBytes ( h . Hash ( ) , h . Index ) )
2018-02-06 06:43:32 +00:00
2018-02-04 19:54:51 +00:00
return nil
}
2020-05-29 14:20:00 +00:00
// GetStateRoot returns state root for a given height.
func ( bc * Blockchain ) GetStateRoot ( height uint32 ) ( * state . MPTRootState , error ) {
return bc . dao . GetStateRoot ( height )
}
2020-07-16 20:06:17 +00:00
// storeBlock performs chain update using the block given, it executes all
// transactions with all appropriate side-effects and updates Blockchain state.
// This is the only way to change Blockchain state.
2020-01-14 12:32:07 +00:00
func ( bc * Blockchain ) storeBlock ( block * block . Block ) error {
2020-04-07 09:41:12 +00:00
cache := dao . NewCached ( bc . dao )
2020-06-18 11:19:55 +00:00
appExecResults := make ( [ ] * state . AppExecResult , 0 , 1 + len ( block . Transactions ) )
2020-06-04 19:59:34 +00:00
if err := cache . StoreAsBlock ( block ) ; err != nil {
2019-01-25 11:20:35 +00:00
return err
}
2019-11-25 17:39:11 +00:00
if err := cache . StoreAsCurrentBlock ( block ) ; err != nil {
2019-10-16 14:29:21 +00:00
return err
}
2018-03-14 09:36:59 +00:00
2020-06-17 10:57:54 +00:00
if block . Index > 0 {
systemInterop := bc . newInteropContext ( trigger . System , cache , block , nil )
2020-07-28 13:38:00 +00:00
v := systemInterop . SpawnVM ( )
2020-06-17 10:57:54 +00:00
v . LoadScriptWithFlags ( bc . contracts . GetPersistScript ( ) , smartcontract . AllowModifyStates | smartcontract . AllowCall )
2020-06-19 08:09:38 +00:00
v . SetPriceGetter ( getPrice )
2020-06-17 10:57:54 +00:00
if err := v . Run ( ) ; err != nil {
return errors . Wrap ( err , "can't persist native contracts" )
} else if _ , err := systemInterop . DAO . Persist ( ) ; err != nil {
return errors . Wrap ( err , "can't persist `onPersist` changes" )
}
2020-06-18 13:34:56 +00:00
for i := range systemInterop . Notifications {
bc . handleNotification ( & systemInterop . Notifications [ i ] , cache , block , block . Hash ( ) )
}
2020-06-18 11:19:55 +00:00
aer := & state . AppExecResult {
TxHash : block . Hash ( ) , // application logs can be retrieved by block hash
Trigger : trigger . System ,
VMState : v . State ( ) ,
GasConsumed : v . GasConsumed ( ) ,
Stack : v . Estack ( ) . ToContractParameters ( ) ,
Events : systemInterop . Notifications ,
}
appExecResults = append ( appExecResults , aer )
err := cache . PutAppExecResult ( aer )
if err != nil {
return errors . Wrap ( err , "failed to Store notifications" )
}
2020-06-17 10:57:54 +00:00
}
2018-03-21 16:11:04 +00:00
for _ , tx := range block . Transactions {
2019-11-25 17:39:11 +00:00
if err := cache . StoreAsTransaction ( tx , block . Index ) ; err != nil {
2019-01-25 11:20:35 +00:00
return err
}
2020-06-05 13:07:04 +00:00
systemInterop := bc . newInteropContext ( trigger . Application , cache , block , tx )
2020-07-28 13:38:00 +00:00
v := systemInterop . SpawnVM ( )
2020-06-10 14:21:26 +00:00
v . LoadScriptWithFlags ( tx . Script , smartcontract . All )
2020-06-05 13:07:04 +00:00
v . SetPriceGetter ( getPrice )
2020-06-18 19:17:48 +00:00
v . GasLimit = tx . SystemFee
2020-01-20 12:31:12 +00:00
2020-06-05 13:07:04 +00:00
err := v . Run ( )
if ! v . HasFailed ( ) {
_ , err := systemInterop . DAO . Persist ( )
if err != nil {
return errors . Wrap ( err , "failed to persist invocation results" )
}
2020-06-18 13:34:56 +00:00
for i := range systemInterop . Notifications {
bc . handleNotification ( & systemInterop . Notifications [ i ] , cache , block , tx . Hash ( ) )
2019-11-13 13:55:20 +00:00
}
2020-06-05 13:07:04 +00:00
} else {
bc . log . Warn ( "contract invocation failed" ,
zap . String ( "tx" , tx . Hash ( ) . StringLE ( ) ) ,
zap . Uint32 ( "block" , block . Index ) ,
zap . Error ( err ) )
}
aer := & state . AppExecResult {
TxHash : tx . Hash ( ) ,
Trigger : trigger . Application ,
VMState : v . State ( ) ,
GasConsumed : v . GasConsumed ( ) ,
Stack : v . Estack ( ) . ToContractParameters ( ) ,
Events : systemInterop . Notifications ,
}
appExecResults = append ( appExecResults , aer )
err = cache . PutAppExecResult ( aer )
if err != nil {
return errors . Wrap ( err , "failed to Store notifications" )
2018-03-21 16:11:04 +00:00
}
}
2020-02-06 15:47:03 +00:00
2020-05-29 14:20:00 +00:00
root := bc . dao . MPT . StateRoot ( )
var prevHash util . Uint256
if block . Index > 0 {
prev , err := bc . dao . GetStateRoot ( block . Index - 1 )
if err != nil {
return errors . WithMessagef ( err , "can't get previous state root" )
}
2020-06-04 14:19:30 +00:00
prevHash = hash . DoubleSha256 ( prev . GetSignedPart ( ) )
2020-05-29 14:20:00 +00:00
}
err := bc . AddStateRoot ( & state . MPTRoot {
MPTRootBase : state . MPTRootBase {
Index : block . Index ,
PrevHash : prevHash ,
Root : root ,
} ,
} )
if err != nil {
return err
}
2020-05-12 14:45:17 +00:00
if bc . config . SaveStorageBatch {
bc . lastBatch = cache . DAO . GetBatch ( )
}
bc . lock . Lock ( )
2020-05-29 14:20:00 +00:00
_ , err = cache . Persist ( )
2019-12-11 10:10:51 +00:00
if err != nil {
2020-05-12 14:45:17 +00:00
bc . lock . Unlock ( )
2018-03-21 16:11:04 +00:00
return err
}
2020-06-18 18:36:20 +00:00
bc . contracts . Policy . OnPersistEnd ( bc . dao )
2020-05-29 14:20:00 +00:00
bc . dao . MPT . Flush ( )
2020-06-04 14:25:57 +00:00
// Every persist cycle we also compact our in-memory MPT.
persistedHeight := atomic . LoadUint32 ( & bc . persistedHeight )
if persistedHeight == block . Index - 1 {
// 10 is good and roughly estimated to fit remaining trie into 1M of memory.
bc . dao . MPT . Collapse ( 10 )
}
2019-12-23 16:18:12 +00:00
bc . topBlock . Store ( block )
2019-09-26 15:14:00 +00:00
atomic . StoreUint32 ( & bc . blockHeight , block . Index )
2020-05-18 08:20:41 +00:00
bc . memPool . RemoveStale ( bc . isTxStillRelevant , bc )
2020-05-12 14:45:17 +00:00
bc . lock . Unlock ( )
updateBlockHeightMetric ( block . Index )
2020-05-12 14:20:41 +00:00
// Genesis block is stored when Blockchain is not yet running, so there
// is no one to read this event. And it doesn't make much sense as event
// anyway.
if block . Index != 0 {
bc . events <- bcEvent { block , appExecResults }
}
2018-03-09 15:55:25 +00:00
return nil
}
2020-06-18 13:34:56 +00:00
func ( bc * Blockchain ) handleNotification ( note * state . NotificationEvent , d * dao . Cached , b * block . Block , h util . Uint256 ) {
2020-06-29 08:25:32 +00:00
if note . Name != "transfer" && note . Name != "Transfer" {
2020-06-18 13:34:56 +00:00
return
}
2020-06-29 08:25:32 +00:00
arr , ok := note . Item . Value ( ) . ( [ ] stackitem . Item )
if ! ok || len ( arr ) != 3 {
2020-06-18 13:34:56 +00:00
return
}
var from [ ] byte
2020-06-29 08:25:32 +00:00
fromValue := arr [ 0 ] . Value ( )
2020-06-18 13:34:56 +00:00
// we don't have `from` set when we are minting tokens
if fromValue != nil {
from , ok = fromValue . ( [ ] byte )
if ! ok {
return
}
}
var to [ ] byte
2020-06-29 08:25:32 +00:00
toValue := arr [ 1 ] . Value ( )
2020-06-18 13:34:56 +00:00
// we don't have `to` set when we are burning tokens
if toValue != nil {
to , ok = toValue . ( [ ] byte )
if ! ok {
return
}
}
2020-06-29 08:25:32 +00:00
amount , ok := arr [ 2 ] . Value ( ) . ( * big . Int )
2020-06-18 13:34:56 +00:00
if ! ok {
2020-06-29 08:25:32 +00:00
bs , ok := arr [ 2 ] . Value ( ) . ( [ ] byte )
2020-06-18 13:34:56 +00:00
if ! ok {
return
}
amount = bigint . FromBytes ( bs )
}
2020-07-09 09:57:24 +00:00
bc . processNEP5Transfer ( d , h , b , note . ScriptHash , from , to , amount )
2020-06-18 13:34:56 +00:00
}
2020-03-05 14:11:58 +00:00
func parseUint160 ( addr [ ] byte ) util . Uint160 {
2020-03-05 07:45:50 +00:00
if u , err := util . Uint160DecodeBytesBE ( addr ) ; err == nil {
2020-03-05 14:11:58 +00:00
return u
2020-03-05 07:45:50 +00:00
}
2020-03-05 14:11:58 +00:00
return util . Uint160 { }
2020-03-05 07:45:50 +00:00
}
2020-07-09 09:57:24 +00:00
func ( bc * Blockchain ) processNEP5Transfer ( cache * dao . Cached , h util . Uint256 , b * block . Block , sc util . Uint160 , from , to [ ] byte , amount * big . Int ) {
2020-03-05 07:45:50 +00:00
toAddr := parseUint160 ( to )
fromAddr := parseUint160 ( from )
2020-07-28 09:23:58 +00:00
var id int32
nativeContract := bc . contracts . ByHash ( sc )
if nativeContract != nil {
id = nativeContract . Metadata ( ) . ContractID
} else {
assetContract := bc . GetContractState ( sc )
if assetContract == nil {
return
}
id = assetContract . ID
}
2020-03-05 14:11:58 +00:00
transfer := & state . NEP5Transfer {
2020-07-28 16:05:16 +00:00
Asset : id ,
2020-03-05 14:11:58 +00:00
From : fromAddr ,
To : toAddr ,
Block : b . Index ,
Timestamp : b . Timestamp ,
2020-06-18 13:34:56 +00:00
Tx : h ,
2020-03-05 14:11:58 +00:00
}
if ! fromAddr . Equals ( util . Uint160 { } ) {
2020-03-11 15:22:46 +00:00
balances , err := cache . GetNEP5Balances ( fromAddr )
2020-03-05 07:45:50 +00:00
if err != nil {
return
}
2020-07-28 09:23:58 +00:00
bs := balances . Trackers [ id ]
2020-07-09 09:57:24 +00:00
bs . Balance = * new ( big . Int ) . Sub ( & bs . Balance , amount )
2020-03-05 07:45:50 +00:00
bs . LastUpdatedBlock = b . Index
2020-07-28 09:23:58 +00:00
balances . Trackers [ id ] = bs
2020-07-09 09:57:24 +00:00
transfer . Amount = * new ( big . Int ) . Sub ( & transfer . Amount , amount )
2020-03-12 09:43:21 +00:00
isBig , err := cache . AppendNEP5Transfer ( fromAddr , balances . NextTransferBatch , transfer )
if err != nil {
return
}
if isBig {
balances . NextTransferBatch ++
}
if err := cache . PutNEP5Balances ( fromAddr , balances ) ; err != nil {
2020-03-05 14:11:58 +00:00
return
}
2020-03-05 07:45:50 +00:00
}
2020-03-05 14:11:58 +00:00
if ! toAddr . Equals ( util . Uint160 { } ) {
2020-03-11 15:22:46 +00:00
balances , err := cache . GetNEP5Balances ( toAddr )
2020-03-05 07:45:50 +00:00
if err != nil {
return
}
2020-07-28 09:23:58 +00:00
bs := balances . Trackers [ id ]
2020-07-09 09:57:24 +00:00
bs . Balance = * new ( big . Int ) . Add ( & bs . Balance , amount )
2020-03-05 07:45:50 +00:00
bs . LastUpdatedBlock = b . Index
2020-07-28 09:23:58 +00:00
balances . Trackers [ id ] = bs
2020-03-05 14:11:58 +00:00
2020-07-09 09:57:24 +00:00
transfer . Amount = * amount
2020-03-12 09:43:21 +00:00
isBig , err := cache . AppendNEP5Transfer ( toAddr , balances . NextTransferBatch , transfer )
if err != nil {
return
}
if isBig {
balances . NextTransferBatch ++
}
if err := cache . PutNEP5Balances ( toAddr , balances ) ; err != nil {
2020-03-05 14:11:58 +00:00
return
}
2020-03-05 07:45:50 +00:00
}
}
2020-03-05 12:16:03 +00:00
// GetNEP5TransferLog returns NEP5 transfer log for the acc.
func ( bc * Blockchain ) GetNEP5TransferLog ( acc util . Uint160 ) * state . NEP5TransferLog {
2020-03-12 09:43:21 +00:00
balances , err := bc . dao . GetNEP5Balances ( acc )
2020-03-05 12:16:03 +00:00
if err != nil {
return nil
}
2020-03-12 09:43:21 +00:00
result := new ( state . NEP5TransferLog )
for i := uint32 ( 0 ) ; i <= balances . NextTransferBatch ; i ++ {
lg , err := bc . dao . GetNEP5TransferLog ( acc , i )
if err != nil {
return nil
}
result . Raw = append ( result . Raw , lg . Raw ... )
}
return result
2020-03-05 12:16:03 +00:00
}
2020-03-11 15:22:46 +00:00
// GetNEP5Balances returns NEP5 balances for the acc.
func ( bc * Blockchain ) GetNEP5Balances ( acc util . Uint160 ) * state . NEP5Balances {
bs , err := bc . dao . GetNEP5Balances ( acc )
if err != nil {
return nil
}
return bs
}
2020-05-18 08:20:41 +00:00
// GetUtilityTokenBalance returns utility token (GAS) balance for the acc.
2020-07-09 09:57:24 +00:00
func ( bc * Blockchain ) GetUtilityTokenBalance ( acc util . Uint160 ) * big . Int {
2020-06-04 19:25:56 +00:00
bs , err := bc . dao . GetNEP5Balances ( acc )
if err != nil {
2020-07-09 09:57:24 +00:00
return big . NewInt ( 0 )
2020-06-04 19:25:56 +00:00
}
2020-07-28 09:23:58 +00:00
balance := bs . Trackers [ bc . contracts . GAS . ContractID ] . Balance
2020-07-09 09:57:24 +00:00
return & balance
2020-05-18 08:20:41 +00:00
}
2020-06-01 20:27:03 +00:00
// GetGoverningTokenBalance returns governing token (NEO) balance and the height
// of the last balance change for the account.
2020-07-09 09:57:24 +00:00
func ( bc * Blockchain ) GetGoverningTokenBalance ( acc util . Uint160 ) ( * big . Int , uint32 ) {
2020-06-01 20:27:03 +00:00
bs , err := bc . dao . GetNEP5Balances ( acc )
if err != nil {
2020-07-09 09:57:24 +00:00
return big . NewInt ( 0 ) , 0
2020-06-01 20:27:03 +00:00
}
2020-07-28 09:23:58 +00:00
neo := bs . Trackers [ bc . contracts . NEO . ContractID ]
2020-07-09 09:57:24 +00:00
return & neo . Balance , neo . LastUpdatedBlock
2020-06-01 20:27:03 +00:00
}
2020-02-06 15:47:03 +00:00
// LastBatch returns last persisted storage batch.
func ( bc * Blockchain ) LastBatch ( ) * storage . MemBatch {
return bc . lastBatch
}
2020-04-07 09:41:12 +00:00
// persist flushes current in-memory Store contents to the persistent storage.
2019-10-21 07:04:58 +00:00
func ( bc * Blockchain ) persist ( ) error {
2018-03-09 15:55:25 +00:00
var (
2018-03-14 09:36:59 +00:00
start = time . Now ( )
2019-10-17 09:27:15 +00:00
persisted int
2019-09-26 15:14:00 +00:00
err error
2018-03-09 15:55:25 +00:00
)
2019-12-12 18:17:13 +00:00
persisted , err = bc . dao . Persist ( )
2019-09-26 15:14:00 +00:00
if err != nil {
return err
2018-02-04 19:54:51 +00:00
}
2018-03-14 09:36:59 +00:00
if persisted > 0 {
2019-11-25 17:39:11 +00:00
bHeight , err := bc . dao . GetCurrentBlockHeight ( )
2019-11-06 13:10:37 +00:00
if err != nil {
return err
}
oldHeight := atomic . SwapUint32 ( & bc . persistedHeight , bHeight )
diff := bHeight - oldHeight
2019-11-25 17:39:11 +00:00
storedHeaderHeight , _ , err := bc . dao . GetCurrentHeaderHeight ( )
2019-11-06 13:10:37 +00:00
if err != nil {
return err
}
2019-12-30 07:43:05 +00:00
bc . log . Info ( "blockchain persist completed" ,
zap . Uint32 ( "persistedBlocks" , diff ) ,
zap . Int ( "persistedKeys" , persisted ) ,
zap . Uint32 ( "headerHeight" , storedHeaderHeight ) ,
zap . Uint32 ( "blockHeight" , bHeight ) ,
zap . Duration ( "took" , time . Since ( start ) ) )
2019-10-29 17:51:17 +00:00
// update monitoring metrics.
updatePersistedHeightMetric ( bHeight )
2018-03-14 09:36:59 +00:00
}
2019-09-26 15:14:00 +00:00
return nil
2018-03-14 09:36:59 +00:00
}
func ( bc * Blockchain ) headerListLen ( ) ( n int ) {
bc . headersOp <- func ( headerList * HeaderHashList ) {
n = headerList . Len ( )
}
<- bc . headersOpDone
2018-03-09 15:55:25 +00:00
return
}
2018-02-04 19:54:51 +00:00
2018-03-21 16:11:04 +00:00
// GetTransaction returns a TX and its height by the given hash.
func ( bc * Blockchain ) GetTransaction ( hash util . Uint256 ) ( * transaction . Transaction , uint32 , error ) {
2020-06-05 16:01:10 +00:00
if tx , ok := bc . memPool . TryGetValue ( hash ) ; ok {
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 tx , 0 , nil // the height is not actually defined for memPool transaction. Not sure if zero is a good number in this case.
}
2019-11-25 17:39:11 +00:00
return bc . dao . GetTransaction ( hash )
2018-03-21 16:11:04 +00:00
}
2020-02-21 14:56:28 +00:00
// GetAppExecResult returns application execution result by the given
// tx hash.
func ( bc * Blockchain ) GetAppExecResult ( hash util . Uint256 ) ( * state . AppExecResult , error ) {
return bc . dao . GetAppExecResult ( hash )
}
2019-10-11 11:08:21 +00:00
// GetStorageItem returns an item from storage.
2020-06-18 10:50:30 +00:00
func ( bc * Blockchain ) GetStorageItem ( id int32 , key [ ] byte ) * state . StorageItem {
return bc . dao . GetStorageItem ( id , key )
2019-10-11 11:08:21 +00:00
}
2020-06-18 10:50:30 +00:00
// GetStorageItems returns all storage items for a given contract id.
func ( bc * Blockchain ) GetStorageItems ( id int32 ) ( map [ string ] * state . StorageItem , error ) {
return bc . dao . GetStorageItems ( id )
2019-10-11 11:08:21 +00:00
}
2018-03-14 09:36:59 +00:00
// GetBlock returns a Block by the given hash.
2020-01-14 12:32:07 +00:00
func ( bc * Blockchain ) GetBlock ( hash util . Uint256 ) ( * block . Block , error ) {
2019-12-23 16:18:12 +00:00
topBlock := bc . topBlock . Load ( )
if topBlock != nil {
2020-01-14 12:32:07 +00:00
if tb , ok := topBlock . ( * block . Block ) ; ok && tb . Hash ( ) . Equals ( hash ) {
2019-12-23 16:18:12 +00:00
return tb , nil
}
}
2020-06-04 19:59:34 +00:00
block , err := bc . dao . GetBlock ( hash )
2019-09-26 15:14:00 +00:00
if err != nil {
2019-10-16 13:41:50 +00:00
return nil , err
2018-03-17 11:53:21 +00:00
}
2019-10-10 17:02:09 +00:00
for _ , tx := range block . Transactions {
2020-02-10 15:53:22 +00:00
stx , _ , err := bc . dao . GetTransaction ( tx . Hash ( ) )
2019-10-10 17:02:09 +00:00
if err != nil {
return nil , err
}
* tx = * stx
}
2018-03-17 11:53:21 +00:00
return block , nil
}
2019-09-03 14:51:37 +00:00
// GetHeader returns data block header identified with the given hash value.
2020-01-14 12:32:07 +00:00
func ( bc * Blockchain ) GetHeader ( hash util . Uint256 ) ( * block . Header , error ) {
2019-12-23 16:18:12 +00:00
topBlock := bc . topBlock . Load ( )
if topBlock != nil {
2020-01-14 12:32:07 +00:00
if tb , ok := topBlock . ( * block . Block ) ; ok && tb . Hash ( ) . Equals ( hash ) {
2019-12-23 16:18:12 +00:00
return tb . Header ( ) , nil
}
}
2020-06-04 19:59:34 +00:00
block , err := bc . dao . GetBlock ( hash )
2019-09-26 15:14:00 +00:00
if err != nil {
return nil , err
}
2018-03-17 11:53:21 +00:00
return block . Header ( ) , nil
2018-03-14 09:36:59 +00:00
}
2019-10-22 14:56:03 +00:00
// HasTransaction returns true if the blockchain contains he given
2018-03-14 09:36:59 +00:00
// transaction hash.
func ( bc * Blockchain ) HasTransaction ( hash util . Uint256 ) bool {
2019-11-25 17:39:11 +00:00
return bc . memPool . ContainsKey ( hash ) || bc . dao . HasTransaction ( hash )
2018-03-14 09:36:59 +00:00
}
2019-10-22 14:56:03 +00:00
// HasBlock returns true if the blockchain contains the given
2018-03-14 09:36:59 +00:00
// block hash.
func ( bc * Blockchain ) HasBlock ( hash util . Uint256 ) bool {
2019-02-20 17:39:32 +00:00
if header , err := bc . GetHeader ( hash ) ; err == nil {
2018-03-17 11:53:21 +00:00
return header . Index <= bc . BlockHeight ( )
}
2018-03-14 09:36:59 +00:00
return false
}
2019-02-09 15:53:58 +00:00
// CurrentBlockHash returns the highest processed block hash.
2018-03-09 15:55:25 +00:00
func ( bc * Blockchain ) CurrentBlockHash ( ) ( hash util . Uint256 ) {
bc . headersOp <- func ( headerList * HeaderHashList ) {
hash = headerList . Get ( int ( bc . BlockHeight ( ) ) )
}
<- bc . headersOpDone
return
2018-02-01 20:28:45 +00:00
}
2018-02-06 06:43:32 +00:00
2018-02-07 14:16:50 +00:00
// CurrentHeaderHash returns the hash of the latest known header.
func ( bc * Blockchain ) CurrentHeaderHash ( ) ( hash util . Uint256 ) {
2018-03-09 15:55:25 +00:00
bc . headersOp <- func ( headerList * HeaderHashList ) {
hash = headerList . Last ( )
}
<- bc . headersOpDone
return
2018-02-07 14:16:50 +00:00
}
2019-10-22 14:56:03 +00:00
// GetHeaderHash returns the hash from the headerList by its
2018-03-14 09:36:59 +00:00
// height/index.
func ( bc * Blockchain ) GetHeaderHash ( i int ) ( hash util . Uint256 ) {
bc . headersOp <- func ( headerList * HeaderHashList ) {
hash = headerList . Get ( i )
}
<- bc . headersOpDone
return
}
2018-03-09 15:55:25 +00:00
// BlockHeight returns the height/index of the highest block.
2018-02-06 06:43:32 +00:00
func ( bc * Blockchain ) BlockHeight ( ) uint32 {
2018-03-09 15:55:25 +00:00
return atomic . LoadUint32 ( & bc . blockHeight )
2018-02-06 06:43:32 +00:00
}
2018-03-09 15:55:25 +00:00
// HeaderHeight returns the index/height of the highest header.
2018-03-14 09:36:59 +00:00
func ( bc * Blockchain ) HeaderHeight ( ) uint32 {
return uint32 ( bc . headerListLen ( ) - 1 )
2018-02-06 06:43:32 +00:00
}
2019-09-30 16:52:16 +00:00
// GetContractState returns contract by its script hash.
2019-11-28 16:06:09 +00:00
func ( bc * Blockchain ) GetContractState ( hash util . Uint160 ) * state . Contract {
2019-12-11 10:10:51 +00:00
contract , err := bc . dao . GetContractState ( hash )
2019-11-25 17:39:11 +00:00
if contract == nil && err != storage . ErrKeyNotFound {
2019-12-30 07:43:05 +00:00
bc . log . Warn ( "failed to get contract state" , zap . Error ( err ) )
2019-09-30 16:52:16 +00:00
}
2019-11-25 17:39:11 +00:00
return contract
2019-09-30 16:52:16 +00:00
}
2020-07-28 13:36:47 +00:00
// GetContractScriptHash returns contract script hash by its ID.
func ( bc * Blockchain ) GetContractScriptHash ( id int32 ) ( util . Uint160 , error ) {
return bc . dao . GetContractScriptHash ( id )
}
2019-10-22 14:56:03 +00:00
// GetAccountState returns the account state from its script hash.
2019-11-28 16:06:09 +00:00
func ( bc * Blockchain ) GetAccountState ( scriptHash util . Uint160 ) * state . Account {
2019-11-25 17:39:11 +00:00
as , err := bc . dao . GetAccountState ( scriptHash )
2019-10-16 13:41:50 +00:00
if as == nil && err != storage . ErrKeyNotFound {
2019-12-30 07:43:05 +00:00
bc . log . Warn ( "failed to get account state" , zap . Error ( err ) )
2019-09-26 15:14:00 +00:00
}
2019-02-08 08:04:38 +00:00
return as
}
2019-10-22 14:56:03 +00:00
// GetConfig returns the config stored in the blockchain.
2019-02-20 17:39:32 +00:00
func ( bc * Blockchain ) GetConfig ( ) config . ProtocolConfiguration {
return bc . config
}
2020-05-12 14:20:41 +00:00
// SubscribeForBlocks adds given channel to new block event broadcasting, so when
// there is a new block added to the chain you'll receive it via this channel.
// Make sure it's read from regularly as not reading these events might affect
// other Blockchain functions.
func ( bc * Blockchain ) SubscribeForBlocks ( ch chan <- * block . Block ) {
bc . subCh <- ch
}
// SubscribeForTransactions adds given channel to new transaction event
// broadcasting, so when there is a new transaction added to the chain (in a
// block) you'll receive it via this channel. Make sure it's read from regularly
// as not reading these events might affect other Blockchain functions.
func ( bc * Blockchain ) SubscribeForTransactions ( ch chan <- * transaction . Transaction ) {
bc . subCh <- ch
}
// SubscribeForNotifications adds given channel to new notifications event
// broadcasting, so when an in-block transaction execution generates a
// notification you'll receive it via this channel. Only notifications from
// successful transactions are broadcasted, if you're interested in failed
// transactions use SubscribeForExecutions instead. Make sure this channel is
// read from regularly as not reading these events might affect other Blockchain
// functions.
func ( bc * Blockchain ) SubscribeForNotifications ( ch chan <- * state . NotificationEvent ) {
bc . subCh <- ch
}
// SubscribeForExecutions adds given channel to new transaction execution event
// broadcasting, so when an in-block transaction execution happens you'll receive
// the result of it via this channel. Make sure it's read from regularly as not
// reading these events might affect other Blockchain functions.
func ( bc * Blockchain ) SubscribeForExecutions ( ch chan <- * state . AppExecResult ) {
bc . subCh <- ch
}
// UnsubscribeFromBlocks unsubscribes given channel from new block notifications,
// you can close it afterwards. Passing non-subscribed channel is a no-op.
func ( bc * Blockchain ) UnsubscribeFromBlocks ( ch chan <- * block . Block ) {
bc . unsubCh <- ch
}
// UnsubscribeFromTransactions unsubscribes given channel from new transaction
// notifications, you can close it afterwards. Passing non-subscribed channel is
// a no-op.
func ( bc * Blockchain ) UnsubscribeFromTransactions ( ch chan <- * transaction . Transaction ) {
bc . unsubCh <- ch
}
// UnsubscribeFromNotifications unsubscribes given channel from new
// execution-generated notifications, you can close it afterwards. Passing
// non-subscribed channel is a no-op.
func ( bc * Blockchain ) UnsubscribeFromNotifications ( ch chan <- * state . NotificationEvent ) {
bc . unsubCh <- ch
}
// UnsubscribeFromExecutions unsubscribes given channel from new execution
// notifications, you can close it afterwards. Passing non-subscribed channel is
// a no-op.
func ( bc * Blockchain ) UnsubscribeFromExecutions ( ch chan <- * state . AppExecResult ) {
bc . unsubCh <- ch
}
2020-06-04 19:59:34 +00:00
// CalculateClaimable calculates the amount of GAS generated by owning specified
2020-06-04 20:16:43 +00:00
// amount of NEO between specified blocks. The amount of NEO being passed is in
// its natural non-divisible form (1 NEO as 1, 2 NEO as 2, no multiplication by
2020-06-29 17:23:23 +00:00
// 10⁸ is needed as for Fixed8).
2020-07-09 09:57:24 +00:00
func ( bc * Blockchain ) CalculateClaimable ( value * big . Int , startHeight , endHeight uint32 ) * big . Int {
2020-06-29 13:09:23 +00:00
var amount int64
2020-02-25 13:15:17 +00:00
di := uint32 ( bc . decrementInterval )
ustart := startHeight / di
if genSize := uint32 ( len ( bc . generationAmount ) ) ; ustart < genSize {
uend := endHeight / di
iend := endHeight % di
if uend >= genSize {
uend = genSize - 1
iend = di
} else if iend == 0 {
uend --
iend = di
}
istart := startHeight % di
for ustart < uend {
2020-06-29 13:09:23 +00:00
amount += int64 ( di - istart ) * int64 ( bc . generationAmount [ ustart ] )
2020-02-25 13:15:17 +00:00
ustart ++
istart = 0
}
2020-06-29 13:09:23 +00:00
amount += int64 ( iend - istart ) * int64 ( bc . generationAmount [ ustart ] )
2020-02-25 13:15:17 +00:00
}
2020-07-09 09:57:24 +00:00
return new ( big . Int ) . Mul ( big . NewInt ( amount ) , value )
2020-02-25 13:15:17 +00:00
}
2020-05-08 17:54:24 +00:00
// FeePerByte returns transaction network fee per byte.
2020-06-23 14:15:35 +00:00
func ( bc * Blockchain ) FeePerByte ( ) int64 {
return bc . contracts . Policy . GetFeePerByteInternal ( bc . dao )
2019-02-20 17:39:32 +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
// GetMemPool returns the memory pool of the blockchain.
2020-02-04 14:36:11 +00:00
func ( bc * Blockchain ) GetMemPool ( ) * mempool . Pool {
return & bc . memPool
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
}
2020-02-18 17:16:38 +00:00
// ApplyPolicyToTxSet applies configured policies to given transaction set. It
// expects slice to be ordered by fee and returns a subslice of it.
2020-06-05 16:01:10 +00:00
func ( bc * Blockchain ) ApplyPolicyToTxSet ( txes [ ] * transaction . Transaction ) [ ] * transaction . Transaction {
2020-06-15 18:13:32 +00:00
maxTx := bc . contracts . Policy . GetMaxTransactionsPerBlockInternal ( bc . dao )
if maxTx != 0 && len ( txes ) > int ( maxTx ) {
txes = txes [ : maxTx ]
2020-02-18 17:16:38 +00:00
}
return txes
}
2020-02-29 14:52:09 +00:00
func ( bc * Blockchain ) verifyHeader ( currHeader , prevHeader * block . Header ) error {
if prevHeader . Hash ( ) != currHeader . PrevHash {
return errors . New ( "previous header hash doesn't match" )
2019-10-15 09:52:10 +00:00
}
2020-02-29 14:52:09 +00:00
if prevHeader . Index + 1 != currHeader . Index {
2019-10-15 09:52:10 +00:00
return errors . New ( "previous header index doesn't match" )
}
2020-02-29 14:52:09 +00:00
if prevHeader . Timestamp >= currHeader . Timestamp {
2019-10-15 09:52:10 +00:00
return errors . New ( "block is not newer than the previous one" )
}
2020-02-29 14:52:09 +00:00
return bc . verifyHeaderWitnesses ( currHeader , prevHeader )
2019-10-15 09:52:10 +00:00
}
2020-02-04 15:43:21 +00:00
// verifyTx verifies whether a transaction is bonafide or not.
func ( bc * Blockchain ) verifyTx ( t * transaction . Transaction , block * block . Block ) error {
2020-04-15 06:50:13 +00:00
height := bc . BlockHeight ( )
if t . ValidUntilBlock <= height || t . ValidUntilBlock > height + transaction . MaxValidUntilBlockIncrement {
return errors . Errorf ( "transaction has expired. ValidUntilBlock = %d, current height = %d" , t . ValidUntilBlock , height )
}
2020-06-17 11:40:33 +00:00
hashes , err := bc . GetScriptHashesForVerifying ( t )
if err != nil {
return err
}
blockedAccounts , err := bc . contracts . Policy . GetBlockedAccountsInternal ( bc . dao )
if err != nil {
return err
}
for _ , h := range hashes {
i := sort . Search ( len ( blockedAccounts ) , func ( i int ) bool {
return ! blockedAccounts [ i ] . Less ( h )
} )
if i != len ( blockedAccounts ) && blockedAccounts [ i ] . Equals ( h ) {
return errors . Errorf ( "policy check failed" )
}
}
2020-05-18 08:20:41 +00:00
balance := bc . GetUtilityTokenBalance ( t . Sender )
2020-06-23 14:15:35 +00:00
need := t . SystemFee + t . NetworkFee
2020-07-09 09:57:24 +00:00
if balance . Cmp ( big . NewInt ( need ) ) < 0 {
2020-05-08 17:54:24 +00:00
return errors . Errorf ( "insufficient funds: balance is %v, need: %v" , balance , need )
}
size := io . GetVarSize ( t )
if size > transaction . MaxTransactionSize {
2019-09-16 13:08:00 +00:00
return errors . Errorf ( "invalid transaction size = %d. It shoud be less then MaxTransactionSize = %d" , io . GetVarSize ( t ) , transaction . MaxTransactionSize )
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
}
2020-06-23 14:15:35 +00:00
needNetworkFee := int64 ( size ) * bc . FeePerByte ( )
netFee := t . NetworkFee - needNetworkFee
2020-05-08 17:54:24 +00:00
if netFee < 0 {
return errors . Errorf ( "insufficient funds: net fee is %v, need %v" , t . NetworkFee , needNetworkFee )
}
2019-11-19 17:37:27 +00:00
if block == nil {
2020-05-18 08:20:41 +00:00
if ok := bc . memPool . Verify ( t , bc ) ; ! ok {
2019-11-19 17:37:27 +00:00
return errors . New ( "invalid transaction due to conflicts with the memory pool" )
}
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
}
2019-10-15 09:52:10 +00:00
return bc . verifyTxWitnesses ( t , block )
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
}
2020-02-05 21:23:49 +00:00
// isTxStillRelevant is a callback for mempool transaction filtering after the
// new block addition. It returns false for transactions already present in the
// chain (added by the new block), transactions using some inputs that are
// already used (double spends) and does witness reverification for non-standard
// contracts. It operates under the assumption that full transaction verification
// was already done so we don't need to check basic things like size, input/output
// correctness, etc.
func ( bc * Blockchain ) isTxStillRelevant ( t * transaction . Transaction ) bool {
var recheckWitness bool
if bc . dao . HasTransaction ( t . Hash ( ) ) {
return false
}
for i := range t . Scripts {
if ! vm . IsStandardContract ( t . Scripts [ i ] . VerificationScript ) {
recheckWitness = true
break
}
}
if recheckWitness {
return bc . verifyTxWitnesses ( t , nil ) == nil
}
return true
}
2020-05-29 14:20:00 +00:00
// AddStateRoot add new (possibly unverified) state root to the blockchain.
func ( bc * Blockchain ) AddStateRoot ( r * state . MPTRoot ) error {
our , err := bc . GetStateRoot ( r . Index )
if err == nil {
if our . Flag == state . Verified {
2020-06-22 07:42:46 +00:00
return bc . updateStateHeight ( r . Index )
2020-05-29 14:20:00 +00:00
} else if r . Witness == nil && our . Witness != nil {
r . Witness = our . Witness
}
}
if err := bc . verifyStateRoot ( r ) ; err != nil {
return errors . WithMessage ( err , "invalid state root" )
}
if r . Index > bc . BlockHeight ( ) { // just put it into the store for future checks
return bc . dao . PutStateRoot ( & state . MPTRootState {
MPTRoot : * r ,
Flag : state . Unverified ,
} )
}
flag := state . Unverified
if r . Witness != nil {
if err := bc . verifyStateRootWitness ( r ) ; err != nil {
return errors . WithMessage ( err , "can't verify signature" )
}
flag = state . Verified
}
2020-06-22 07:42:46 +00:00
err = bc . dao . PutStateRoot ( & state . MPTRootState {
2020-05-29 14:20:00 +00:00
MPTRoot : * r ,
Flag : flag ,
} )
2020-06-22 07:42:46 +00:00
if err != nil {
return err
}
return bc . updateStateHeight ( r . Index )
}
func ( bc * Blockchain ) updateStateHeight ( newHeight uint32 ) error {
h , err := bc . dao . GetCurrentStateRootHeight ( )
if err != nil {
return errors . WithMessage ( err , "can't get current state root height" )
} else if newHeight == h + 1 {
2020-06-24 11:47:08 +00:00
updateStateHeightMetric ( newHeight )
2020-06-22 07:42:46 +00:00
return bc . dao . PutCurrentStateRootHeight ( h + 1 )
}
return nil
2020-05-29 14:20:00 +00:00
}
// verifyStateRoot checks if state root is valid.
func ( bc * Blockchain ) verifyStateRoot ( r * state . MPTRoot ) error {
if r . Index == 0 {
return nil
}
prev , err := bc . GetStateRoot ( r . Index - 1 )
if err != nil {
return errors . New ( "can't get previous state root" )
2020-06-04 14:19:30 +00:00
} else if ! r . PrevHash . Equals ( hash . DoubleSha256 ( prev . GetSignedPart ( ) ) ) {
2020-05-29 14:20:00 +00:00
return errors . New ( "previous hash mismatch" )
} else if prev . Version != r . Version {
return errors . New ( "version mismatch" )
}
return nil
}
// verifyStateRootWitness verifies that state root signature is correct.
func ( bc * Blockchain ) verifyStateRootWitness ( r * state . MPTRoot ) error {
b , err := bc . GetBlock ( bc . GetHeaderHash ( int ( r . Index ) ) )
if err != nil {
return err
}
interopCtx := bc . newInteropContext ( trigger . Verification , bc . dao , nil , nil )
interopCtx . Container = r
return bc . verifyHashAgainstScript ( b . NextConsensus , r . Witness , interopCtx , true ,
bc . contracts . Policy . GetMaxVerificationGas ( interopCtx . DAO ) )
}
2020-02-04 15:43:21 +00:00
// VerifyTx verifies whether a transaction is bonafide or not. Block parameter
// is used for easy interop access and can be omitted for transactions that are
// not yet added into any block.
// Golang implementation of Verify method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L270).
func ( bc * Blockchain ) VerifyTx ( t * transaction . Transaction , block * block . Block ) error {
bc . lock . RLock ( )
defer bc . lock . RUnlock ( )
return bc . verifyTx ( t , block )
}
// PoolTx verifies and tries to add given transaction into the mempool.
func ( bc * Blockchain ) PoolTx ( t * transaction . Transaction ) error {
bc . lock . RLock ( )
defer bc . lock . RUnlock ( )
if bc . HasTransaction ( t . Hash ( ) ) {
return ErrAlreadyExists
}
if err := bc . verifyTx ( t , nil ) ; err != nil {
return err
}
2020-02-18 17:16:38 +00:00
// Policying.
2020-06-15 18:13:32 +00:00
if ok , err := bc . contracts . Policy . CheckPolicy ( bc . newInteropContext ( trigger . Application , bc . dao , nil , t ) , t ) ; err != nil {
return err
} else if ! ok {
return ErrPolicy
}
2020-02-05 11:24:36 +00:00
if err := bc . memPool . Add ( t , bc ) ; err != nil {
2020-02-04 15:43:21 +00:00
switch err {
case mempool . ErrOOM :
return ErrOOM
case mempool . ErrConflict :
return ErrAlreadyExists
default :
return err
}
}
return nil
}
2019-11-11 15:25:28 +00:00
//GetStandByValidators returns validators from the configuration.
2020-06-23 15:15:55 +00:00
func ( bc * Blockchain ) GetStandByValidators ( ) keys . PublicKeys {
2020-06-29 07:44:31 +00:00
return bc . sbValidators . Copy ( )
2019-11-11 15:25:28 +00:00
}
2020-07-11 10:10:57 +00:00
// GetValidators returns current validators.
2020-04-26 17:04:16 +00:00
func ( bc * Blockchain ) GetValidators ( ) ( [ ] * keys . PublicKey , error ) {
2020-07-11 09:29:06 +00:00
return bc . contracts . NEO . GetValidatorsInternal ( bc , bc . dao )
2019-11-18 12:24:48 +00:00
}
2020-07-11 10:10:57 +00:00
// GetNextBlockValidators returns next block validators.
func ( bc * Blockchain ) GetNextBlockValidators ( ) ( [ ] * keys . PublicKey , error ) {
return bc . contracts . NEO . GetNextBlockValidatorsInternal ( bc , bc . dao )
}
2020-04-26 17:04:16 +00:00
// GetEnrollments returns all registered validators.
func ( bc * Blockchain ) GetEnrollments ( ) ( [ ] state . Validator , error ) {
return bc . contracts . NEO . GetRegisteredValidators ( bc . dao )
2019-11-18 12:24:48 +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
// GetScriptHashesForVerifying returns all the ScriptHashes of a transaction which will be use
// to verify whether the transaction is bonafide or not.
// Golang implementation of GetScriptHashesForVerifying method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L190)
func ( bc * Blockchain ) GetScriptHashesForVerifying ( t * transaction . Transaction ) ( [ ] util . Uint160 , error ) {
hashes := make ( map [ util . Uint160 ] bool )
2020-04-16 14:10:42 +00:00
hashes [ t . Sender ] = true
2020-04-30 13:00:33 +00:00
for _ , c := range t . Cosigners {
hashes [ c . Account ] = true
}
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
// convert hashes to []util.Uint160
hashesResult := make ( [ ] util . Uint160 , 0 , len ( hashes ) )
for h := range hashes {
hashesResult = append ( hashesResult , h )
}
return hashesResult , nil
}
2019-10-29 15:31:39 +00:00
// GetTestVM returns a VM and a Store setup for a test run of some sort of code.
2020-06-10 11:45:55 +00:00
func ( bc * Blockchain ) GetTestVM ( tx * transaction . Transaction ) * vm . VM {
systemInterop := bc . newInteropContext ( trigger . Application , bc . dao , nil , tx )
2020-07-28 13:38:00 +00:00
vm := systemInterop . SpawnVM ( )
2020-01-21 12:37:47 +00:00
vm . SetPriceGetter ( getPrice )
2020-04-03 06:49:01 +00:00
return vm
2019-10-29 15:31:39 +00:00
}
2019-12-25 15:18:17 +00:00
// ScriptFromWitness returns verification script for provided witness.
// If hash is not equal to the witness script hash, error is returned.
func ScriptFromWitness ( hash util . Uint160 , witness * transaction . Witness ) ( [ ] byte , error ) {
2019-10-15 09:52:10 +00:00
verification := witness . VerificationScript
if len ( verification ) == 0 {
2020-02-03 14:46:51 +00:00
bb := io . NewBufBinWriter ( )
2020-05-07 11:58:59 +00:00
emit . AppCall ( bb . BinWriter , hash )
2019-10-15 09:52:10 +00:00
verification = bb . Bytes ( )
2019-12-25 15:18:17 +00:00
} else if h := witness . ScriptHash ( ) ; hash != h {
return nil , errors . New ( "witness hash mismatch" )
}
return verification , nil
}
// verifyHashAgainstScript verifies given hash against the given witness.
2020-07-13 15:24:58 +00:00
func ( bc * Blockchain ) verifyHashAgainstScript ( hash util . Uint160 , witness * transaction . Witness , interopCtx * interop . Context , useKeys bool , gas int64 ) error {
2019-12-25 15:18:17 +00:00
verification , err := ScriptFromWitness ( hash , witness )
if err != nil {
return err
2019-10-15 09:52:10 +00:00
}
2020-07-25 11:32:04 +00:00
gasPolicy := bc . contracts . Policy . GetMaxVerificationGas ( interopCtx . DAO )
if gas > gasPolicy {
gas = gasPolicy
}
2020-07-28 13:38:00 +00:00
vm := interopCtx . SpawnVM ( )
2020-07-13 15:24:58 +00:00
vm . SetPriceGetter ( getPrice )
vm . GasLimit = gas
2020-06-10 14:21:26 +00:00
vm . LoadScriptWithFlags ( verification , smartcontract . ReadOnly )
2019-10-15 09:52:10 +00:00
vm . LoadScript ( witness . InvocationScript )
core: protect (*Blockchain).keyCache with a lock
Avoid failures like this:
fatal error: concurrent map writes
goroutine 103 [running]:
runtime.throw(0xca6fe3, 0x15)
/usr/lib64/go/1.12/src/runtime/panic.go:617 +0x72 fp=0xc000687988 sp=0xc000687958 pc=0x42d8d2
runtime.mapassign(0xb9d4a0, 0xc0000b1f80, 0xc000687a6c, 0x13bd940)
/usr/lib64/go/1.12/src/runtime/map.go:590 +0x5e3 fp=0xc000687a10 sp=0xc000687988 pc=0x40e2a3
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHashAgainstScript(0xc0000f8000, 0x1300d1f5a3d348be, 0x78607089e4fe9fab, 0xc0a21e4f71, 0xc000d16070, 0x1e2b8881f8178e92, 0xd589cfa965287bb8, 0x24a4e4f292ebc797, 0xb90d3120d878e619, 0xc0001d01e0, ...)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1866 +0x371 fp=0xc000687ad8 sp=0xc000687a10 pc=0x984ed1
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHeaderWitnesses(0xc0000f8000, 0xc000d16000, 0xc00042d950, 0x1, 0xc00008a6c0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1914 +0x209 fp=0xc000687c90 sp=0xc000687ad8 pc=0x985999
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHeader(0xc0000f8000, 0xc000d16000, 0xc00042d950, 0xf0ee0049d611101f, 0xf8967398a8f9351b)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1254 +0x235 fp=0xc000687d70 sp=0xc000687c90 pc=0x97e4e5
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).addHeaders(0xc0000f8000, 0x1, 0xc000d12000, 0x7d0, 0x7d0, 0x0, 0x1)
/home/rik/dev/neo-go/pkg/core/blockchain.go:369 +0x266 fp=0xc000687e68 sp=0xc000687d70 pc=0x973f46
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddHeaders(0xc0000f8000, 0xc000d12000, 0x7d0, 0x7d0, 0x0, 0xc0003640c0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:338 +0x51 fp=0xc000687eb0 sp=0xc000687e68 pc=0x973cb1
github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleHeadersCmd(0xc00022c140, 0xdd0ca0, 0xc000374000, 0xc00000c020)
/home/rik/dev/neo-go/pkg/network/server.go:431 +0x74 fp=0xc000687fc0 sp=0xc000687eb0 pc=0x9ce5e4
runtime.goexit()
/usr/lib64/go/1.12/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000687fc8 sp=0xc000687fc0 pc=0x45ca51
created by github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage
/home/rik/dev/neo-go/pkg/network/server.go:705 +0xa4a
...
2020-03-04 17:26:18 +00:00
if useKeys {
bc . keyCacheLock . RLock ( )
if bc . keyCache [ hash ] != nil {
vm . SetPublicKeys ( bc . keyCache [ hash ] )
}
bc . keyCacheLock . RUnlock ( )
2019-12-10 16:13:29 +00:00
}
2019-12-25 15:18:17 +00:00
err = vm . Run ( )
2019-10-15 09:52:10 +00:00
if vm . HasFailed ( ) {
2019-10-22 10:44:14 +00:00
return errors . Errorf ( "vm failed to execute the script with error: %s" , err )
2019-10-15 09:52:10 +00:00
}
resEl := vm . Estack ( ) . Pop ( )
if resEl != nil {
2020-04-28 08:23:58 +00:00
if ! resEl . Bool ( ) {
2019-10-15 09:52:10 +00:00
return errors . Errorf ( "signature check failed" )
}
core: protect (*Blockchain).keyCache with a lock
Avoid failures like this:
fatal error: concurrent map writes
goroutine 103 [running]:
runtime.throw(0xca6fe3, 0x15)
/usr/lib64/go/1.12/src/runtime/panic.go:617 +0x72 fp=0xc000687988 sp=0xc000687958 pc=0x42d8d2
runtime.mapassign(0xb9d4a0, 0xc0000b1f80, 0xc000687a6c, 0x13bd940)
/usr/lib64/go/1.12/src/runtime/map.go:590 +0x5e3 fp=0xc000687a10 sp=0xc000687988 pc=0x40e2a3
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHashAgainstScript(0xc0000f8000, 0x1300d1f5a3d348be, 0x78607089e4fe9fab, 0xc0a21e4f71, 0xc000d16070, 0x1e2b8881f8178e92, 0xd589cfa965287bb8, 0x24a4e4f292ebc797, 0xb90d3120d878e619, 0xc0001d01e0, ...)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1866 +0x371 fp=0xc000687ad8 sp=0xc000687a10 pc=0x984ed1
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHeaderWitnesses(0xc0000f8000, 0xc000d16000, 0xc00042d950, 0x1, 0xc00008a6c0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1914 +0x209 fp=0xc000687c90 sp=0xc000687ad8 pc=0x985999
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).verifyHeader(0xc0000f8000, 0xc000d16000, 0xc00042d950, 0xf0ee0049d611101f, 0xf8967398a8f9351b)
/home/rik/dev/neo-go/pkg/core/blockchain.go:1254 +0x235 fp=0xc000687d70 sp=0xc000687c90 pc=0x97e4e5
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).addHeaders(0xc0000f8000, 0x1, 0xc000d12000, 0x7d0, 0x7d0, 0x0, 0x1)
/home/rik/dev/neo-go/pkg/core/blockchain.go:369 +0x266 fp=0xc000687e68 sp=0xc000687d70 pc=0x973f46
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddHeaders(0xc0000f8000, 0xc000d12000, 0x7d0, 0x7d0, 0x0, 0xc0003640c0)
/home/rik/dev/neo-go/pkg/core/blockchain.go:338 +0x51 fp=0xc000687eb0 sp=0xc000687e68 pc=0x973cb1
github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleHeadersCmd(0xc00022c140, 0xdd0ca0, 0xc000374000, 0xc00000c020)
/home/rik/dev/neo-go/pkg/network/server.go:431 +0x74 fp=0xc000687fc0 sp=0xc000687eb0 pc=0x9ce5e4
runtime.goexit()
/usr/lib64/go/1.12/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000687fc8 sp=0xc000687fc0 pc=0x45ca51
created by github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage
/home/rik/dev/neo-go/pkg/network/server.go:705 +0xa4a
...
2020-03-04 17:26:18 +00:00
if useKeys {
bc . keyCacheLock . RLock ( )
_ , ok := bc . keyCache [ hash ]
bc . keyCacheLock . RUnlock ( )
if ! ok {
bc . keyCacheLock . Lock ( )
bc . keyCache [ hash ] = vm . GetPublicKeys ( )
bc . keyCacheLock . Unlock ( )
}
2019-12-10 16:13:29 +00:00
}
2019-10-15 09:52:10 +00:00
} else {
return errors . Errorf ( "no result returned from the script" )
}
return nil
}
2019-10-22 14:56:03 +00:00
// verifyTxWitnesses verifies the scripts (witnesses) that come with a given
2019-09-30 14:39:42 +00:00
// transaction. It can reorder them by ScriptHash, because that's required to
2019-10-11 14:00:11 +00:00
// match a slice of script hashes from the Blockchain. Block parameter
// is used for easy interop access and can be omitted for transactions that are
// not yet added into any block.
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
// Golang implementation of VerifyWitnesses method in C# (https://github.com/neo-project/neo/blob/master/neo/SmartContract/Helper.cs#L87).
2020-01-14 12:32:07 +00:00
func ( bc * Blockchain ) verifyTxWitnesses ( t * transaction . Transaction , block * block . Block ) error {
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
hashes , err := bc . GetScriptHashesForVerifying ( t )
if err != nil {
return err
}
witnesses := t . Scripts
if len ( hashes ) != len ( witnesses ) {
return errors . Errorf ( "expected len(hashes) == len(witnesses). got: %d != %d" , len ( hashes ) , len ( witnesses ) )
}
2019-09-30 14:39:42 +00:00
sort . Slice ( hashes , func ( i , j int ) bool { return hashes [ i ] . Less ( hashes [ j ] ) } )
sort . Slice ( witnesses , func ( i , j int ) bool { return witnesses [ i ] . ScriptHash ( ) . Less ( witnesses [ j ] . ScriptHash ( ) ) } )
2020-04-03 06:49:01 +00:00
interopCtx := bc . newInteropContext ( trigger . Verification , bc . dao , block , t )
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
for i := 0 ; i < len ( hashes ) ; i ++ {
2020-07-13 17:12:13 +00:00
err := bc . verifyHashAgainstScript ( hashes [ i ] , & witnesses [ i ] , interopCtx , false , t . NetworkFee )
2019-10-15 09:52:10 +00:00
if err != nil {
numStr := fmt . Sprintf ( "witness #%d" , i )
return errors . Wrap ( err , numStr )
2019-09-23 17:13:44 +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
}
return nil
}
2020-02-29 14:52:09 +00:00
// verifyHeaderWitnesses is a block-specific implementation of VerifyWitnesses logic.
func ( bc * Blockchain ) verifyHeaderWitnesses ( currHeader , prevHeader * block . Header ) error {
2019-10-15 09:52:10 +00:00
var hash util . Uint160
2020-02-29 14:52:09 +00:00
if prevHeader == nil && currHeader . PrevHash . Equals ( util . Uint256 { } ) {
hash = currHeader . Script . ScriptHash ( )
2019-10-15 09:52:10 +00:00
} else {
hash = prevHeader . NextConsensus
}
2020-04-03 06:49:01 +00:00
interopCtx := bc . newInteropContext ( trigger . Verification , bc . dao , nil , nil )
2020-04-14 14:24:21 +00:00
interopCtx . Container = currHeader
2020-07-13 15:24:58 +00:00
return bc . verifyHashAgainstScript ( hash , & currHeader . Script , interopCtx , true , verificationGasLimit )
2019-10-15 09:52:10 +00:00
}
2020-05-20 19:32:59 +00:00
// GoverningTokenHash returns the governing token (NEO) native contract hash.
func ( bc * Blockchain ) GoverningTokenHash ( ) util . Uint160 {
return bc . contracts . NEO . Hash
}
// UtilityTokenHash returns the utility token (GAS) native contract hash.
func ( bc * Blockchain ) UtilityTokenHash ( ) util . Uint160 {
return bc . contracts . GAS . Hash
}
2018-02-06 06:43:32 +00:00
func hashAndIndexToBytes ( h util . Uint256 , index uint32 ) [ ] byte {
2019-09-16 09:18:13 +00:00
buf := io . NewBufBinWriter ( )
2019-11-27 09:23:18 +00:00
buf . WriteBytes ( h . BytesLE ( ) )
2019-12-12 15:52:23 +00:00
buf . WriteU32LE ( index )
2019-09-16 09:18:13 +00:00
return buf . Bytes ( )
2018-02-06 06:43:32 +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
func ( bc * Blockchain ) secondsPerBlock ( ) int {
return bc . config . SecondsPerBlock
}
2019-12-30 11:01:49 +00:00
2020-04-08 10:35:39 +00:00
func ( bc * Blockchain ) newInteropContext ( trigger trigger . Type , d dao . DAO , block * block . Block , tx * transaction . Transaction ) * interop . Context {
2020-04-22 20:00:18 +00:00
ic := interop . NewContext ( trigger , bc , d , bc . contracts . Contracts , block , tx , bc . log )
2020-07-28 13:38:00 +00:00
ic . Functions = [ ] [ ] interop . Function { systemInterops , neoInterops }
2020-04-13 13:31:04 +00:00
switch {
case tx != nil :
ic . Container = tx
case block != nil :
ic . Container = block
}
return ic
2019-12-30 11:01:49 +00:00
}