2018-02-01 20:28:45 +00:00
package core
2018-02-04 19:54:51 +00:00
import (
2018-02-06 06:43:32 +00:00
"bytes"
2019-02-19 11:48:48 +00:00
"context"
2018-02-06 06:43:32 +00:00
"encoding/binary"
2018-03-09 15:55:25 +00:00
"fmt"
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
"math"
2018-03-09 15:55:25 +00:00
"sync/atomic"
2018-02-04 19:54:51 +00:00
"time"
2018-03-25 10:45:54 +00:00
"github.com/CityOfZion/neo-go/config"
2018-03-17 11:53:21 +00:00
"github.com/CityOfZion/neo-go/pkg/core/storage"
2018-03-21 16:11:04 +00:00
"github.com/CityOfZion/neo-go/pkg/core/transaction"
2018-02-04 19:54:51 +00:00
"github.com/CityOfZion/neo-go/pkg/util"
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"
2018-03-14 09:36:59 +00:00
log "github.com/sirupsen/logrus"
2018-02-04 19:54:51 +00:00
)
2018-02-01 20:28:45 +00:00
// tuning parameters
const (
2018-03-09 15:55:25 +00:00
headerBatchCount = 2000
2018-03-25 10:45:54 +00:00
version = "0.0.1"
2018-02-01 20:28:45 +00:00
)
var (
2018-03-25 10:45:54 +00:00
genAmount = [ ] int { 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }
decrementInterval = 2000000
2018-04-13 10:14:08 +00:00
persistInterval = 1 * time . Second
2018-02-01 20:28:45 +00:00
)
2018-03-25 10:45:54 +00:00
// Blockchain represents the blockchain.
2018-02-01 20:28:45 +00:00
type Blockchain struct {
2018-03-25 10:45:54 +00:00
config config . ProtocolConfiguration
2018-02-01 20:28:45 +00:00
// Any object that satisfies the BlockchainStorer interface.
2018-03-17 11:53:21 +00:00
storage . Store
2018-02-01 20:28:45 +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().
2018-03-17 11:53:21 +00:00
// Write access should only happen in persist().
2018-03-09 15:55:25 +00:00
blockHeight uint32
2018-02-04 19:54:51 +00:00
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
2018-03-09 15:55:25 +00:00
blockCache * Cache
2018-02-06 06:43:32 +00:00
2018-03-17 11:53:21 +00:00
// All operation on headerList must be called from an
// 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
// Whether we will verify received blocks.
verifyBlocks bool
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
memPool MemPool
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
2018-03-17 11:53:21 +00:00
// NewBlockchain return a new blockchain object the will use the
// given Store as its underlying storage.
2019-02-19 11:48:48 +00:00
func NewBlockchain ( ctx context . Context , s storage . Store , cfg config . ProtocolConfiguration ) ( * Blockchain , error ) {
2018-03-09 15:55:25 +00:00
bc := & Blockchain {
2018-03-25 10:45:54 +00:00
config : cfg ,
2018-03-09 15:55:25 +00:00
Store : s ,
headersOp : make ( chan headersOpFunc ) ,
headersOpDone : make ( chan struct { } ) ,
blockCache : NewCache ( ) ,
2018-03-14 09:36:59 +00:00
verifyBlocks : false ,
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
memPool : NewMemPool ( 50000 ) ,
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
go bc . run ( ctx )
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
}
2019-01-22 12:14:52 +00:00
// GetBlockchainLevelDB returns blockchain based on configuration
2019-02-19 11:48:48 +00:00
func NewBlockchainLevelDB ( ctx context . Context , cfg config . Config ) ( * Blockchain , error ) {
2019-01-22 12:14:52 +00:00
store , err := storage . NewLevelDBStore (
2019-02-19 11:48:48 +00:00
ctx ,
2019-01-22 12:14:52 +00:00
cfg . ApplicationConfiguration . DataDirectoryPath ,
nil ,
)
if err != nil {
return nil , err
}
2019-02-19 11:48:48 +00:00
return NewBlockchain ( ctx , store , cfg . ProtocolConfiguration )
2019-01-22 12:14:52 +00:00
}
2018-03-17 11:53:21 +00:00
func ( bc * Blockchain ) init ( ) error {
2018-03-25 10:45:54 +00:00
genesisBlock , err := createGenesisBlock ( bc . config )
2018-03-17 11:53:21 +00:00
if err != nil {
2018-03-25 10:45:54 +00:00
return err
}
bc . headerList = NewHeaderHashList ( genesisBlock . Hash ( ) )
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.
ver , err := storage . Version ( bc . Store )
if err != nil {
log . Infof ( "no storage version found! creating genesis block" )
2019-01-25 11:20:35 +00:00
if err = storage . PutVersion ( bc . Store , version ) ; err != nil {
return err
}
2018-04-09 16:58:09 +00:00
return bc . persistBlock ( genesisBlock )
}
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.
2018-04-09 16:58:09 +00:00
log . Infof ( "restoring blockchain with version: %s" , version )
2018-03-25 10:45:54 +00:00
2018-04-09 16:58:09 +00:00
bHeight , err := storage . CurrentBlockHeight ( bc . Store )
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
2018-03-17 11:53:21 +00:00
2018-04-09 16:58:09 +00:00
hashes , err := storage . HeaderHashes ( bc . Store )
2018-03-17 11:53:21 +00:00
if err != nil {
return err
}
2018-03-25 10:45:54 +00:00
2018-03-17 11:53:21 +00:00
for _ , hash := range hashes {
2018-03-25 10:45:54 +00:00
if ! genesisBlock . Hash ( ) . Equals ( hash ) {
2018-03-17 11:53:21 +00:00
bc . headerList . Add ( hash )
bc . storedHeaderCount ++
}
}
2018-04-09 16:58:09 +00:00
currHeaderHeight , currHeaderHash , err := storage . CurrentHeaderHeight ( bc . Store )
2018-03-17 11:53:21 +00:00
if err != nil {
return err
}
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.
if currHeaderHeight > bc . storedHeaderCount {
hash := currHeaderHash
targetHash := bc . headerList . Get ( bc . headerList . Len ( ) - 1 )
2018-04-09 16:58:09 +00:00
headers := make ( [ ] * 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 )
if err := bc . AddHeaders ( headers ... ) ; err != nil {
return err
}
}
return nil
2018-03-09 15:55:25 +00:00
}
2019-02-19 11:48:48 +00:00
func ( bc * Blockchain ) run ( ctx context . Context ) {
2018-03-17 11:53:21 +00:00
persistTimer := time . NewTimer ( persistInterval )
2019-02-19 11:48:48 +00:00
defer persistTimer . Stop ( )
2018-03-09 15:55:25 +00:00
for {
select {
2019-02-19 11:48:48 +00:00
case <- ctx . Done ( ) :
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-02-19 11:48:48 +00:00
go bc . persist ( ctx )
2018-03-14 09:36:59 +00:00
persistTimer . Reset ( persistInterval )
2018-03-09 15:55:25 +00:00
}
2018-02-04 19:54:51 +00:00
}
}
2018-03-14 09:36:59 +00:00
// AddBlock processes the given block and will add it to the cache so it
// can be persisted.
2018-02-04 19:54:51 +00:00
func ( bc * Blockchain ) AddBlock ( block * Block ) error {
2018-03-09 15:55:25 +00:00
if ! bc . blockCache . Has ( block . Hash ( ) ) {
bc . blockCache . Add ( block . Hash ( ) , block )
}
2018-02-04 19:54:51 +00:00
2018-03-14 09:36:59 +00:00
headerLen := bc . headerListLen ( )
2018-02-04 19:54:51 +00:00
if int ( block . Index - 1 ) >= headerLen {
return nil
}
if int ( block . Index ) == headerLen {
2018-03-10 12:04:06 +00:00
if bc . verifyBlocks && ! block . Verify ( false ) {
return fmt . Errorf ( "block %s is invalid" , block . Hash ( ) )
}
return bc . AddHeaders ( block . Header ( ) )
2018-02-01 20:28:45 +00:00
}
2018-03-10 12:04:06 +00:00
return nil
2018-02-04 19:54:51 +00:00
}
2018-03-14 09:36:59 +00:00
// AddHeaders will process the given headers and add them to the
// HeaderHashList.
2018-03-09 15:55:25 +00:00
func ( bc * Blockchain ) AddHeaders ( headers ... * Header ) ( err error ) {
var (
start = time . Now ( )
2018-03-17 11:53:21 +00:00
batch = bc . Batch ( )
2018-03-09 15:55:25 +00:00
)
bc . headersOp <- func ( headerList * HeaderHashList ) {
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
}
2018-03-14 09:36:59 +00:00
if batch . Len ( ) > 0 {
2018-03-17 11:53:21 +00:00
if err = bc . PutBatch ( batch ) ; err != nil {
2018-03-09 15:55:25 +00:00
return
}
2018-03-14 09:36:59 +00:00
log . WithFields ( log . Fields {
"headerIndex" : headerList . Len ( ) - 1 ,
"blockHeight" : bc . BlockHeight ( ) ,
"took" : time . Since ( start ) ,
} ) . Debug ( "done processing headers" )
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.
2018-03-17 11:53:21 +00:00
func ( bc * Blockchain ) processHeader ( h * 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
2018-03-09 15:55:25 +00:00
buf := new ( bytes . Buffer )
for int ( h . Index ) - headerBatchCount >= int ( bc . storedHeaderCount ) {
if err := headerList . Write ( buf , int ( bc . storedHeaderCount ) , headerBatchCount ) ; err != nil {
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 ( )
2018-02-06 06:43:32 +00:00
if err := h . EncodeBinary ( buf ) ; err != nil {
return err
2018-02-04 19:54:51 +00:00
}
2018-03-17 11:53:21 +00:00
key := storage . AppendPrefix ( storage . DataBlock , h . Hash ( ) . BytesReverse ( ) )
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
}
2018-03-21 16:11:04 +00:00
// TODO: persistBlock needs some more love, its implemented as in the original
// project. This for the sake of development speed and understanding of what
// is happening here, quite allot as you can see :). If things are wired together
// and all tests are in place, we can make a more optimized and cleaner implementation.
2018-03-09 15:55:25 +00:00
func ( bc * Blockchain ) persistBlock ( block * Block ) error {
2018-03-21 16:11:04 +00:00
var (
batch = bc . Batch ( )
unspentCoins = make ( UnspentCoins )
2018-04-16 20:15:30 +00:00
spentCoins = make ( SpentCoins )
2018-03-21 16:11:04 +00:00
accounts = make ( Accounts )
2018-04-16 20:15:30 +00:00
assets = make ( Assets )
2018-03-21 16:11:04 +00:00
)
2018-03-14 09:36:59 +00:00
2019-01-25 11:20:35 +00:00
if err := storeAsBlock ( batch , block , 0 ) ; err != nil {
return err
}
2018-03-17 11:53:21 +00:00
storeAsCurrentBlock ( batch , block )
2018-03-14 09:36:59 +00:00
2018-03-21 16:11:04 +00:00
for _ , tx := range block . Transactions {
2019-01-25 11:20:35 +00:00
if err := storeAsTransaction ( batch , tx , block . Index ) ; err != nil {
return err
}
2018-04-16 20:15:30 +00:00
unspentCoins [ tx . Hash ( ) ] = NewUnspentCoinState ( len ( tx . Outputs ) )
2018-03-21 16:11:04 +00:00
// Process TX outputs.
for _ , output := range tx . Outputs {
2018-04-16 20:15:30 +00:00
account , err := accounts . getAndUpdate ( bc . Store , output . ScriptHash )
2018-03-21 16:11:04 +00:00
if err != nil {
return err
}
if _ , ok := account . Balances [ output . AssetID ] ; ok {
account . Balances [ output . AssetID ] += output . Amount
} else {
account . Balances [ output . AssetID ] = output . Amount
}
}
// Process TX inputs that are grouped by previous hash.
for prevHash , inputs := range tx . GroupInputsByPrevHash ( ) {
2018-04-16 20:15:30 +00:00
prevTX , prevTXHeight , err := bc . GetTransaction ( prevHash )
2018-03-21 16:11:04 +00:00
if err != nil {
2018-03-25 10:45:54 +00:00
return fmt . Errorf ( "could not find previous TX: %s" , prevHash )
2018-03-21 16:11:04 +00:00
}
for _ , input := range inputs {
2018-04-16 20:15:30 +00:00
unspent , err := unspentCoins . getAndUpdate ( bc . Store , input . PrevHash )
2018-03-21 16:11:04 +00:00
if err != nil {
return err
}
unspent . states [ input . PrevIndex ] = CoinStateSpent
prevTXOutput := prevTX . Outputs [ input . PrevIndex ]
2018-04-16 20:15:30 +00:00
account , err := accounts . getAndUpdate ( bc . Store , prevTXOutput . ScriptHash )
2018-03-21 16:11:04 +00:00
if err != nil {
return err
}
2018-04-16 20:15:30 +00:00
if prevTXOutput . AssetID . Equals ( governingTokenTX ( ) . Hash ( ) ) {
spentCoin := NewSpentCoinState ( input . PrevHash , prevTXHeight )
spentCoin . items [ input . PrevIndex ] = block . Index
spentCoins [ input . PrevHash ] = spentCoin
}
2018-03-21 16:11:04 +00:00
account . Balances [ prevTXOutput . AssetID ] -= prevTXOutput . Amount
}
}
// Process the underlying type of the TX.
2018-04-16 20:15:30 +00:00
switch t := tx . Data . ( type ) {
2018-03-21 16:11:04 +00:00
case * transaction . RegisterTX :
2018-04-16 20:15:30 +00:00
assets [ tx . Hash ( ) ] = & AssetState {
ID : tx . Hash ( ) ,
AssetType : t . AssetType ,
Name : t . Name ,
Amount : t . Amount ,
Precision : t . Precision ,
Owner : t . Owner ,
Admin : t . Admin ,
}
2018-03-21 16:11:04 +00:00
case * transaction . IssueTX :
case * transaction . ClaimTX :
case * transaction . EnrollmentTX :
case * transaction . StateTX :
case * transaction . PublishTX :
2018-04-16 20:15:30 +00:00
contract := & ContractState {
Script : t . Script ,
ParamList : t . ParamList ,
ReturnType : t . ReturnType ,
HasStorage : t . NeedStorage ,
Name : t . Name ,
CodeVersion : t . CodeVersion ,
Author : t . Author ,
Email : t . Email ,
Description : t . Description ,
}
fmt . Printf ( "%+v" , contract )
2018-03-21 16:11:04 +00:00
case * transaction . InvocationTX :
}
}
// Persist all to storage.
if err := accounts . commit ( batch ) ; err != nil {
return err
}
if err := unspentCoins . commit ( batch ) ; err != nil {
return err
}
2018-04-16 20:15:30 +00:00
if err := spentCoins . commit ( batch ) ; err != nil {
return err
}
if err := assets . commit ( batch ) ; err != nil {
return err
}
2018-03-17 11:53:21 +00:00
if err := bc . PutBatch ( batch ) ; err != nil {
2018-03-14 09:36:59 +00:00
return err
}
2018-03-21 16:11:04 +00:00
2018-03-25 10:45:54 +00:00
atomic . StoreUint32 ( & bc . blockHeight , block . Index )
2018-03-09 15:55:25 +00:00
return nil
}
2019-02-19 11:48:48 +00:00
func ( bc * Blockchain ) persist ( ctx context . Context ) ( err error ) {
2018-03-09 15:55:25 +00:00
var (
2018-03-14 09:36:59 +00:00
start = time . Now ( )
2018-03-09 15:55:25 +00:00
persisted = 0
lenCache = bc . blockCache . Len ( )
)
2018-04-13 10:14:08 +00:00
if lenCache == 0 {
return nil
}
2018-03-14 09:36:59 +00:00
bc . headersOp <- func ( headerList * HeaderHashList ) {
for i := 0 ; i < lenCache ; i ++ {
if uint32 ( headerList . Len ( ) ) <= bc . BlockHeight ( ) {
return
}
2018-03-09 15:55:25 +00:00
hash := headerList . Get ( int ( bc . BlockHeight ( ) + 1 ) )
if block , ok := bc . blockCache . GetBlock ( hash ) ; ok {
if err = bc . persistBlock ( block ) ; err != nil {
2018-03-25 10:45:54 +00:00
log . Warnf ( "failed to persist blocks: %s" , err )
2018-03-09 15:55:25 +00:00
return
}
bc . blockCache . Delete ( hash )
persisted ++
}
}
2018-02-04 19:54:51 +00:00
}
2019-02-19 11:48:48 +00:00
select {
case <- ctx . Done ( ) :
return
case <- bc . headersOpDone :
//
}
2018-03-14 09:36:59 +00:00
if persisted > 0 {
log . WithFields ( log . Fields {
2018-04-09 16:58:09 +00:00
"persisted" : persisted ,
"headerHeight" : bc . HeaderHeight ( ) ,
"blockHeight" : bc . BlockHeight ( ) ,
"took" : time . Since ( start ) ,
2018-03-14 09:36:59 +00:00
} ) . Info ( "blockchain persist completed" )
}
return
}
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 ) {
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 tx , ok := bc . memPool . TryGetValue ( hash ) ; ok {
return tx , 0 , nil // the height is not actually defined for memPool transaction. Not sure if zero is a good number in this case.
}
2018-03-21 16:11:04 +00:00
key := storage . AppendPrefix ( storage . DataTransaction , hash . BytesReverse ( ) )
b , err := bc . Get ( key )
if err != nil {
return nil , 0 , err
}
r := bytes . NewReader ( b )
var height uint32
if err := binary . Read ( r , binary . LittleEndian , & height ) ; err != nil {
return nil , 0 , err
}
tx := & transaction . Transaction { }
if err := tx . DecodeBinary ( r ) ; err != nil {
return nil , 0 , err
}
return tx , height , nil
}
2018-03-14 09:36:59 +00:00
// GetBlock returns a Block by the given hash.
func ( bc * Blockchain ) GetBlock ( hash util . Uint256 ) ( * Block , error ) {
2018-03-17 11:53:21 +00:00
key := storage . AppendPrefix ( storage . DataBlock , hash . BytesReverse ( ) )
b , err := bc . Get ( key )
if err != nil {
return nil , err
}
block , err := NewBlockFromTrimmedBytes ( b )
if err != nil {
return nil , err
}
// TODO: persist TX first before we can handle this logic.
2019-01-25 11:20:35 +00:00
// if len(block.Transactions) == 0 {
// return nil, fmt.Errorf("block has no TX")
// }
2018-03-17 11:53:21 +00:00
return block , nil
}
2019-02-20 17:39:32 +00:00
func ( bc * Blockchain ) GetHeader ( hash util . Uint256 ) ( * Header , error ) {
2018-03-17 11:53:21 +00:00
b , err := bc . Get ( storage . AppendPrefix ( storage . DataBlock , hash . BytesReverse ( ) ) )
if err != nil {
return nil , err
}
block , err := NewBlockFromTrimmedBytes ( b )
if err != nil {
return nil , err
}
return block . Header ( ) , nil
2018-03-14 09:36:59 +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
// HasTransaction return 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 {
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 bc . memPool . ContainsKey ( hash ) {
return true
}
key := storage . AppendPrefix ( storage . DataTransaction , hash . BytesReverse ( ) )
if _ , err := bc . Get ( key ) ; err == nil {
return true
}
2018-03-14 09:36:59 +00:00
return false
}
// HasBlock return true if the blockchain contains the given
// 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
}
2018-03-14 09:36:59 +00:00
// GetHeaderHash return the hash from the headerList by its
// 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-02-08 08:04:38 +00:00
// GetAssetState returns asset state from its assetID
2018-11-26 21:12:33 +00:00
func ( bc * Blockchain ) GetAssetState ( assetID util . Uint256 ) * AssetState {
var as * AssetState
bc . Store . Seek ( storage . STAsset . Bytes ( ) , func ( k , v [ ] byte ) {
var a AssetState
2019-02-09 15:53:58 +00:00
if err := a . DecodeBinary ( bytes . NewReader ( v ) ) ; err == nil && a . ID == assetID {
2018-11-26 21:12:33 +00:00
as = & a
}
} )
return as
}
2019-02-08 08:04:38 +00:00
// GetAccountState returns the account state from its script hash
func ( bc * Blockchain ) GetAccountState ( scriptHash util . Uint160 ) * AccountState {
var as * AccountState
bc . Store . Seek ( storage . STAccount . Bytes ( ) , func ( k , v [ ] byte ) {
var a AccountState
if err := a . DecodeBinary ( bytes . NewReader ( v ) ) ; err == nil && a . ScriptHash == scriptHash {
as = & a
}
} )
return as
}
2019-02-20 17:39:32 +00:00
// GetConfig returns the config stored in the blockchain
func ( bc * Blockchain ) GetConfig ( ) config . ProtocolConfiguration {
return bc . config
}
// References returns a map with input prevHash as key (util.Uint256)
// and transaction output as value from a transaction t.
// @TODO: unfortunately we couldn't attach this method to the Transaction struct in the
// transaction package because of a import cycle problem. Perhaps we should think to re-design
// the code base to avoid this situation.
func ( bc * Blockchain ) References ( t * transaction . Transaction ) map [ util . Uint256 ] * transaction . Output {
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
references := make ( map [ util . Uint256 ] * transaction . Output , 0 )
2019-02-20 17:39:32 +00:00
for prevHash , inputs := range t . GroupInputsByPrevHash ( ) {
if tx , _ , err := bc . GetTransaction ( prevHash ) ; err != nil {
tx = nil
} else if tx != nil {
for _ , in := range inputs {
references [ in . PrevHash ] = tx . Outputs [ in . PrevIndex ]
}
} else {
references = nil
}
}
return references
}
// FeePerByte returns network fee divided by the size of the transaction
func ( bc * Blockchain ) FeePerByte ( t * transaction . Transaction ) util . Fixed8 {
return bc . NetworkFee ( t ) . Div ( int64 ( t . Size ( ) ) )
}
// NetworkFee returns network fee
func ( bc * Blockchain ) NetworkFee ( t * transaction . Transaction ) util . Fixed8 {
inputAmount := util . NewFixed8 ( 0 )
for _ , txOutput := range bc . References ( t ) {
if txOutput . AssetID == utilityTokenTX ( ) . Hash ( ) {
inputAmount . Add ( txOutput . Amount )
}
}
outputAmount := util . NewFixed8 ( 0 )
for _ , txOutput := range t . Outputs {
if txOutput . AssetID == utilityTokenTX ( ) . Hash ( ) {
outputAmount . Add ( txOutput . Amount )
}
}
return inputAmount . Sub ( outputAmount ) . Sub ( bc . SystemFee ( t ) )
}
// SystemFee returns system fee
func ( bc * Blockchain ) SystemFee ( t * transaction . Transaction ) util . Fixed8 {
return bc . GetConfig ( ) . SystemFee . TryGetValue ( t . Type )
}
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
// IsLowPriority flags a trnsaction as low priority if the network fee is less than
// LowPriorityThreshold
func ( bc * Blockchain ) IsLowPriority ( t * transaction . Transaction ) bool {
return bc . NetworkFee ( t ) < util . NewFixed8FromFloat ( bc . GetConfig ( ) . LowPriorityThreshold )
}
// GetMemPool returns the memory pool of the blockchain.
func ( bc * Blockchain ) GetMemPool ( ) MemPool {
return bc . memPool
}
// Verify verifies whether a transaction is bonafide or not.
// 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 ) Verify ( t * transaction . Transaction ) error {
if t . Size ( ) > transaction . MaxTransactionSize {
return errors . Errorf ( "invalid transaction size = %d. It shoud be less then MaxTransactionSize = %d" , t . Size ( ) , transaction . MaxTransactionSize )
}
if ok := bc . verifyInputs ( t ) ; ! ok {
return errors . New ( "invalid transaction's inputs" )
}
if ok := bc . memPool . Verify ( t ) ; ! ok {
return errors . New ( "invalid transaction due to conflicts with the memory pool" )
}
if IsDoubleSpend ( bc . Store , t ) {
return errors . New ( "invalid transaction caused by double spending" )
}
if ok := bc . verifyOutputs ( t ) ; ! ok {
return errors . New ( "invalid transaction's outputs" )
}
if ok := bc . verifyResults ( t ) ; ! ok {
return errors . New ( "invalid transaction's results" )
}
for _ , a := range t . Attributes {
if a . Usage == transaction . ECDH02 || a . Usage == transaction . ECDH03 {
return errors . Errorf ( "invalid attribute's usage = %s " , a . Usage )
}
}
return bc . VerifyWitnesses ( t )
}
func ( bc * Blockchain ) verifyInputs ( t * transaction . Transaction ) bool {
for i := 1 ; i < len ( t . Inputs ) ; i ++ {
for j := 0 ; j < i ; j ++ {
if t . Inputs [ i ] . PrevHash == t . Inputs [ j ] . PrevHash && t . Inputs [ i ] . PrevIndex == t . Inputs [ j ] . PrevIndex {
return false
}
}
}
return true
}
func ( bc * Blockchain ) verifyOutputs ( t * transaction . Transaction ) bool {
for assetID , outputs := range t . GroupOutputByAssetID ( ) {
assetState := bc . GetAssetState ( assetID )
if assetState == nil {
return false
}
if assetState . Expiration < bc . blockHeight + 1 && assetState . AssetType != transaction . GoverningToken && assetState . AssetType != transaction . UtilityToken {
return false
}
for _ , out := range outputs {
if int64 ( out . Amount ) % int64 ( math . Pow10 ( 8 - int ( assetState . Precision ) ) ) != 0 {
return false
}
}
}
return true
}
func ( bc * Blockchain ) verifyResults ( t * transaction . Transaction ) bool {
results := bc . GetTransationResults ( t )
if results == nil {
return false
}
var resultsDestroy [ ] * transaction . Result
var resultsIssue [ ] * transaction . Result
for _ , re := range results {
if re . Amount . GreaterThan ( util . Fixed8 ( 0 ) ) {
resultsDestroy = append ( resultsDestroy , re )
}
if re . Amount . LessThan ( util . Fixed8 ( 0 ) ) {
resultsIssue = append ( resultsIssue , re )
}
}
if len ( resultsDestroy ) > 1 {
return false
}
if len ( resultsDestroy ) == 1 && resultsDestroy [ 0 ] . AssetID != utilityTokenTX ( ) . Hash ( ) {
return false
}
if bc . SystemFee ( t ) . GreaterThan ( util . Fixed8 ( 0 ) ) && ( len ( resultsDestroy ) == 0 || resultsDestroy [ 0 ] . Amount . LessThan ( bc . SystemFee ( t ) ) ) {
return false
}
switch t . Type {
case transaction . MinerType , transaction . ClaimType :
for _ , r := range resultsIssue {
if r . AssetID != utilityTokenTX ( ) . Hash ( ) {
return false
}
}
break
case transaction . IssueType :
for _ , r := range resultsIssue {
if r . AssetID == utilityTokenTX ( ) . Hash ( ) {
return false
}
}
break
default :
if len ( resultsIssue ) > 0 {
return false
}
break
}
return true
}
// GetTransationResults returns the transaction results aggregate by assetID.
// Golang of GetTransationResults method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L207)
func ( bc * Blockchain ) GetTransationResults ( t * transaction . Transaction ) [ ] * transaction . Result {
var tempResults [ ] * transaction . Result
var results [ ] * transaction . Result
tempGroupResult := make ( map [ util . Uint256 ] util . Fixed8 )
if references := bc . References ( t ) ; references == nil {
return nil
} else {
for _ , output := range references {
tempResults = append ( tempResults , & transaction . Result {
AssetID : output . AssetID ,
Amount : output . Amount ,
} )
}
for _ , output := range t . Outputs {
tempResults = append ( tempResults , & transaction . Result {
AssetID : output . AssetID ,
Amount : - output . Amount ,
} )
}
for _ , r := range tempResults {
if amount , ok := tempGroupResult [ r . AssetID ] ; ok {
tempGroupResult [ r . AssetID ] = amount . Add ( r . Amount )
} else {
tempGroupResult [ r . AssetID ] = r . Amount
}
}
results = [ ] * transaction . Result { } // this assignment is necessary. (Most of the time amount == 0 and results is the empty slice.)
for assetID , amount := range tempGroupResult {
if amount != util . Fixed8 ( 0 ) {
results = append ( results , & transaction . Result {
AssetID : assetID ,
Amount : amount ,
} )
}
}
return results
}
}
// 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 ) {
references := bc . References ( t )
if references == nil {
return nil , errors . New ( "Invalid operation" )
}
hashes := make ( map [ util . Uint160 ] bool )
for _ , i := range t . Inputs {
h := references [ i . PrevHash ] . ScriptHash
if _ , ok := hashes [ h ] ; ! ok {
hashes [ h ] = true
}
}
for _ , a := range t . Attributes {
if a . Usage == transaction . Script {
h , err := util . Uint160DecodeBytes ( a . Data )
if err != nil {
return nil , err
}
if _ , ok := hashes [ h ] ; ! ok {
hashes [ h ] = true
}
}
}
for a , outputs := range t . GroupOutputByAssetID ( ) {
as := bc . GetAssetState ( a )
if as == nil {
return nil , errors . New ( "Invalid operation" )
}
if as . AssetType == transaction . DutyFlag {
for _ , o := range outputs {
h := o . ScriptHash
if _ , ok := hashes [ h ] ; ! ok {
hashes [ h ] = true
}
}
}
}
// convert hashes to []util.Uint160
hashesResult := make ( [ ] util . Uint160 , 0 , len ( hashes ) )
for h := range hashes {
hashesResult = append ( hashesResult , h )
}
return hashesResult , nil
}
// VerifyWitnesses verify the scripts (witnesses) that come with a transactions.
// Golang implementation of VerifyWitnesses method in C# (https://github.com/neo-project/neo/blob/master/neo/SmartContract/Helper.cs#L87).
// Unfortunately the IVerifiable interface could not be implemented because we can't move the References method in blockchain.go to the transaction.go file
func ( bc * Blockchain ) VerifyWitnesses ( t * transaction . Transaction ) error {
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 ) )
}
for i := 0 ; i < len ( hashes ) ; i ++ {
verification := witnesses [ i ] . VerificationScript
if len ( verification ) == 0 {
/ * TODO : replicate following C # code :
using ( ScriptBuilder sb = new ScriptBuilder ( ) )
{
sb . EmitAppCall ( hashes [ i ] . ToArray ( ) ) ;
verification = sb . ToArray ( ) ;
}
* /
} else {
if h , err := witnesses [ i ] . ScriptHash ( ) ; err != nil || hashes [ i ] != h {
return err
}
}
/ * TODO : replicate following C # code :
using ( ApplicationEngine engine = new ApplicationEngine ( TriggerType . Verification , verifiable , snapshot , Fixed8 . Zero ) )
{
engine . LoadScript ( verification ) ;
engine . LoadScript ( verifiable . Witnesses [ i ] . InvocationScript ) ;
if ( ! engine . Execute ( ) ) return false ;
if ( engine . ResultStack . Count != 1 || ! engine . ResultStack . Pop ( ) . GetBoolean ( ) ) return false ;
} * /
}
return nil
}
2018-02-06 06:43:32 +00:00
func hashAndIndexToBytes ( h util . Uint256 , index uint32 ) [ ] byte {
buf := make ( [ ] byte , 4 )
binary . LittleEndian . PutUint32 ( buf , index )
2018-03-02 15:24:09 +00:00
return append ( h . BytesReverse ( ) , buf ... )
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
}