Compare commits

...

1435 Commits

Author SHA1 Message Date
Dan Aharoni
8680231e5a [NOD-339] Remove cfilters and cfindex (#527)
* [NOD-339] Remove cfilters and cfindex

* [NOD-339] Remove some leftovers
2019-12-10 10:13:49 +02:00
stasatdaglabs
30f0e95969 [NOD-506] Remove blockNode.height and any references to it (#522)
* [NOD-506] Remove blockNode.height.

* [NOD-506] Use blueScore instead of chainHeight in validateParents.
2019-12-09 14:27:53 +02:00
Dan Aharoni
c94becf144 [NOD-498] Disable the option to choose mainnet and activeNet. (#525) 2019-12-08 18:33:04 +02:00
Svarog
369ec449a8 [NOD-509] Change organization name to kaspanet (#524)
* [NOD-509] Change organization name to kaspanet

* [NOD-509] Reorganize imports
2019-12-08 17:33:42 +02:00
Svarog
f4c6859e51 [NOD-509] Updated repository and imports to github.com/daglabs/kaspad (#521) 2019-12-08 16:28:53 +02:00
stasatdaglabs
683dd52fcf [NOD-492] Split API-Server to syncer and frontend applications (#519)
* [NOD-492] Split ApiServer to server and syncd.

* [NOD-492] Add missing file.

* [NOD-492] Remove references to --migrate from common config.

* [NOD-492] Move MQTT to the sync daemon.

* [NOD-492] Fix server Dockerfile and create one for syncd.

* [NOD-492] Rename ApiServer to Kasparov.

* [NOD-492] Fix packages.

* [NOD-492] Fix more packages.

* [NOD-492] Fix comments and package names.

* [NOD-492] Move blank import packages to main.

* [NOD-492] Move common logging logic out of individual config.go files.

* [NOD-492] Move database models to a package called dbmodels.

* [NOD-492] Rename models package to apimodels.
2019-12-08 14:38:47 +02:00
stasatdaglabs
11e936d109 [NOD-493] Make GetBlock return an appropriate error if the hash is known to be of an invalid block. (#520) 2019-12-08 14:05:56 +02:00
stasatdaglabs
9adb105e37 [NOD-487] Implement a mechanism to gracefully shut down after a panic (#512)
* [NOD-487] Implement a mechanism to gracefully shut down after a panic.

* [NOD-487] Fixed bad log.

* [NOD-487] Removed unused import.

* [NOD-487] Convert panic handlers from anonymous functions to methods.
2019-12-05 12:29:39 +02:00
stasatdaglabs
7b6ed9a778 [NOD-412] Remove run-dev.sh-related stuff. (#518) 2019-12-04 18:35:50 +02:00
Ori Newman
3218fc5a04 [NOD-488] Make getBlueBlocksBetween return error if startHash is not in selected parent chain of stopHash (#514)
* [NOD-488] Make getBlueBlocksBetween return error if start hash is not in the selected parent chain of stop hash

* [NOD-488] Convert for to while style
2019-12-04 17:52:28 +02:00
Ori Newman
3f94f8ca4c [NOD-491] Withhold blocks in mining simulator (#517) 2019-12-04 17:36:27 +02:00
Ori Newman
0842778c2c [NOD-485] Remove redundant argument from saveChangesFromBlock (#516) 2019-12-04 17:24:20 +02:00
Ori Newman
1332e1aa68 [NOD-479] Fix unorphaning in API server initial sync (#513) 2019-12-04 17:06:20 +02:00
Ori Newman
e872ebc7b3 [NOD-417] Remove mempool and alert messages (#515) 2019-12-04 10:43:50 +02:00
Svarog
e68b242243 [NOD-489] Don't skip notification about transactions for orphan/non-current blocks (#511) 2019-12-03 14:18:32 +02:00
Ori Newman
9cc2a7260b [NOD-479] Separate max outbound connections and max inbound connections (#509)
* [NOD-479] Separate max outbound connections and max inbound connections

* [NOD-479] Fix merge

* [NOD-479] Renames and add function countinboundPeers

* [NOD-479] Remove redundant check on maximum outbound peers

* [NOD-479] Rename countinboundPeers -> countInboundPeers
2019-12-03 12:27:49 +02:00
Ori Newman
bcd73012de [NOD-428] Require RPC user and password, and do not create a default config file for btcctl if rpc login details were provided (#510)
* [NOD-428] Required RPC user and password, and do not create a default config file for btcctl if rpc login details were provided

* [NOD-428] Don't check rpc user and password if rpc is disabled

* [NOD-428] Fix error message
2019-12-03 11:18:28 +02:00
Dan Aharoni
1fea2a9421 [NOD-486] API Server TX posting: Forward error when RPC Error is received (#507)
* [NOD-486] Forward error when RPC Error is recieved

* [NOD-486] Rename variable

* [NOD-486] Rename variable

* [NOD-486] Rename Variable (again)
2019-12-02 18:44:39 +02:00
stasatdaglabs
bb7d68deda [NOD-484] Fix deadlock between p2p server and sync manager during shutdown (#508)
* [NOD-484] Fix deadlock between p2p server and sync manager during shutdown.

* [NOD-484] Fix quitWaitGroup.Wait() potentially not waiting in some scenarios.

* [NOD-484] Add a comment explaining quitWaitGroup.

* [NOD-484] Fix typo.

* [NOD-484] Add etc to comment.
2019-12-02 18:08:32 +02:00
Ori Newman
3ab861227d [NOD-443] Fix API server unorphaning (#501)
* [NOD-443] Immediately request missing parents in API server sync

* [NOD-443] Add rpc client log to api server

* [NOD-443] Fix wrong ordering of pendingHeaders queue

* [NOD-443] Fix error comparision at TestNewHashFromStr

* [NOD-443] Make a separate handleMissingParentHeaders function

* [NOD-443] Change log level

* [NOD-443] Put handleMissingParentHeaders next to handleBlockHeader

* [NOD-443] Make handleBlockAddedMsg function

* [NOD-443] Make reusable missingParentsHashesStr string

* [NOD-443] Remove redundant 's'

* [NOD-443] Refactor to first get all blocks and then add them to database

* [NOD-443] Rename variables and functions, and remove redundant logs

* [NOD-443] Make fetchBlockAndMissingAncestors use block hash as an argument

* [NOD-443] Add log only for first orphan block

* [NOD-443] Fix wrong order of adding blocks to pendingBlocks

* [NOD-443] Write logs for all orphans

* [NOD-443] Log only missing parents that are not already fetched

* [NOD-443] Rename rawVerboseBlockTuple -> rawVerboseBlock

* [NOD-443] Make fetchBlock return *rawVerboseBlock

* [NOD-443] Rename rawVerboseBlock -> rawAndVerboseBlock
2019-12-02 13:29:28 +02:00
stasatdaglabs
8f0d98ef9b [NOD-464] Fix error messages in GetBlocks. (#506) 2019-12-02 13:05:56 +02:00
Svarog
dbd8bf3d2c [NOD-478] Add buffer to newOutboundConnection channel (#502) 2019-12-01 17:58:05 +02:00
Dan Aharoni
1b6b02e0d2 [NOD-475] Return error when requesting limit=0 (#505) 2019-12-01 17:25:27 +02:00
Ori Newman
2402bae1ff [NOD-410] Add log level CLI argument to API server (#503)
* [NOD-410] Add log level CLI argument to API server

* [NOD-410] Add comments

* [NOD-410] Remove pre-allocation of one item
2019-12-01 17:24:12 +02:00
Dan Aharoni
3dcf8d88b8 [NOD-481] Fix /blocks limit error message for api server (#504) 2019-12-01 16:19:14 +02:00
stasatdaglabs
dbf9c09a2e [NOD-461] Fix error code and message in GetTransactionsByAddressHandler. (#499) 2019-11-28 17:32:34 +02:00
stasatdaglabs
5e9fc2defc [NOD-464] Fix error messages in GetBlocks. (#500) 2019-11-28 17:31:19 +02:00
Ori Newman
bdc3cbceaa [NOD-472] Don't fetch accepting block and tx confirmations for getBlocks (#498)
* [NOD-472] Don't fetch accepting block and tx confirmations for getBlocks

* [NOD-472] Don't fetch accepting block and tx confirmations in any block verbose result

* [NOD-472] Add stringPointerToString function
2019-11-28 13:04:03 +02:00
stasatdaglabs
a71528fefb [NOD-450] Fix netsync clogging its own request queue with orphans that it had just now processed (#497)
* [NOD-450] Fix netsync clogging its own request queue with orphans that it had just now processed.

* [NOD-450] Rename hash to orphanHash.
2019-11-28 11:30:50 +02:00
Dan Aharoni
6725742d2c [NOD-470] Pass string instead of hash to controller (#496) 2019-11-27 17:08:23 +02:00
Dan Aharoni
9a510e2e23 [NOD-463] Change default order to descending (as appears in the spec) (#495) 2019-11-26 18:00:44 +02:00
Dan Aharoni
08a4b0dbf6 [NOD-426] Publish unaccepted transaction notifications (#490)
* [NOD-382] Add notification for accepted transactions

* [NOD-382] Remove print statement

* [NOD-426] Publish notifications for unaccepted transactions

* [NOD-426] Load DB in controller

* [NOD-426] Remove function name from error message

* [NOD-426] Add input addresses for transactions notifications

* [NOD-426] Remove function name from error message

* [NOD-426] Change method name to accepted transactions

* [NOD-426] Remove newlines

* [NOD-426] Use join instead of separate query

* [NOD-426] Remove new line
2019-11-26 16:59:16 +02:00
Ori Newman
0c9e55a358 [NOD-427] Selected tip notification (#494)
* [NOD-427] Send notifications to `dag/selected-tip`

* [NOD-442] Add selected tip notification

* [NOD-427] Add comment to PublishSelectedTipNotification

* [NOD-427] Remove redundant argument from errors.Wrapf

* [NOD-427] Add handleBlockAddedMsg function

* [NOD-427] Return errors instead of panicking

* [NOD-427] Fix findHashOfBluestBlock to use []string instead of dbmodels.Block

* [NOD-427] Add constants

* [NOD-427] use path.Join instead of topic+address

* [NOD-427] Remove redundant select

* [NOD-427] Change break to return

* [NOD-427] Fix findHashOfBluestBlock to handle empty blocks table

* [NOD-427] Return httpserverutils.HasDBError(dbErrors)
2019-11-26 16:46:12 +02:00
Ori Newman
532e57b61c [NOD-427] Add selected tip mqtt notification for the API server (#489)
* [NOD-427] Send notifications to `dag/selected-tip`

* [NOD-442] Add selected tip notification

* [NOD-427] Add comment to PublishSelectedTipNotification

* [NOD-427] Remove redundant argument from errors.Wrapf

* [NOD-427] Add handleBlockAddedMsg function

* [NOD-427] Return errors instead of panicking

* [NOD-427] Fix findHashOfBluestBlock to use []string instead of dbmodels.Block

* [NOD-427] Add constants

* [NOD-427] use path.Join instead of topic+address

* [NOD-427] Remove redundant select

* [NOD-427] Change break to return
2019-11-26 14:44:27 +02:00
Dan Aharoni
b1f59914d2 [NOD-466] Fix error where limit overides skip (#493) 2019-11-26 14:11:49 +02:00
Dan Aharoni
9a54b286c9 [NOD-462] Add error message when address is invalid for UTXO API request (#492)
* [NOD-462] Add error message when address is invalid

* [NOD-462] Fix error message

* [NOD-462] Remove function name from error message
2019-11-26 13:50:36 +02:00
Ori Newman
6e4b18a498 [NOD-442] Make dbFetchTxAcceptingBlock return nil if accepting bucket doesn't exist for transaction (#487) 2019-11-26 10:54:15 +02:00
Dan Aharoni
b5f8a0452e [NOD-460] Fix error where we set skip instead of limit (#491) 2019-11-26 10:36:26 +02:00
Dan Aharoni
fab043ef14 [NOD-382] Add notification for accepted transactions (#488)
* [NOD-382] Add notification for accepted transactions

* [NOD-382] Remove print statement
2019-11-25 10:09:27 +02:00
Ori Newman
8e0e62f21a [NOD-447] fix deadlocks and hanging goroutines (#481)
* [NOD-447] Fix deadlocks and hanging goroutines

* [NOD-447] Add tests

* [NOD-447] Add unpatch to spawnPatch

* [NOD-447] Don't send to releaseWait if waitingCounter is zero

* [NOD-447] Change waitingCounter to boolean and rename to isReleaseWaitWaiting, change checkIfRunningSpawnsAreLeft to return only one function, and lock critical code related to wg.isReleaseWaitWaiting

* [NOD-447] Rename txConfirmations -> txConfirmationsNoLock, txConfirmationsWithLock -> txConfirmations

* [NOD-447] Add documentation and delete redundant spawn

* [NOD-447] Fix comments

* [NOD-447] Fix comments
2019-11-24 15:59:45 +02:00
Dan Aharoni
9a1c2e2641 [NOD-457] Fix error message (#486) 2019-11-24 15:50:15 +02:00
Svarog
8cbc6670cc [NOD-445] Enable using EC2 AutoScalingGroup to get list of btcds for mining simulator (#484)
* [NOD-445] Added option to mining simulator to get address list from AWS

* [NOD-445] Add support to get miningsimulator addresslist from AWS

* [NOD-445] Added mechanism to update when new servers come online

* [NOD-445] Set config in connectionManager

* [NOD-445] Invert DisableTLS condition in readCert
2019-11-24 13:03:26 +02:00
Dan Aharoni
28ee6a8026 [NOD-381] Send transaction notifications to MQTT (#483)
* [NOD-381] Publish transaction messages to MQTT

* [NOD-381] Remove redundant variable

* [NOD-381] Send payload as string

* [NOD-381] Add Error handling

* [NOD-381] Respond with TransactionResponse

* [NOD-381] Use transactionResponse for notifications

* [NOD-381] Move code to appropriate places

* [NOD-381] Pass raw block instead of txId

* [NOD-381] Add comments to public functions

* [NOD-381] Remove print statement

* [NOD-381] Pass transaction instead of block; Use pointers so default will be nil;

* [NOD-381] Use pointers so value could be nil

* [NOD-381] Change variable name

* [NOD-381] Set QoS to 2

* [NOD-381] Move isConnected to MQTT, so client won't have to worry about it; General code refactors;
2019-11-24 10:53:09 +02:00
Svarog
af39e96e3e [NOD-455] Make GetFeeEstimateHandler return err, not HandlerError (#485) 2019-11-24 10:43:51 +02:00
stasatdaglabs
db6e9c773f [NOD-448] Fix initial sync in API Server crashing due to misaligned getBlocks calls (#482)
* [NOD-448] Change GetBlocksCmd to be able to include both raw and verbose block data.

* [NOD-448] Update sync logic to only make one getBlocks call per page.

* [NOD-448] Make GetBlocks get each block only once.
2019-11-21 12:21:19 +02:00
Dan Aharoni
47214121a7 [NOD-423] Implement get selected tip RPC command (#469)
* [NOD-423] Rename BestBlock to SelectedTip

* [NOD-423] Implement GetSelectedTip RPC command

* [NOD-423] Add help to getSelectedTip command

* [NOD-423] Fix getSelectedTip test

* [NOD-423] Fix tests so they would compile. These tests will need to be rewriten at some point.

* [NOD-423] Make integration test compile. Test need to be revisited

* [NOD-423] Rename variables

* [NOD-423] Change comment s about best block to selected tip.

* [NOD-423] Update comment

* [NOD-423] Change height to bluescore
2019-11-20 12:04:22 +02:00
stasatdaglabs
7b07609fd8 [NOD-437] Fix bad logger import in API Server. (#480) 2019-11-19 11:23:44 +02:00
stasatdaglabs
acb4b3f260 [NOD-434] Re-request missing parents when adding a block (#476)
* [NOD-434] Add the same enqueue/process mechanism as chainChangedMsgs for blockAddedMsgs.

* [NOD-434] Clean up after merge.

* [NOD-434] Implement mechanism for re-requesting missing parent blocks.

* [NOD-434] Fixed bad error message.

* [NOD-434] Split processBlockAddedMsgs.

* [NOD-434] Name return values in canHandleBlockAddedMsg.

* [NOD-434] Rename canHandleBlockAddedMsg to missingParentHashes and fix bad loop break.

* [NOD-434] Rename the variable missingParentHashes to missingHashes.

* [NOD-434] Rename a couple of variables.

* [NOD-434] Rename outerloop to outerLoop.

* [NOD-434] Fix typo and remove superfluous continue.

* [NOD-412] Change Warnf to Errorf where appropriate.
2019-11-19 11:22:17 +02:00
Ori Newman
e0221aa8ab [NOD-438] In api server, change block_data to MEDIUMBLOB (#474) 2019-11-19 10:42:57 +02:00
Ori Newman
cba346d753 [NOD-422] Separate request queue and request queue set by inv type (#471)
* [NOD-422] Separate request queue and request queue set by inv type

* [NOD-422] Make one-liner pop and remove redundant nil assignment
2019-11-18 15:34:01 +02:00
Ori Newman
0f34cfb1a2 [NOD-433] Make buildGetBlockVerboseResult use BlockConfirmationsByHashNoLock (#479) 2019-11-18 15:17:51 +02:00
stasatdaglabs
ea846a3284 [NOD-436] Remove unnecessary check before sending a chainChanged notification. (#478) 2019-11-18 14:49:27 +02:00
stasatdaglabs
63bfac9740 [NOD-436] Fix sending empty chainChanged messages. (#477) 2019-11-18 14:33:07 +02:00
Svarog
7284815c21 [NOD-435] Rollback transactions if they were not commited (#475) 2019-11-18 11:14:29 +02:00
Svarog
80307d108b [NOD-430] Print hashes of missing parents in case can't insert block into DB of API-Server (#473)
* [NOD-430] Print hashes of missing parents in case can't insert block into DB of API-Server

* [NOD-430] Use continue OUTER_LOOP instead of break

* [NOD-430] Use lowerCamelCase for label
2019-11-18 10:03:17 +02:00
stasatdaglabs
722437afe9 [NOD-424] Fix API Server not syncing new blocks (#470)
* [NOD-424] Fix typo in SQL query.

* [NOD-424] Rewrite handling chainChangedMsgs.

* [NOD-424] Separated enqueueChainChangedMsg and processChainChangedMsgs.

* [NOD-424] Fix a typo.
2019-11-17 16:52:45 +02:00
Ori Newman
684cf4b5fa [NOD-406] Don't do ECMH operations on mempool (#467)
* [NOD-406] Don't do ECMH operations on mempool

* [NOD-406] Change NewUTXODiff(false) to NewUTXODiffWithoutMultiset

* [NOD-406] Rename dClone -> clone

* [NOD-406] Remove redudnant assignment

* [NOD-406] Remove dag.UTXOToECMHCacheLock and make NewBlockTemplate use dag's write lock

* [NOD-406] Add tests to UTXO diffs without multiset
2019-11-14 17:40:05 +02:00
Ori Newman
c95a7b13a6 [NOD-379] Make a separate limit for block invs in getdata message (#465) 2019-11-14 13:45:24 +02:00
stasatdaglabs
1ce7f21026 [NOD-380] Implement MQTT client in api-server (#468)
* [NOD-380] Add MQTT to the project.

* [NOD-380] Add MQTT params to config.

* [NOD-380] Implement connecting to an mqtt broker.

* [NOD-380] Fix a comment.

* [NOD-380] Removed unnecessary option.

* [NOD-380] Added comments to MQTT functions.

* [NOD-380] Fix copy+paste error.

* [NOD-380] Make it so that all the mqtt flags must be passed together.

* [NOD-380] Use activeConfig instead of passing it everywhere.
2019-11-14 10:44:45 +02:00
Svarog
7d7df10493 [NOD-418] Added IsSpendable field to /utxos/ queries in API server (#466) 2019-11-13 16:25:10 +02:00
Dan Aharoni
8179862e0b [NOD-421] Fix DNSSeeder parsing error caused by NOD-386 (#464) 2019-11-13 12:49:58 +02:00
stasatdaglabs
6828f623b4 [NOD-395] Fix a crash in diffFromAcceptanceData caused by wrong order of iteration over blocks (463)
* [NOD-395] Write a test for the diffFromAcceptanceData crash.

* [NOD-395] Converted MultiBlockTxsAcceptanceData into a slice.

* [NOD-395] Fix failing test.

* [NOD-395] Populate multiBlockTxsAcceptanceData bottom-to-top.

* [NOD-395] Add comment to FindAcceptanceData.

* [NOD-395] Remove no-longer relevant note about probability in TestOrderInDiffFromAcceptanceData.
2019-11-13 12:28:52 +02:00
stasatdaglabs
2c88a5b2fe [NOD-413] Make "Max failed connection attempts reached" logs less frequent (#458)
* [NOD-413] Make "Max failed connection attempts reached" less frequent

* [NOD-413] Throttle only certain types of logs.

* [NOD-413] Add a comment for shouldWriteConnFailedLog.

* [NOD-413] Fix lint error.

* [NOD-413] Make ErrNoAddress a special type to support error wrapping.

* [NOD-413] Make throttledConnFailedLogInterval 10 minutes.

* [NOD-413] Move p2p errors into variables.

* [NOD-413] Reorganize throttled stuff to be next to each other.
2019-11-13 11:25:39 +02:00
Ori Newman
a7f08598f3 [NOD-416] Use errors.Is and add goroutine stack trace to HandlePanic (#459)
* [NOD-416] Use errors.Is and add goroutine stack trace to HandlePanic

* [NOD-416] Don't print goroutineStackTrace if it's nil
2019-11-13 11:20:20 +02:00
Dan Aharoni
83bad65d3a [NOD-419] Btcctl parsing error (introduced by NOD-386) (#462) 2019-11-12 14:25:28 +02:00
Dan Aharoni
1f35378a4d [NOD-386] Extract net parsing functionality to a shared place. (#453)
* [NOD-386] Extract net parsing functionality to a shared place.

* [NOD-386] Add extract ActiveNetParams to cmdconfig

* [NOD-386] Adding comments so go-vet won't shout at me

* [NOD-386] Rename package name to config

* [NOD-386] Rename commandConfig to configFlags

* [NOD-386] Rename function to ResolveNetwork

* [NOD-386] Fix renaming errors

* [NOD-386] Refactor network config to btcd level so APIserver and btcd could use it

* [NOD-386] Refactor network config to config package

* [NOD-386] Move ActiveNetParams to network section

* [NOD-386] Explictly return nil

* [NOD-386] Reuse activeNetParams from netwrok config

* [NOD-386] Set ActiveNetworkFlags instance to be global

* [NOD-386] Remove redundant newline

* [NOD-386] Init ActiveNetParams in address manager test

* [NOD-386] Add dnsseeder network config

* [NOD-386] Use ActiveConfig() method to access configuration
2019-11-12 10:51:36 +02:00
Dan Aharoni
39eab7a6d5 [NOD-373] Schnorr signature scheme (#451)
* [NOD-373] Implement Schnorr digital signatures and remove ECDSA (based on code from gcash/bchd)

* [NOD-374] Add new error to list; Update comments.

* [NOD-373] Remove leftovers of verifyMessage RPC command (which was deleted)

* [NOD-373] Remove redundant test, add Schnorr tests, and fix tests where needed

* [NOD-373] Fix tests and remove redundant ones

* [NOD-373] Refactor functions names

* [NOD-373] Remove empty line

* [NOD-373] Fix comments, rename functions to more meaningful names

* [NOD-373] Additional data in nonceRFC6979 should not be nil

* [NOD-373] Refactor function name

* [NOD-373] Add permalinks for links to bchd code
2019-11-12 10:09:38 +02:00
Dan Aharoni
9dd025d4da [NOD-408] Remove unimplemented redundant RPC command GetTxOutProof (#461) 2019-11-11 12:53:56 +02:00
Dan Aharoni
bb75ea5020 [NOD-414] Remove AES encryption/decryption from btcd (#460) 2019-11-11 11:01:02 +02:00
stasatdaglabs
8dbd4a2bed [NOD-411] Fix underflowing check for resending transactions. (#457) 2019-11-07 16:08:40 +02:00
Ori Newman
24305cda68 [NOD-385] Change confirmation calculation to be relative to the selected tip (#455)
* [NOD-385] Make confirmations be calculated as dag.selectedTip().blueScore - acceptingBlock.blueScore + 2

* [NOD-385] Fix comments

* [NOD-385] Make more explicit check in accepting block for selected tip

* [NOD-385] Put only non accepted transactions in areTxsInBlock

* [NOD-385] fetchSelectedTip only if needed
2019-11-07 13:42:25 +02:00
Ori Newman
770dfd147d [NOD-404] Calculate mass in API server (#452)
* [NOD-404] Calculate mass in API server

* [NOD-404] Fix uninitialized maps

* [NOD-404] Use txID instead of prevDBTransactionsOutput.Transaction.TransactionID
2019-11-07 10:27:12 +02:00
Ori Newman
a9ff9b0e70 [NOD-398] Change API server type HandlerError to work with errors instead of error strings (#454)
* [NOD-398] Change API server type HandlerError to work with errors instead of error strings

* [NOD-398] Rename OriginalError -> Cause and isHandleError -> ok
2019-11-06 16:58:58 +02:00
Ori Newman
3cc6f2d648 [NOD-384] Remove mass from rpc results (#449)
* [NOD-384] Remove mass from rpc results

* [NOD-384] Fix tests
2019-11-04 18:06:01 +02:00
stasatdaglabs
a8f0d7b05b [NOD-400] Fix ECHM cache crashing on concurrent access. (#450) 2019-11-04 17:38:01 +02:00
stasatdaglabs
13f06ca293 [NOD-399] Fix TxGen resending coinbase transactions. (#448) 2019-11-04 13:04:06 +02:00
Ori Newman
c88fa1492e [NOD-375] Move to pkg/errors (#447)
* [NOD-375] Move to pkg/errors

* [NOD-375] Fix tests

* [NOD-375] Make AreErrorsEqual a shared function
2019-11-04 11:24:12 +02:00
Ori Newman
40657a83f5 [NOD-344] Cache ECMH (#445)
* [NOD-134] Change newConnMtx to newConnReqMtx

* [NOD-344] Change ECMH cache size to 4e6

* [NOD-344] Refactor

* [NOD-344] Fix go.mod
2019-11-03 12:29:55 +02:00
Dan Aharoni
44dd58b461 [NOD-396] Fix updateAddedChainBlocks query to select all transactions and not the first one (#444) 2019-11-03 11:29:51 +02:00
Svarog
47891b17ab [NOD-392] If transaction is in mempool - don't try to get number of confirmations (#443) 2019-11-03 11:27:46 +02:00
Ori Newman
f7fbfbf5c4 [NOD-383] Fix updateAddedChainBlocks and updateRemovedChainHashes to update IsChainBlock and AcceptingBlockID appropriately 2019-10-31 15:47:30 +02:00
Ori Newman
0e278ca22b [NOD-350] Implement testnet faucet (#438)
* [NOD-350] Implement testnet faucet

* [NOD-350] Add JSON annotations to api server response types

* [NOD-350] Fix IP check query, update IP usage with upsert, and make IP a primary key

* [NOD-377] Remove redundant float conversion

* [NOD-377] Change not current database error message

* [NOD-377] change API route from /money_request to /request_money

* [NOD-377] Add a constant for 24 hours

* [NOD-377] Remove redundant call for getWalletUTXOSet()

* [NOD-377] Condition refactoring

* [NOD-377] Fix POST request to API server content type

* [NOD-350] Rename day -> timeBetweenRequests

* [NOD-377] Rename timeBetweenRequests -> minRequestInterval, timeBefore24Hours -> minRequestInterval

* [NOD-350] Rename file responsetypes -> response_types

* [NOD-350] Rename convertTxModelToTxResponse -> convertTxDBModelToTxResponse

* [NOD-350] Explicitly select blue_score in fetchSelectedTipBlueScore

* [NOD-350] Refactor and add comments

* [NOD-350] Make calcFee use MassPerTxByte

* [NOD-350] Convert IP column to varchar(39) to allow ipv6 addresses

* [NOD-350] Add comments to isFundedAndIsChangeOutputRequired

* [NOD-350] Remove approximateConfirmationsForCoinbaseMaturity

* [NOD-350] Fix comments
2019-10-31 11:59:56 +02:00
Ori Newman
c66fb294c8 [NOD-377] Don't disconnect from peers with finalized rendezvous point. Instead remove them from sync candidates. (#439) 2019-10-30 17:18:46 +02:00
stasatdaglabs
88b7e7ca03 [NOD-394] Add --cleanup to ./run-dev.sh (#441)
* [NOD-394] Rename --only-build to --no-run.

* [NOD-394] Allow --rm and --no-build to be run together with no-run.

* [NOD-394] Make --cleanup alias for --rm --no-run --no-build.

* [NOD-394] Fix typo in usage string.

* [NOD-394] Set docker/docker-compose.yaml to use devnet instead of testnet.
2019-10-30 17:12:48 +02:00
stasatdaglabs
a9b659a36f [NOD-393] Force docker-compose to actually cleanup before it runs when running ./run-dev.sh --rm. (#440) 2019-10-30 14:43:23 +02:00
stasatdaglabs
90fc6ba3e7 [NOD-376] Fix invalid orphan blocks causing their valid parents to be rejected (#436)
* [NOD-376] Made bad unorphaned blocks not reject the original block.

* [NOD-376] Fix wording in a comment.

* [NOD-376] Add a test to make sure that bad child blocks don't invalidate valid parent blocks.

* [NOD-376] Clarify comments and don't check PoW for child block (it's irrelevant for this test case).
2019-10-29 15:54:59 +02:00
stasatdaglabs
8ea97aa3fd [NOD-356] Add indication in getBlock that a block is an orphan. (#437) 2019-10-29 15:53:56 +02:00
Dan Aharoni
7c9f5a65d8 [NOD-374] Create transaction signer tool (#435)
* [NOD-374 ] Create transaction signer tool

* [NOD-374] Rename variables

* [NOD-374] Add network selection; Move error handling to control-flow function

* [NOD-374] Rename variables

* [NOD-374] Add new line after if blocks

* [NOD-374] Fix formatting error (gofmt)
2019-10-29 15:00:03 +02:00
stasatdaglabs
e2d3c4c821 [NOD-378] Add --no-build and --only-build to run-dev.sh. (#434) 2019-10-28 14:57:14 +02:00
Dan Aharoni
92578e2853 [NOD-368] correct text output of address generator (#433)
* [NOD-368] correct text output of address generator (address instead of public key)

* [NOD-368] add hash160 to output of genaddr

* [NOD-368] change encoding to hexadecimal

* [NOD-368] fix formatting
2019-10-24 18:00:06 +03:00
Dan Aharoni
3018c18616 [NOD-362] Calculate txgen fees by mass instead of size (#431)
* [NOD-362] Calculate txgen fee by mass

* formating

* [NOD-362] add varint size to calculation

* [NOD-362] rename variables

* [NOD-362] formating
2019-10-22 18:08:19 +03:00
Ori Newman
3ac9fa83c1 [NOD-367] Propagate transactions every 100 ms (#432) 2019-10-22 15:17:18 +03:00
Dan Aharoni
c5b0398dac [NOD-357] change finality interval (#430)
* change finality interval to 1000 ( ~16.6 minutes interval)

* [NOD-357] define finality interval in dagParams instead of using a constant.

* use dagParams for FinalityInterval instead of constant

* override parameter so test would pass on CI (Jenkins machine runs out of memory if we use 1000)

* formating the code
2019-10-16 15:32:10 +03:00
Ori Newman
76f23d8a9b [NOD-359] Calculate Transaction mass from previous scriptPubKeys (#429)
* [NOD-359] Calculate Transaction mass from previous scriptPubKeys

* [NOD-359] Add missing block errors
2019-10-15 13:03:16 +03:00
stasatdaglabs
089cee0e1d [NOD-352] Create a Dockerfile for APIServer (#428)
* [NOD-352] Created a Dockerfile for APIServer.

* [NOD-352] Removed unnecessary testing stuff from the APIServer and DNSSeeder Dockerfiles.
2019-10-13 14:07:44 +03:00
stasatdaglabs
982340456d [NOD-361] Fixed Nil dereference in connmgr/seed.go. (#427) 2019-10-08 13:04:07 +03:00
stasatdaglabs
13cf1f7715 [NOD-360] Renamed TestNet3 to TestNet. (#426) 2019-10-08 12:59:54 +03:00
stasatdaglabs
d99af7424c [NOD-353] Fixed passing wrong argument in handleGetRawTransaction. (#424)
* [NOD-353] Fixed passing wrong argument in handleGetRawTransaction.

* [NOD-353] Renamed mtx to msgTx.
2019-10-07 16:14:53 +03:00
Dan Aharoni
40ad9c5d2b [NOD-355] remove 'flushDbCache' and 'getBlockHash' rpc commands (#425)
* [NOD-355] remove 'flushDbCache' and 'getBlockHash' rpc commands

* [NOD-355] remove 'flushDbCache' and 'getBlockHash' from rpc server help
2019-10-07 14:07:20 +03:00
Ori Newman
9dfc3091b4 [NOD-134] Don't connect to an address from the same 16 CIDR (#423)
* [NOD-134] Don't connect to an address from the same 16 CIDR

* [NOD-134] Rename outboundPeerConnected variables

* [NOD-134] Change newConnMtx to newConnReqMtx
2019-10-06 15:53:58 +03:00
Ori Newman
e6a4ed04f3 [NOD-338] recover indexer if didnt work for a while (#422)
* [NOD-338] Recover indexer if it didn't work for a while

* [NOD-338] Recover indexer if it didn't work for a while

* [NOD-338] Recover indexer if it didn't work for a while

* [NOD-338] Add tests and move blockidhash.go to blockdag package

* [NOD-338] Delete index current block id when dropping index, and do some refactoring

* [NOD-338] Change comments

* [NOD-338] Change recover error messages

* [NOD-338] Fix comments

* [NOD-338] Fix comments and fix test name
2019-09-26 18:19:58 +03:00
stasatdaglabs
e3aa8d65dc [NOD-337] In CheckTransactionSanity make max mass of transaction to be half of block max mass (#421)
* [NOD-337] In CheckTransactionSanity, made max mass of transaction to be half of block max mass.

* [NOD-337] Added a comment for MaxMassPerTx.

* [NOD-337] Fixed a couple of comments.
2019-09-24 14:27:04 +03:00
stasatdaglabs
ece0fb83e8 [NOD-342] Renamed CHAN to BDAG. (#420) 2019-09-24 11:42:52 +03:00
stasatdaglabs
683830d574 [NOD-341] Made ChainChanged not fire if the acceptance index is off. (#419) 2019-09-22 17:59:20 +03:00
stasatdaglabs
c5108a4abd [NOD-276] Extracted p2p onXXX methods to separate files. (#418) 2019-09-22 17:40:10 +03:00
stasatdaglabs
40342eb45a [NOD-275] Split rpcserver.go to separate files (#417)
* [NOD-275] Moved getBlockTemplate and related functionality to a separate file.

* [NOD-275] Started moving handlers to separate files.

* [NOD-275] Fixed merge errors.

* [NOD-275] Moved all handlers out of rpcserver.go.

* [NOD-275] Moved non-shared functions out of rpcserver.go.

* [NOD-275] Moved handleGetAllManualNodesInfo to a separate file.

* [NOD-275] Moved handlers out of rpcwebsocket.go to separate files.

* [NOD-275] Fixed import error.

* [NOD-275] Renamed all handler files to include underscores.

* [NOD-275] Moved common rpc helper functions to common.go.
2019-09-22 16:41:37 +03:00
stasatdaglabs
adf4b4380e [NOD-289] Implement API-Server bootstrapping and booting after downtime (#408)
* [NOD-289] Implemented database isCurrent checking and connection.

* [NOD-289] Added GetChainFromBlock to RPCClient.

* [NOD-289] Limited the amount of blocks in GetChainFromBlockResponse.

* [NOD-289] Fixed various issues that were keeping GetChainFromBlocks from working properly.

* [NOD-289] Created blockloop.go.

* [NOD-289] Updated go.mod after merge.

* [NOD-289] Implemented collection of current selected parent chain.

* [NOD-289] Fixed test. Reverted not deleting utxoDiffData from the DB.

* [NOD-289] Implemented GetBlocks.

* [NOD-289] Added comment to BlockHashesFrom.

* [NOD-289] Added GetBlocks to rpcclient.

* [NOD-289] Added verboseBlocks to GetBlocks.

* [NOD-289] Implemented block insertion.

* [NOD-289] Added AUTO_INCREMENT to tables that were missing it.

* [NOD-289] Made gasLimit in subnetwork nullable.

* [NOD-289] Renamed transactions_outputs to transaction_outputs.

* [NOD-289] Fixed weird coinbase behavior in vin.

* [NOD-289] Made collectCurrentBlocks start from the most recent startHash.

* [NOD-289] Added IsChainBlock to GetBlockVerboseResult.

* [NOD-289] Implemented adding a block from onBlockAdded.

* [NOD-289] Added removedParentChainHashes to getChainFromBlock.

* [NOD-289] Implemented updating the selected parent chain from onChainChanged.

* [NOD-289] Implemented some initial logic for updating the UTXO.

* [NOD-289] Fixed merge errors.

* [NOD-326] Fixed some more merge errors.

* [NOD-289] Added error handling for missing required records.

* [NOD-289] Implemented handling removedChainHashes.

* [NOD-289] Implemented handling addedChainBlocks.

* [NOD-289] Fixed incorrect coinbase check.

* [NOD-289] Implemented inserting the transaction output address.

* [NOD-289] Added updating block.IsChainBlock.

* [NOD-289] Split insertBlock into many small functions.

* [NOD-289] Split updateSelectedParentChain into smaller functions.

* [NOD-289] Fixed pointer errors.

* [NOD-289] Fixed a bad exists check.

* [NOD-289] Fixed a couple of small bugs.

* [NOD-289] Fixed a TxID/Hash mixup.

* [NOD-289] Added block/tx mass to getBlockVerboseResponse.

* [NOD-289] Renamed blockLoop.go to sync.go. Added comments.

* [NOD-289] Deleted apiserver README.

* [NOD-289] Fixed golint errors.

* [NOD-289] Renamed findMostRecentBlockHash to findHashOfBluestBlock.

* [NOD-289] Fixed style in syncBlocks and fixed a comment.

* [NOD-289] Copied NewErrorFromDBErrors over from NOD-324.

* [NOD-289] Created a couple of utils to make error handling with gorm slightly less painful.

* [NOD-289] Added error handling for database calls.

* [NOD-289] Fixed some more style/comments.

* [NOD-289] Fixed comments.

* [NOD-289] Renamed TransactionInput.TransactionOutput to TransactionInput.PreviousTransactionOutput.

* [NOD-289] Added a commends about pagination in getBlocks and getChainFromBlock.

* [NOD-289] Removed the coinbase field from Vin.

* [NOD-289] Deferred handling chainChangedMsgs until we have the appropriate data.

* [NOD-289] Optimized queries in updateRemovedChainHashes and updateAddedChainBlocks.

* [NOD-289] Optimized queries in insertBlockParents.

* [NOD-289] Optimized queries in insertTransactionInput.

* [NOD-289] Split Where calls to separate lines.

* [NOD-289] Fixed merge errors.

* [NOD-289] Exited early from insertBlockParents if we're the genesis block.

* [NOD-289] Improved nextChainChangedChan mechanism.

* [NOD-289] Fixed the above sync mechanism a bit.

* [NOD-289] Renamed IsDBRecordNotFoundError to HasDBRecordNotFoundError and IsDBError to HasDBError.

* [NOD-289] Replaced old error handling for db errors with the lovely new stuff.

* [NOD-289] Exited early if we already inserted a block. This saves us checking if a record already exists for some record types.

* [NOD-289] Decoupled syncBlocks from syncSelectedParentChain.

* [NOD-289] Made a comment more explicit.

* [NOD-289] Extracted net resolution to a separate function.

* [NOD-289] Extracted syncing to a separate function.

* [NOD-289] Fixed a comment.

* [NOD-289] Fixed merge erros.

* [NOD-289] Fixed a couple of bugs.

* [NOD-289] Fixed another bug.

* [NOD-289] Extracted ChainChangedMsg conversion to a separate function.

* [NOD-289] Optimized queries in canHandleChainChangedMsg.

* [NOD-289] Moved the sync function closer to its call site.

* [NOD-289] Renamed HasDBRecordNotFoundError to IsDBRecordNotFoundError.

* [NOD-289] Used count instead of first.

* [NOD-289] Renamed address to hexAddress.
2019-09-22 13:14:51 +03:00
Ori Newman
7371120481 [NOD-333] Make ExtractScriptPubKeyAddrs return single address (#415)
* [NOD-333] Make ExtractScriptPubKeyAddrs return single address

* [NOD-333] Remove reference to required signatures from ExtractScriptPubKeyAddrs
2019-09-19 11:19:51 +03:00
stasatdaglabs
1064b5009d [NOD-315] Implement acceptance index (#413)
* [NOD-315] Created acceptanceindex.go including boilerplate.

* [NOD-315] Disallowed calls to notifyChainChanges and getChainFromBlock if the acceptance index is not on.

* [NOD-315] Implemented the acceptance index.

* [NOD-315] Fixed serialization/deserialization. Added test.

* [NOD-315] Fixed/added comments.

* [NOD-315] Fixed copy/paste errors.

* [NOD-315] Added an empty line for readability.
2019-09-19 10:38:33 +03:00
stasatdaglabs
850876e6a7 [NOD-335] Don't print stack-trace when cli flags are invalid (#416)
* [NOD-335] Made it not write a stack trace if the command line flags are wrong.

* [NOD-335] Fixed panic not printing the right error.

* [NOD-335] Removed code duplication.
2019-09-18 17:22:32 +03:00
Svarog
d4083cbdbe [NOD-309] post transaction (#403)
* [NOD-319] Add query params to api server route handler

* Temp commit

* [NOD-322] Make database.DB a function

* [NOD-322] Move context to be the first parameter in all functions

* [NOD-322] Set db to nil on database.Close()

* [NOD-322] Tidy go.mod/go.sum

* [NOD-323] Move rpc-client to separate package

* [NOD-309] Add controller for POST /transaction

* [NOD-309] Added route for POST /transaction

* [NOD-309] in POST /transaction: Forward reject errors to client

* [NOD-309] Added custom client messages to errors in POST /transaction

* [NOD-309] Use utils.NewInternalServerHandlerError where appropriate
2019-09-18 16:09:48 +03:00
Ori Newman
47c5eddf38 [NOD-329] Separate connect timeout and request timeout to JSON-RPC server (#411) 2019-09-18 15:01:31 +03:00
Ori Newman
f6a6508eff [NOD-328] Make API server mainHandler return an object (#412) 2019-09-18 14:47:59 +03:00
Ori Newman
a036618b44 [NOD-324] Properly handle GORM errors in API server (#409)
* [NOD-324] Properly handle GORM errors in API server

* [NOD-324] Handle RecordNotFound error in GetBlockByHashHandler

* [NOD-324] Make a separate function for NewErrorFromDBErrors
2019-09-18 14:09:07 +03:00
Ori Newman
2429b623fc [NOD-327] Add --migrate cli flag to API server (#407)
* [NOD-327] Add --migrate cli flag to API server

* [NOD-327] Change log messages

* [NOD-327] Remove `required` flag from API server RPC CLI arguments

* [NOD-327] Add database version in migrations logs
2019-09-18 13:51:20 +03:00
Ori Newman
f4850b9e7a [NOD-330] Use BTCD logs for gorm (#410) 2019-09-18 11:47:54 +03:00
Ori Newman
e81ac5f19e [NOD-307] Implement get blocks for api server (#405)
* [NOD-307] Implement API-Server GET /blocks

* [NOD-307] Implement API-Server GET /blocks

* [NOD-307] Add comments to exported constants

* [NOD-307] Flatten GET query values and check that 'order' value is valid

* [NOD-307] Validate order values in GetBlocksHandler

* [NOD-307] Add convertQueryParamToInt function
2019-09-16 16:53:57 +03:00
Ori Newman
31ccedf136 [NOD-325] Enable separate error messages for logging and client (#406)
* [NOD-325] Enable separate error messages for logging and client

* [NOD-325] Add json annotation to clientError
2019-09-16 13:26:05 +03:00
Svarog
502b510ccd [NOD-322] Minor api-server refactoring. (#401)
* [NOD-322] Make database.DB a function

* [NOD-322] Move context to be the first parameter in all functions

* [NOD-322] Set db to nil on database.Close()

* [NOD-322] Tidy go.mod/go.sum

* [NOD-322] Use http package const + message for StatusInternalServerError
2019-09-15 12:32:12 +03:00
stasatdaglabs
369031f963 [NOD-326] Replaced the UTXOs table with TransactionOutputs.isSpent (#404)
* [NOD-326] Replaced UTXO table with TransactionOutput.IsSpent.

* [NOD-326] Fixed merge errors.
2019-09-15 12:04:51 +03:00
Ori Newman
a789680db1 [NOD-314] change pkscript to scriptpubkey (#400)
* [NOD-314] Change everywhere PkScript to ScriptPubKey

* [NOD-314] Change everywhere PkScript to ScriptPubKey

* [NOD-314] Rename pkPops -> scriptPubKeyPops
2019-09-15 11:09:36 +03:00
Svarog
90bda69931 [NOD-323] Move rpc-client to separate package (#402) 2019-09-15 10:21:42 +03:00
Svarog
9647cb3e08 [NOD-318] Upgrade everything to Go1.13 (#393) 2019-09-09 15:57:31 +03:00
Ori Newman
79c9060909 [NOD-308] Implement API-Server GET /fee-estimates (#399) 2019-09-09 11:21:56 +03:00
Svarog
20206789e0 [NOD-299] Add waitgroup to wait for all spawns to complete before calling teardown (#385)
* [NOD-299] Add waitgroup to wait for all `spawn`s to complete before calling teardown

* [NOD-299] Restore spawn on teardown + mark spawn done in the correct thread
2019-09-09 11:02:31 +03:00
Ori Newman
1ddae35277 [NOD-305] Implement API-Server GET /utxos/{address} (#398)
* [NOD-305] Implement API-Server GET /utxos/{address}

* [NOD-305] Add accepting block blue score to the resulted utxo
2019-09-08 16:58:28 +03:00
Ori Newman
75a8c6459a [NOD-306] Implement API-Server GET /block/{hash} (#397)
* [NOD-306] Implement API-Server GET /block/{hash}

* [NOD-306] Validate that hash string is a valid hex

* [NOD-306] Unite invalid hash errors
2019-09-08 16:09:09 +03:00
Ori Newman
7fc2430ab1 [NOD-304] Implement get transactions by address for api server (#395)
* [NOD-303] Implement get transactions by address for API server

* [NOD-304] Implement get transactions by address

* [NOD-304] Implement get transactions by address

* [NOD-304] Use structs for where if possible

* [NOD-304] Auto increment IDs

* [NOD-304] Make defaultGetTransactionsLimit constant

* [NOD-304] Delete db directory

* [NOD-304] change db var to query

* [NOD-304] Extract route handle function from addRoutes

* [NOD-304] Order transactions by ID

* [NOD-304] Add error for passing arrays to GET
2019-09-08 10:52:03 +03:00
Ori Newman
cf9af0fb5d [NOD-320] Make txgen explicitly skip mempool transactions (#396) 2019-09-05 16:14:55 +03:00
Ori Newman
db6d6293c7 [NOD-319] Add query params to api server route handler (#394) 2019-09-04 17:34:36 +03:00
Ori Newman
ae25ec2e6b [NOD-303] Implement get transaction by id for api server (#391)
* [NOD-303] Implement get transaction by id for api server

* [NOD-303] Make routeParamTxID a constant

* [NOD-303] Change database is not current error.

* [NOD-303] Add ID to TransactionInput and TransactionOutput models

* [NOD-303] Change transactions_outputs table name to transaction_outputs and transactions_inputs to transaction_inputs

* [NOD-303] Add json annotations to transaction response types

* [NOD-303] Split server package

* [NOD-303] Add GetTransactionByHashHandler

* [NOD-303] Add comments to exported functions and variables

* [NOD-303] Put response types in a separate file

* [NOD-303] Rename functions
2019-09-03 15:54:59 +03:00
Svarog
7521545682 [NOD-311] Removed JSONRPCfyer (#392) 2019-09-03 11:24:25 +03:00
Ori Newman
169e96e851 [NOD-310] Implement REST server in API server (#389)
* [NOD-310] Implement REST server in API server

* [NOD-310] MetaData -> Metadata

* [NOD-310] Make custom context methods instead of custom request functions

* [NOD-310] change "Request ID" prefix to "RID" and convert to apiServerContext with newAPIServerContext everywhere
2019-09-01 17:03:43 +03:00
Ori Newman
893b8a88c8 [NOD-312] Change defaultMinRelayTxFee to 1 satoshi per byte (#390) 2019-09-01 15:21:24 +03:00
Svarog
c60711ab15 [NOD-302] Accept ConnectionEstablished when Pending is expected in TestRetryPermanent (#388) 2019-08-29 15:27:03 +03:00
Ori Newman
1b00e01030 [NOD-301] Don't sync with peer if the rendezvous point is below finality (#387)
* [NOD-301] Don't sync with peer if the rendezvous point is below finality

* [NOD-301] Add block hash and peer address for the warn message

* [NOD-301] Fix perrLog.Warnf arguments order
2019-08-29 10:47:05 +03:00
Ori Newman
f0c80905eb [NOD-300] If node has invalid ancestor set the according status in blockindex (#386)
* [NOD-300] If node has invalid ancestor set the according status in blockindex

* [NOD-300] Test that status is also updated for grand child of an invalid block

* [NOD-300] change make(blockSet) to newSet()
2019-08-28 17:44:33 +03:00
stasatdaglabs
b07a118431 [NOD-292] When writing block to database - also create record in block index (#376)
* [NOD-292] In accept.go, made dbStoreBlock and flushToDB occur within the same transaction.

* [NOD-292] Implemented processing blocks that were not validated on BTCD start.

* [NOD-292] Fixed processing logic on init. Added a test for it.

* [NOD-292] Fixed some comments.

* [NOD-292] Made unlocks deferred in a couple of places.

* [NOD-292] Made unprocessed block reprocess via ProcessBlock rather than maybeAcceptBlock.

* [NOD-292] Fixed grammar in comment. Added an explanation to TestAcceptingInInit.

* [NOD-292] Split flushToDB into two versions.

* [NOD-292] Fixed a bad assignment.

* [NOD-292] Fixed bad spacing.
2019-08-28 14:52:57 +03:00
Ori Newman
0ae06cd277 [NOD-297] fix onchainchanged on rpcclient (#384)
* [NOD-297] Fix onChainChanged on rpcclient

* [NOD-285] create gorm models for db (#378)

* [NOD-285] Map API-Server database using GORM

* [NOD-285] Add accepting block to transactions and blocks models, and remove accepting block model

* [NOD-285] Define model relations

* [NOD-285] Fix many to many for Transaction and Block models

* [NOD-285] Remove redundant main file

* [NOD-296] Send SyncMgr.SubmitBlock errors as rpc errors (#381)

* [NOD-296] Send SyncMgr.SubmitBlock errors as rpc errors

* [NOD-296] Add error message prefix

* [NOD-298] Add comments to gorm models (#382)

* [NOD-294] Fix golint in deploy.sh and fix all lint warnings (#380)

* [NOD-294] Fix golint in deploy.sh and fixed all lint errors

* [NOD-294] Fix typos in comments

* [NOD-294] Convert VirtualForTest into alias of *virtualBlock

* [NOD-294] Fixed some more typos in comments

* [NOD-295] Limit the length of GetData to 50 (#383)

* [NOD-295] Fixed bad break condition in addInvsToGetDataMessageFromQueue.

* [NOD-295] Fixed the fix for bad break condition in addInvsToGetDataMessageFromQueue.

* [NOD-295] Made the check for max invs refer to invsNum instead of MaxInvPerGetDataMsg.

* [NOD-297] Fix onChainChanged on rpcclient

* [NOD-286] Implement API-Server base structure (#379)

* [NOD-286] Implement API-Server base structure

* [NOD-286] Add rpc user and password as command line arguments

* [NOD-286] Make log directory a CLI argument

* [NOD-286] Add db login details as CLI arguments

* [NOD-297] Fix onChainChanged on rpcclient and server

* [NOD-297] Fix variables and functions names

* [NOD-297] Fix AcceptedTxIds -> AcceptedTxIDs
2019-08-28 12:52:07 +03:00
Ori Newman
ed9165f533 [NOD-286] Implement API-Server base structure (#379)
* [NOD-286] Implement API-Server base structure

* [NOD-286] Add rpc user and password as command line arguments

* [NOD-286] Make log directory a CLI argument

* [NOD-286] Add db login details as CLI arguments
2019-08-27 16:19:01 +03:00
stasatdaglabs
c73113a12e [NOD-295] Limit the length of GetData to 50 (#383)
* [NOD-295] Fixed bad break condition in addInvsToGetDataMessageFromQueue.

* [NOD-295] Fixed the fix for bad break condition in addInvsToGetDataMessageFromQueue.

* [NOD-295] Made the check for max invs refer to invsNum instead of MaxInvPerGetDataMsg.
2019-08-27 13:09:36 +03:00
Svarog
480b2ca07c [NOD-294] Fix golint in deploy.sh and fix all lint warnings (#380)
* [NOD-294] Fix golint in deploy.sh and fixed all lint errors

* [NOD-294] Fix typos in comments

* [NOD-294] Convert VirtualForTest into alias of *virtualBlock

* [NOD-294] Fixed some more typos in comments
2019-08-27 12:00:23 +03:00
Ori Newman
c72b914050 [NOD-298] Add comments to gorm models (#382) 2019-08-27 11:48:43 +03:00
Ori Newman
5cf7f01d3f [NOD-296] Send SyncMgr.SubmitBlock errors as rpc errors (#381)
* [NOD-296] Send SyncMgr.SubmitBlock errors as rpc errors

* [NOD-296] Add error message prefix
2019-08-27 11:25:45 +03:00
Ori Newman
552a5917c2 [NOD-285] create gorm models for db (#378)
* [NOD-285] Map API-Server database using GORM

* [NOD-285] Add accepting block to transactions and blocks models, and remove accepting block model

* [NOD-285] Define model relations

* [NOD-285] Fix many to many for Transaction and Block models

* [NOD-285] Remove redundant main file
2019-08-27 11:25:07 +03:00
stasatdaglabs
5c14719f14 [NOD-295] Capped amount of invs in a getData message. (#377) 2019-08-26 15:12:55 +03:00
Svarog
d2353a189a [NOD-291] Remove database check from dag.BlockExists (#375) 2019-08-25 15:00:16 +03:00
Ori Newman
4fcd705ae3 [NOD-290] Remove block_id from transactions table (#374)
* [NOD-290] Remove block_id from transactions table

* [NOD-290] Remove block_id foreign key from transactions table
2019-08-25 10:23:03 +03:00
Ori Newman
744c17b4c8 [NOD-280] Create database for API server (#373)
* [NOD-280] Create database for API server

* [NOD-280] Rename public_key_script to pk_script

* [NOD-280] Change indexes names

* [NOD-280] Add accepting block to blocks and transactions table and remove accepting_blocks table

* [NOD-280] Add readme

* [NOD-280] Change VARCHAR(32) to CHAR(64)

* [NOD-280] Rename location_in_block to index
2019-08-22 17:21:09 +03:00
stasatdaglabs
e2eca24b33 [NOD-282] Fixed Telegram messages not being sent. (#371) 2019-08-22 13:41:48 +03:00
stasatdaglabs
36d5ac189f [NOD-283] Fixed a crash in notifyChainChanged. (#372) 2019-08-22 13:23:30 +03:00
stasatdaglabs
1a569c7bd7 [NOD-270] Implement NotifyChainUpdates api call (#368)
* [NOD-270] Added notifyChainChanges and related commands.

* [NOD-270] Added NTChainChanged to blockdag.

* [NOD-270] Implemented collection and sending of ChainChanged notifications.

* [NOD-270] Fixed an improperly named test.

* [NOD-270] Added a test: TestChainChangedNotification.

* [NOD-270] Fixed a couple copy+paste errors.

* [NOD-270] Added a couple of comments for TestChainChangedNotification.

* [NOD-270] Fixed formatting error.

* [NOD-270] Fixed a comment.

* [NOD-270] Uncoupled chain updates inside blockdag from the concept of a notification.

* [NOD-270] Removed intermediary ChainUpdates object from ChainChangedNotificationData.
2019-08-21 12:58:32 +03:00
Ori Newman
6bb53eaae3 [NOD-256] add error log (#369)
* [NOD-256] Add error log

* [NOD-256] Add error log

* [NOD-256] Fix typo and comment

* [NOD-256] Remove btclog dir

* [NOD-256] Format project

* [NOD-256] Add error log files

* [NOD-256] Add an option to add a log file to write into to an existing backend logger

* [NOD-256] Get rid of redundant logs initialization

* [NOD-256] rename initLogRotators to initLog

* [NOD-256] Get rid ExampleSignTxOutput and convert ExampleBlockDAG_ProcessBlock to a regular test

* [NOD-256] Show error message if os.Exiting from initLog
2019-08-21 11:26:21 +03:00
Svarog
747a9bb944 [NOD-278] Added default for MinRelayTxFee (#367) 2019-08-19 17:52:58 +03:00
Ori Newman
d2daf334a5 [NOD-241] Implement lower resolution peer rendezvous point discovery (#353)
* [NOD-241] Implement lower resolution peer rendezvous point discovery

* [NOD-241] Implement lower resolution peer rendezvous point discovery

* [NOD-241] Find exact rendezvous point

* [NOD-241] Find exact rendezvous point

* [NOD-241] Fix tests

* [NOD-241] Remove hash stop from MsgBlockLocator and add tests to MsgBlockLocator and MsgGetBlockLocator

* [NOD-241] Change everywhere startHash to hashStart and change comments

* [NOD-241] Fix locateBlockNodes to stop at hashStop

* [NOD-241] Formatted locatorSummary.

* [NOD-241] Fix node reversal

* [NOD-241] Fix hash start and hash stop order, and don't include startNode in dag.blockLocator

* [NOD-241] rename locateBlockNodes -> getBlueBlocksBetween and add a comment to it

* [NOD-241] change hash start to start hash and hash stop to stop hash

* [NOD-241] Move block locator stuff to a different file

* [NOD-241] Rename msggetblocks.go to msggetblockinvs.go

* [NOD-241] Format project

* [NOD-241] Rename rpcserverSyncManager.LocateHeaders to GetBlueBlocksHeadersBetween

* [NOD-241] Move the logic of finding the highest shared block to OnBlockLocator

* [NOD-241] Rename chainHeight -> nextChainHeight

* [NOD-241] Fix typo in comment
2019-08-19 15:35:13 +03:00
stasatdaglabs
70737e4e94 [NOD-264] Implement tx-selection algorithm (#358)
* [NOD-264] Implemented calcTxSelectionValue.

* [NOD-264] Fixed bad subnetworkID in calcTxSelectionValue.

* [NOD-264] Implemented sorting the txDescs by value.

* [NOD-264] Got rid of txPrioItem.

* [NOD-264] Moved transaction selection to a separate file.

* [NOD-264] Renamed the result object to txsForBlockTemplate.

* [NOD-264] Implemented tx selection.

* [NOD-264] Fixed trying to get the gas limit for built-in subnetworks.

* [NOD-264] Wrote comments where appropriate.

* [NOD-264] Moved calcTxSelectionValue to the mining package. (Non-mining nodes shouldn't be forced to calc selection value for every transaction)

* [NOD-264] Wrote a test for selectTxs.

* [NOD-264] Fixed a comment.

* [NOD-264] Fixed misunderstood test.

* [NOD-264] Added zero fee check. Added a couple more tests.

* [NOD-264] Added probabilistic tests. Fixed a couple of bugs in tx selection.

* [NOD-264] Fixed tests with missing fees.

* [NOD-264] Added a test over a range of txs with different gas/mass.

* [NOD-264] Added expected probability to the rest of the test cases.

* [NOD-264] Tightened bounds in probability test.

* [NOD-264] Fixed values in probabily test.

* [NOD-264] Added a comments for alpha and rebalanceThreshold.

* [NOD-264] Fixed a couple of comments, renamed result to txsForBlockTemplate.

* [NOD-264] Removed an irrelevant comment. Changed Tracef to Warnf in some logs.

* [NOD-264] Renamed selectionValue -> txValue.

* [NOD-264] Moved rebalancing to the start of the tx selection loop.

* [NOD-264] Added overflow check for gasUsage.

* [NOD-264] Renamed blockSigOps and blockMass to totalSigOps and totalMass.

* [NOD-264] Removed the need to pass usedCount to reblanaceCandidates. Also relaxed bounds in a test.

* [NOD-264] Split selectTxs into smaller functions. Also relaxed bounds in a test some more.

* [NOD-264] Added a comment for findTx.

* [NOD-264] Ordered candidateTxs by subnetwork instead of txValue.

* [NOD-264] Disallowed zero tx fees in mempool and config. Renamed iterateCandidateTxs to populateTemplateFromCandidates.

* [NOD-264] Changed isFinalizedTransaction log level from Warn to Debug.

* [NOD-264] Removed references to SigOps in txSelection.

* [NOD-264] Removed SigOps validation. Validating mass should suffice.

* [NOD-264] Renamed wasUsed to isMarkedForDeletion.

* [NOD-264] Renamed markCandidateTxUsed to markCandidateTxForDeletion.

* [NOD-264] Made some probabilistic tests less likely to fail when they shouldn't.

* [NOD-264] Added a message warning people about probabilistic tests.

* [NOD-264] Rephrased a comment about rebalanceThreshold.

* [NOD-264] Removed IsCoinBase, CheckTransactionInputsAndCalulateFee, and ValidateTransactionScripts from txSelection.

* [NOD-264] Removed a condition that is no longer relevant.

* [NOD-264] "which's" -> "whose"

* [NOD-264] Removed wasteful preallocations.

* [NOD-264] Fixed a comment referring to "used" transactions.
2019-08-19 12:08:48 +03:00
stasatdaglabs
5f49115cac [NOD-269] Implement GetChainFromBlock api-call (#364)
* [NOD-269] Added a skeleton for getChainFromBlock.

* [NOD-269] Made startHash and includeBlocks optional.

* [NOD-269] Implemented chainBlock collection.

* [NOD-269] Extracted GetBlockVerboseResult building to its own method.

* [NOD-269] Implemented the IncludeBlocks part of GetChainFromBlock.

* [NOD-269] Added a comment for NewGetChainFromBlockCmd.

* [NOD-269] Made IsInSelectedPathChain return an error.

* [NOD-269] Fixed a very wrong comment.

* [NOD-269] Made SelectedPathChain allocate only the required amount of space.

* [NOD-269] Renamed pathChain to parentChain.

* [NOD-269] Split handleGetChainFromBlock to separate functions.

* [NOD-269] Fixed some grammar.
2019-08-18 13:31:54 +03:00
stasatdaglabs
534cb2bf5b [NOD-272] In CheckTransactionSanity, multiply mass limit check by massPerTxByte. (#365) 2019-08-15 15:05:26 +03:00
stasatdaglabs
187c525667 [NOD-234] In getBlockTemplate check if node is current (#362)
* [NOD-234] Added an IsCurrent check to handleGetBlockTemplate.

* [NOD-234] Removed IsCurrent check from handleGetBlockTemplateRequest. Added an explanation for why we're checking the chainHeight.

* [NOD-234] Added ShouldMineOnGenesis to the IsCurrent check.

* [NOD-234] Flipped && operands to fail fast.
2019-08-14 15:09:35 +03:00
stasatdaglabs
6032727965 [NOD-268] Implement selectedParentChain-related structures in btcjson. (#363) 2019-08-13 11:15:51 +03:00
stasatdaglabs
bb3f23b6dc [NOD-240] Get rid of all references for wallet related RPC commands (#361)
* [NOD-240] Removed references to the wallet in rpcserver.go.

* [NOD-240] Began removing btcwalletxxx.go.

* [NOD-240] Got rid of rpcclient/wallet.go and walletsvrcmds.go.

* [NOD-240] Moved GetBestBlockResult to dagsvrresults.go.

* [NOD-240] Finished removing walletsvrXXX.go.

* [NOD-240] Removed wallet stuff from btcctl.

* [NOD-240] Removed a few last things that I've missed.
2019-08-12 09:54:07 +03:00
stasatdaglabs
e5485ac5e6 [NOD-262] Renamed all instances of GetBlocks to GetBlockInvs. (#359) 2019-08-11 12:25:29 +03:00
stasatdaglabs
594a209f83 [NOD-263] Rename all instances of hashStop to stopHash. (#360) 2019-08-11 11:20:21 +03:00
Svarog
9981ce7adb [NOD-255] When adding orphans during netsync process - report only to debug log, unless number of orphans > k*2 (#357)
* [NOD-255] When orphan blocks arrive from netsync - don't write log unless we are in Debug

* [NOD-255] If there are more than K*2 orphans in pool - report as a potential problem anyway

* [NOD-255] Update comment to explain the K*2 figure
2019-08-07 11:30:32 +03:00
Svarog
49ac97c7db [NOD-265] Return an empty array for searchRawTransactions when no txs were found (#356) 2019-08-06 17:54:56 +03:00
stasatdaglabs
bfdf7a2cf2 [NOD-237] Implement transaction mass (#355)
* [NOD-237] Implemented transaction mass.

* [NOD-237] Added transaction mass validation to the mempool.

* [NOD-237] Made blockMaxMassMax not rely on MaxBlockPayload.

* [NOD-237] Added comments describing the new constants in validate.go.

* [NOD-237] Changed the default blockmaxmass to 10,000,000.

* [NOD-237] Fixed a comment that erroneously didn't refer to mass.

* [NOD-237] Added comments to ValidateTxMass and CalcTxMass.

* [NOD-237] Renamed "size" to "byte". Made validateBlockMass exit early if validation fails. Fixed unit names in comments. In CalcTxMass, moved summing of mass to the bottom of the function.

* [NOD-237] Instead of ErrMassTooHigh, renamed ErrBlockTooBig and ErrTxTooBig. Replaced wire.MaxBlockPayload with MaxMassPerBlock.

* [NOD-237] Fixed sanity checks related to block size in commands.

* [NOD-237] To use up less memory during testing, made the mass in the "too big" test come from pkScripts rather than input bytes.

* [NOD-237] Added an overflow check to validateBlockMass.
2019-08-05 16:04:24 +03:00
Ori Newman
54b681460d [NOD-244] Don't validate subnetwork registry transactions when they are being registered (#354) 2019-08-01 12:40:01 +03:00
stasatdaglabs
2147d16c1f [NOD-220] When handling an INV message, made it skip tx invs that are currently being requested. (#352) 2019-08-01 12:31:40 +03:00
Ori Newman
7c1cb47bd0 [NOD-249] Change WaitGroup to use channels (#350)
* [NOD-248] Implement waitgroup to enable waiting while adding

* [NOD-248] fix waitGroup.done() error message

* [NOD-248] atomically read wg.counter

* [NOD-248] return lowPriorityMutex

* [NOD-249] Add tests to waitgroup

* [NOD-249] Change waitgroup to use channels

* [NOD-249] Format project

* [NOD-249] Add comments and logs to waitGroup, and remove timeouts from
prioritymutex_test.go

* [NOD-249] Fix comments
2019-07-28 18:23:26 +03:00
stasatdaglabs
6acfa18d7c [NOD-220] Fixed handleSearchRawTransactions trying to get confirmations of mempool transactions, which made txgen crash on start. (#351) 2019-07-28 13:02:16 +03:00
Svarog
f0a675162c [NOD-253] Use spawn instead of go (#349) 2019-07-22 12:06:13 +03:00
Svarog
7a4deb6f18 [NOD-251] Use bluest parent anywhere validating difficulty, instead of selectedTip (#348) 2019-07-17 14:46:58 +03:00
Svarog
96842353de [NOD-250] Calculate UTXOCommitment when loading DAG + add UTXOCommitment to getBlockDagInfo (#347) 2019-07-16 16:53:28 +03:00
Svarog
5ce8875ce0 [NOD-243] Optimize validateGasLimit (#346)
* [NOD-243] Optimize validateGasLimit to only remember the current subnetwork gasUsage

* [NOD-243] Fix a typo

* [NOD-243] Rephrased comment
2019-07-16 11:07:58 +03:00
Ori Newman
812819e92f [NOD-248] Implement waitgroup to enable waiting while adding (#345)
* [NOD-248] Implement waitgroup to enable waiting while adding

* [NOD-248] fix waitGroup.done() error message

* [NOD-248] atomically read wg.counter

* [NOD-248] return lowPriorityMutex
2019-07-14 18:50:09 +03:00
Ori Newman
5cb536643e [NOD-236] Add secondary address to txgen (#341)
* [NOD-236] Add secondary address to txgen

* [NOD-236] Add description to secondary address cli argument

* [NOD-236] Remove unnecessary empty line
2019-07-14 11:26:32 +03:00
Ori Newman
4c6b8969d3 [NOD-245] Increase MaxInvPerMsg and MaxBlocksPerMsg to 65536 (#343)
* [NOD-245] Increase MaxInvPerMsg and MaxBlocksPerMsg to 65536

* [NOD-245] Fix MaxInvPerMsg to 1 << 16
2019-07-14 10:47:41 +03:00
Svarog
8ccc63752c [NOD-246] Increate RequestTimeout in mining simulator (#344) 2019-07-14 10:36:55 +03:00
Ori Newman
1088b69616 [NOD-239] Use custom priority mutex for utxo diff store (#340)
* [NOD-239] Use custom priority mutex for utxo diff store

* [NOD-239] Add shared slice to TestMutex

* [NOD-239] Add TestHighPriorityReadLock

* [NOD-239] Change comments

* [NOD-239] Rename LowPriorityLock -> LowPriorityWriteLock

* [NOD-239] Rename lock functions to write lock

* [NOD-239] Make TestHighPriorityReadLock use channels
2019-07-14 10:17:26 +03:00
Ori Newman
541119dda2 [NOD-238] Check if the incoming block is the newset orphan (#339) 2019-07-08 10:31:00 +03:00
stasatdaglabs
7400eabc6d [NOD-233] Fixed iteration order when iterating over the blockIndex bucket. (#338) 2019-07-04 18:29:56 +03:00
stasatdaglabs
c3c429494f [NOD-228] Added JSONRPCifyer to the project and created a Dockerfile for it. (#337) 2019-07-04 11:16:05 +03:00
stasatdaglabs
6d20202354 [NOD-222] Use accepting block blue score instead of containing block blue score for sequence lock and block maturity (#333)
* [NOD-222] Added constant: UnacceptedBlueScore.

* [NOD-222] Made it so that block transactions always have UnacceptedBlueScore.

* [NOD-222] Implemented updating unaccepted UTXO entries with accepted ones in the virtual.

* [NOD-222] Fixed an unclear comment.

* [NOD-222] Fixed diffFromAcceptanceData not receiving the right blue score.

* [NOD-222] Fixed various issues with the implementation. It appears to work now.

* [NOD-222] Removed debug logs.

* [NOD-222] Fixed tests that relied on utxoCollection.String().

* [NOD-222] Fixed TestChainedTransactions.

* [NOD-222] Fixed tests that relied on GetVirtualFromParentsForTest.

* [NOD-222] Fixed having identical entries in toAdd and toRemove.

* [NOD-222] Fixed logic in diffFrom that I previously broke.

* [NOD-222] Fixed a wrong check.

* [NOD-222] Figured out the magical invocation to make everything work.

* [NOD-222] Fixed blockDB tests.

* [NOD-222] Removed debug method.

* [NOD-222] Fixed comments related to setting coinbase maturity to 0.

* [NOD-222] Fixed a typo in a comment.

* [NOD-222] Added a comment that explains the new addition in GetVirtualFromParentsForTest.

* [NOD-222] Added a comment to DiffUTXOSet.Get().

* [NOD-222] Fixed a nuance in DiffUTXOSet.containsInputs.

* [NOD-222] Replaced nonsense in GetVirtualFromParentsForTest with diffFromAcceptanceData.

* [NOD-222] Renamed newVirtualUTXO -> newVirtualPastUTXO.

* [NOD-222] Fixed a comment.

* [NOD-222] Extracted checking utxoCollection with blueScore to a method.

* [NOD-222] Added tests where the same entry is in both toAdd and toRemove.

* [NOD-222] Used Add/RemoveEntry inside diffFromAcceptedTx.

* [NOD-222] Removed superfluous test for UnacceptedBlueScore.

* [NOD-222] Added/Updated comments.

* [NOD-222] Added tests to TestUTXODiffRules.

* [NOD-222] Added appropriate protection against impossible "from"s in diffFrom.

* [NOD-222] Added a comment explaining why we diffFrom acceptanceData in verifyAndBuildUTXO.

* [NOD-222] Fixed comments and equal() in utxoset.
2019-07-02 16:28:54 +03:00
Ori Newman
d6297a3192 [NOD-225] Finalize nodes below finality point (#335)
* [NOD-225] Finalize nodes below finality point

* [NOD-225] finalizeNodesBelowFinalityPoint only if dag.lastFinalityPoint is changed

* [NOD-225] change comment in validateParents

* [NOD-225] add string to ErrInvalidParentsRelation error

* [NOD-225] Change comment in validateParents

* [NOD-225] Change comment in validateParents

* [NOD-225] change comment in validateParents

* [NOD-225] Delete diff data from db directly from finalizeNodesBelowFinalityPoint

* [NOD-225] Refactor updateFinalityPoint
2019-07-02 16:10:33 +03:00
Ori Newman
e2f8d4e0aa [NOD-232] Remove diff and diffChild from blockNode (#336) 2019-07-02 11:01:41 +03:00
stasatdaglabs
589763e8ec [NOD-226] Fix comments around BlockLocator (#334)
* [NOD-226] Corrected blockLocator-related comments.

* [NOD-226] Fixed "current tips" -> "selected tip".
2019-06-30 12:34:53 +03:00
Ori Newman
c14c64d534 [NOD-224] Make P2PK and raw Multisig non-standard (#332) 2019-06-27 12:44:22 +03:00
Ori Newman
f7f44995d6 [NOD-215] implement difficulty adjustment algorithm (#331)
* [NOD-215] Implement difficulty adjustment algorithm

* [NOD-215] Handle blocks with genesis parent, and fix adjustment factor calculation

* [NOD-215] Fix tests

* [NOD-215] fix calcNextRequiredDifficulty

* [NOD-215] Add TestDifficulty

* [NOD-215] Fix delay to be positive, and add tests for delayed blocks

* [NOD-215] Split calcBlockWindowMinMaxAndMedianTimestamps to two functions

* [NOD-215] Make explicit loop for padding blue block window with genesis

* [NOD-215] Name return values

* [NOD-215] Fix delay != 0 error messages

* [NOD-215] Fix comments

* [NOD-215] Fix blueBlockWindow

* [NOD-215] Add TestBlueBlockWindow

* [NOD-215] Rename PowLimit -> PowMax

* [NOD-215] Fix delay != 0 error messages

* [NOD-215] Move PowMaxBits to BlockDAG

* [NOD-215] Make blockWindow type

* [NOD-215] Make blueBlockWindow always pad with genesis

* [NOD-215] Remove redundant line in checkWindowIDs

* [NOD-215] Make medianTimestamp return error for empty window
2019-06-26 15:47:39 +03:00
Ori Newman
263737b3fb [NOD-196] move coinbase scriptpukey to payload (#330)
* [NOD-196] move coinbase scriptpukey to payload (no tests) (#311)

* [NOD-196] Move coinbase scriptPubKey to payload

* [NOD-196] Rename SubnetworkID.IsFull to SubnetworkID.IsBuiltIn

* [NOD-196] Fix comments

* [NOD-196] Add block subsidy to fee transaction

* [NOD-196] Fix comments

* [NOD-217] Merge coinbase and fee transaction (#328)

* [NOD-196] Fix tests

* [NOD-196] Fix tests

* [NOD-217] Add error to getBluesFeeData

* [NOD-217] Merge Coinbase and fee transaction

* [NOD-217] Format project

* [NOD-217] Remove OpTrue default for mining.NewBlockTemplate

* [NOD-196] Format project

* [NOD-217] Add missing space before comment

* [NOD-196] Change MaxCoinbasePayloadLen to 150
2019-06-17 17:43:13 +03:00
Svarog
0c5f3d72bd [NOD-223] Removed Nulldata as standard tx type (#329)
* [NOD-223] Removed Nulldata as standard tx type

* [NOD-223] Removed redundant space
2019-06-16 16:31:38 +03:00
stasatdaglabs
ffd886498a [NOD-208] Make block reward maturity use the same mechanism as confirmations (#327)
* [NOD-208] Added blockBlueScore to UTXOEntry.

* [NOD-208] Added blueBlockScore to NewUTXOEntry.

* [NOD-208] Fixed compilation errors in policy, utxoset, and dag tests.

* [NOD-208] Changed validateBlockRewardMaturity and CheckTransactionInputsAndCalulateFee to use blueScore.

* [NOD-208] Changed CalcBlockSubsidy to use blueScore.

* [NOD-208] Changed SequenceLockActive to use blueScore.

* [NOD-208] Removed ExtractCoinbaseHeight.

* [NOD-208] Removed reference to block height in ensureNoDuplicateTx.

* [NOD-208] Changed IsFinalizedTransaction to use blueScore.

* [NOD-208] Fixed merge errors.

* [NOD-208] Made UTXOEntry serialization use blueScore.

* [NOD-208] Changed CalcPriority and calcInputValueAge to use blueScore.

* [NOD-208] Changed calcSequenceLock to use blueScore.

* [NOD-208] Removed blockChainHeight from UTXOEntry.

* [NOD-208] Fixed compilation errors in feeEstimator. Fixed a bug in the test pool hardness.

* [NOD-208] Fixed oldestChainBlockWithBlueScoreGreaterThan not handling an extreme case.

* [NOD-208] Fixed TestDiffFromTx.

* [NOD-208] Got rid of priority and support of free transactions.

* [NOD-208] Fixed TestProcessTransaction.

* [NOD-208] Fixed TestTxFeePrioHeap.

* [NOD-208] Fixed TestAddrIndex and TestFeeEstimatorCfg.

* [NOD-208] Removed unused rateLimit parameter from ProcessTransaction.

* [NOD-208] Fixed tests that rely on CreateTxChain.

* [NOD-208] Fixed tests that rely on CreateSignedTxForSubnetwork.

* [NOD-208] Fixed TestFetchTransaction.

* [NOD-208] Fixed TestHandleNewBlock. Fixed HandleNewBlock erroneously processing fee transactions.

* [NOD-208] Fixed TestTxIndexConnectBlock.

* [NOD-208] Removed the use of Height() from the fee estimator.

* [NOD-208] Removed unused methods from rpcwebsocket.go.

* [NOD-208] Removed Height from util.Block.

* [NOD-208] Removed ErrForkTooOld. It doesn't make sense in a DAG.

* [NOD-208] Made blockHeap use blueScore instead of height.

* [NOD-208] Removed fee estimator.

* [NOD-208] Removed DAG.Height.

* [NOD-208] Made TestAncestorErrors test chainHeight instead of height.

* [NOD-208] Fixed a couple of comments that were still speaking about block height.

* [NOD-208] Replaced all uses of HighestTipHash with SelectedTipHash.

* [NOD-208] Remove blockNode highest and some remaining erroneous uses of height.

* [NOD-208] Fixed a couple of comments. Fixed outPoint -> outpoint merge error.

* [NOD-208] Fixed a couple more comments.

* [NOD-208] Used calcMinRequiredTxRelayFee instead of DefaultMinRelayTxFee for mempool tests.

* [NOD-208] Renamed mempool Config BestHeight to DAGChainHeight.

* [NOD-208] Fixed a bug in oldestChainBlockWithBlueScoreGreaterThan. Made calcSequenceLock use the node's selected parent chain rather than the virtual block's.

* [NOD-208] Removed chainHeight from blockNode String().
Renamed checkpointsByHeight to checkpointsByChainHeight and prevCheckpointHeight to prevCheckpointChainHeight.
Removed reference to chainHeight in blockIndexKey.
Fixed comments in dagio.go.

* [NOD-208] Removed indexers/blocklogger.go, as no one was using it.

* [NOD-208] Made blocklogger.go log blueScore instead of height.

* [NOD-208] Fixed typo.

* [NOD-208] Fixed comments, did minor renaming.

* [NOD-208] Made a "common sense" wrapper around sort.Search.

* [NOD-208] Fixed comment in SearchSlice.
2019-06-16 14:12:02 +03:00
Svarog
76f5619de7 [NOD-211] Add concurrent-safe version of BlockConfirmationsByHash (#326) 2019-06-10 13:30:16 +03:00
Svarog
35703e7956 [NOD-218] Fix the order of txgen log message arguments (#325) 2019-06-06 16:10:21 +03:00
Ori Newman
29231d8d14 [NOD-213] add customization to txgen (#324)
* [NOD-213] Add customization to txgen

* [NOD-213] Add fee rate as an argument

* [NOD-213] Don't delay transaction emission if there's no need

* [NOD-213] enqueueTransactions -> queueTransactions

* [NOD-213] reuse delay variable

* [NOD-213] Add ExtractGasLimit function

* [NOD-213] Use time.Ticket in sendTransactionLoop
2019-06-06 14:01:28 +03:00
Svarog
396842ae40 [NOD-207] Rename any place that says 'OutPoint' to 'Outpoint' (#323)
* [NOD-207] Rename any place that says 'OutPoint' to 'Outpoint'

* [NOD-207] Fix any place that says output point
2019-06-05 16:23:57 +03:00
Svarog
072c753323 [NOD-216] Revert implicit fee transaction (#322)
* Revert "[NOD-214] Remove Fee transaction from addrindex (#321)"

This reverts commit e4b2d869d4.

* Revert "[NOD-195] Make fee tx implicit (#315)"

This reverts commit ccca580a4b.
2019-06-05 12:54:51 +03:00
Ori Newman
6250342b86 [NOD-205] Reimplement txgen (#320)
* [NOD-205] Reimplement txgen

* [NOD-205] remove prev outpoints of all initial transactions

* [NOD-205] break txloop to smaller functions

* [NOD-205] Limit collectTransactions iterations

* [NOD-205] Use requiredConfirmations constant instead of inline number

* [NOD-205] Rename wTx -> walletTx

* [NOD-205] Remove handleNewBlock

* [NOD-205] Fix search and replace error
2019-06-04 18:06:35 +03:00
Ori Newman
e4b2d869d4 [NOD-214] Remove Fee transaction from addrindex (#321) 2019-06-04 16:12:00 +03:00
Svarog
ccca580a4b [NOD-195] Make fee tx implicit (#315)
* [NOD-195] Made fee tx implicit

* [NOD-195] Removed redundant checks for fee transactions

* [NOD-195] Add fee tx data into acceptence data and fee data

* [NOD-195] Fix some tests

* [NOD-195] Update Block100000 with new data

* [NOD-195] Fixed remaining tests

* [NOD-195] Save and load feeTx to/from database

* [NOD-195] Remove DisconnectBlock methods from indexers, since they are not used anywhere

* [NOD-195] Add fee tx to addrindex

* [NOD-195] Don't populate inputs for fee transactions

* [NOD-195] Delete feeTxBucket in removeDAGState

* [NOD-195] Got rid of util.FeeTRansactionIndex
2019-06-03 17:30:57 +03:00
stasatdaglabs
84970a8378 [NOD-201] Create AddSubnetwork cli tool (#319)
* [NOD-201] Implemented the AddSubnetwork CLI tool.

* [NOD-201] Fixed various bugs in AddSubnetwork.

* [NOD-201] Fixed mempool maybeAcceptTransaction verifying gasLimit for a subnetwork registry transaction.

* [NOD-201] Fixed serialization/deserialization bugs in addrIndex.

* [NOD-201] Fixed BlockConfirmationsByHash not handling the zeroHash.

* [NOD-201] Used btclog instead of go log.

* [NOD-201] Made gasLimit a command-line flag. Made waitForSubnetworkToBecomeAccepted only return an error.

* [NOD-201] Filtered out mempool transactions.

* [NOD-201] Fixed embarrassing typos.

* [NOD-201] Added subnetwork registry tx fee + appropriate cli flag.

* [NOD-201] Skipped TXOs that can't pay for registration.
2019-06-03 15:44:43 +03:00
Ori Newman
901bde1fd4 [NOD-202] undo createDAGState if blockdag new fails (#318)
* [NOD-204] Add UTXOCommitment to GetBlockTemplateResult

* [NOD-204] Add UTXOCommitment to GetBlockTemplateResult

* [NOD-206] Avoid leaking blocks from previous miner when switching miners

* [NOD-202] Undo createDAGState if blockdag.New fails

* [NOD-202] Fix gofmt errors
2019-05-30 18:14:27 +03:00
Ori Newman
33a4183bfa [NOD-206] Avoid leaking blocks from previous miner when switching miners (#317)
* [NOD-204] Add UTXOCommitment to GetBlockTemplateResult

* [NOD-204] Add UTXOCommitment to GetBlockTemplateResult

* [NOD-206] Avoid leaking blocks from previous miner when switching miners
2019-05-30 17:25:53 +03:00
Ori Newman
0bc6e5bc92 [NOD-204] Add utxo commitment to get block template result (#316)
* [NOD-204] Add UTXOCommitment to GetBlockTemplateResult

* [NOD-204] Add UTXOCommitment to GetBlockTemplateResult
2019-05-30 16:59:26 +03:00
stasatdaglabs
8323e468da [NOD-200] Add GetSubNetwork command to JSON-RPC (#314)
* [NOD-200] Implemented the GetSubnetwork JSON-RPC command.

* [NOD-200] Fixed a copy+paste error in a comment.
2019-05-29 17:59:18 +03:00
Ori Newman
7912fe4c35 [NOD-203] Add UTXO commitment to devnet genesis (#313) 2019-05-29 15:06:10 +03:00
stasatdaglabs
266e471941 [NOD-190] Implement Confirmations counting algorithm (#312)
* [NOD-192] Add method to compute confirmations of a single transaction (#306)

* [NOD-192] Implemented txConfirmations.

* [NOD-192] Renamed acceptedBy -> acceptingBlock and ConfirmationsByHash -> BlockConfirmationsByHash.

* [NOD-194 + NOD-199] Update all JSON-RPC methods to use new methods for computing confirmations + Remove the x1.5 factor when counting confirmations in txgen (#309)

* [NOD-194] Connected JSON-RPC commands with new confirmations logic.

* [NOD-194] Fixed failing tests.

* [NOD-194] Removed x1.5 from isTxMatured.

* [NOD-194] Made isTxMatured panic if it receives nil confirmations.

* [NOD-194] Added isInMempool to RPC methods that require it.

* [NOD-194] Fixed a typo.

* [NOD-194] Made the declaration of isInMempool more clear.

* [NOD-194] Removed some unnecessary complexity from isTxMatured.

* [NOD-193] Update Tx-Index to accomodate correct Confirmations structure (#308)

* [NOD-193] Uploaded BlockID to be uint64 in txIndex and addrIndex.

* [NOD-193] Removed the inclusion of current block transactions to txsAcceptanceData.

* [NOD-193] Implemented writing to the tx index txs with the virtual as the accepting block.

* [NOD-193] Added test for txs accepted by the virtual block.

* [NOD-193] Removed the requirement for subnetwork registry transactions to be accepted.

* [NOD-194] Made in-memory the txsAcceptedByVirtual part of txIndex.

* [NOD-193] Optimized txsAcceptedByVirtual initialization.

* [NOD-193] Fixed weird loop in txsAcceptedByVirtual initialization.

* [NOD-190] Fixed merge errors.
2019-05-29 13:09:16 +03:00
stasatdaglabs
4e6edd4ffd [NOD-189] Optimize UTXOCollection operations (#307)
* [NOD-189] Made UTXODiff WithDiff and DiffFrom allocate collections with appropriate sizes.
In mempool HandleNewBlock, Replaced removeTransaction loop with removeTransactions.

* [NOD-189] Removed code duplication between removeTransaction and removeTransactions.

* [NOD-189] Fixed a merge error.

* [NOD-189] Fixed another merge error.

* [NOD-189] Renamed removeRedeemers to removeDependants.

* [NOD-189] Removed superfluous check inside removeTransactionWithDiff.

* [NOD-189] Added a comment to removeTransactions detailing what it optimizes.

* [NOD-189] Added documentation to removeTransactionWithDiff and split it into smaller methods.
2019-05-29 11:46:55 +03:00
Ori Newman
7069d173c6 [NOD-180] Add validation of utxo commitments (#310)
* [NOD-172] Port EMCH from bchd

* [NOD-172] Fix hdkeychain.TestErrors and add btcec.TestRecoverCompact

* [NOD-172] Make ECMH immutable

* [NOD-172] Fix gofmt errors

* [NOD-172] Add TestMultiset_NewMultisetFromDataSlice and fix Point to be immutable

* [NOD-172] Fix gofmt errors

* [NOD-172] Add test for checking that the Union of a multiset and its inverse is zero

* [NOD-179] Add ECMH Point to all UTXO-structs

* [NOD-179] Fix utxo set tests

* [NOD-179] Fix mempool tests

* [NOD-179] Remove RemoveTxOuts

* [NOD-179] Move serializeBlockUTXODiffData to the top of the file

* [NOD-179] Fix serializeBlockUTXODiffData comment format

* [NOD-179] Fix AddTx comment and name return values

* [NOD-180] Validate utxo commitments

* [NOD-179] Fix TestAcceptingBlock and TestConfirmations to not use the block hash as phantom break even

* [NOD-180] Fix typo

* [NOD-180] move most of the logic in calcUTXOCommitment to UTXOSet.WithTransactions

* [NOD-180] Optionally return error when a transaction in WithTransactions is double spent

* [NOD-180] Rename allowDoubleSpends to ignoreDoubleSpends
2019-05-28 11:33:11 +03:00
Ori Newman
aa51b5f071 [NOD-179] Added ECMH-Multiset to all UTXO structs (#304)
* [NOD-172] Port EMCH from bchd

* [NOD-172] Fix hdkeychain.TestErrors and add btcec.TestRecoverCompact

* [NOD-172] Make ECMH immutable

* [NOD-172] Fix gofmt errors

* [NOD-172] Add TestMultiset_NewMultisetFromDataSlice and fix Point to be immutable

* [NOD-172] Fix gofmt errors

* [NOD-172] Add test for checking that the Union of a multiset and its inverse is zero

* [NOD-179] Add ECMH Point to all UTXO-structs

* [NOD-179] Fix utxo set tests

* [NOD-179] Fix mempool tests

* [NOD-179] Remove RemoveTxOuts

* [NOD-179] Move serializeBlockUTXODiffData to the top of the file

* [NOD-179] Fix serializeBlockUTXODiffData comment format

* [NOD-179] Fix AddTx comment and name return values
2019-05-23 15:11:42 +03:00
stasatdaglabs
da7c9c7dfb [NOD-191] Added .acceptingBlock and .confirmations methods to blockNode (#305)
* [NOD-191] Added selectedPathChainSlice to virtualBlock.

* [NOD-191] Implemented acceptingBlock().

* [NOD-191] Implemented confirmations().

* [NOD-191] Added selectedPathChainSlice tests to TestSelectedPath.

* [NOD-191] Fixed a bug in acceptingBlock(). Written tests for confirmations().

* [NOD-191] Written tests for acceptingBlock().

* [NOD-191] Added test to make sure that acceptingBlock(tip) returns the virtual block.

* [NOD-191] Added a panic if we somehow feed a childless block that isn't the virtual to acceptingBlock.

* [NOD-191] Fixed comments.

* [NOD-191] Fixed a bug in acceptingBlock. Added red block tests for acceptingBlock.

* [NOD-191] Added red block tests for confirmations.

* [NOD-191] Fixed misleading comment and error message.
2019-05-23 10:57:03 +03:00
Svarog
ec10346e79 [NOD-184] Use lock-less NextAcceptedIDMerkleRoot in NewBlockTemplate (#302) 2019-05-16 17:33:04 +03:00
stasatdaglabs
2481871c10 [NOD-175] When resolving orphans - don't send inv (#300)
* [NOD-175] Added BlockAddedNotificationData and sent it instead of just a block on BlockAdded.

* [NOD-175] Added BFWasUnorphaned and raised it when an unorphaned block was to be accepted.

* [NOD-175] Fixed a typo.

* [NOD-175] Made it so that only the mempool gets updated if we're not current or the block was just now unorphaned.
2019-05-16 13:05:30 +03:00
Svarog
ac1fd11a42 [NOD-182] Added AcceptedIDMerkleRoot to GetBlockTemplateResult (#301) 2019-05-16 12:44:25 +03:00
stasatdaglabs
b1d3ca0206 [NOD-177] Remove idMerkleRoot (#299)
* [NOD-177] Removed references to idMerkleRoot.

* [NOD-177] Generated new genesis hashes.

* [NOD-177] Generated new blk_ blocks.

* [NOD-177] Fixed TestHaveBlock.

* [NOD-177] Fixed The rest of the tests.

* [NOD-177] Fixed a couple of comments and a duplicate test.

* [NOD-177] Fixed blocks1-256.bz2.
2019-05-15 16:16:57 +03:00
Ori Newman
5c5491e1e4 [NOD-172] Port ECMH from bchd and fix Remove to preserve commutativity (#292)
* [NOD-172] Port EMCH from bchd

* [NOD-172] Fix hdkeychain.TestErrors and add btcec.TestRecoverCompact

* [NOD-172] Make ECMH immutable

* [NOD-172] Fix gofmt errors

* [NOD-172] Add TestMultiset_NewMultisetFromDataSlice and fix Point to be immutable

* [NOD-172] Fix gofmt errors

* [NOD-172] Add test for checking that the Union of a multiset and its inverse is zero
2019-05-15 16:07:37 +03:00
Evgeny Khirin
8dedca693e [NOD-164 + NOD-167] AcceptedIDMerkleRoot validation and newBlockTemplate (#295)
* [NOD-164] Added validation routine

* [NOD-167] Extracted acceptedIDMerkleRoot calculation to its own method and implemented NextAcceptedIDMerkleRoot.

* [NOD-164] Fixed TestValidateFeeTransaction.

* [NOD-164] Fixed TestFinality.

* [NOD-164] Fixed blk_ tests.

* [NOD-164] Fixed if -> iff in a comment.

* [NOD-164] Minor style changes in comments.

* [NOD-164] Moved validateAcceptedIDMerkleRoot to before its population with the block's own transactions.
Replaced heavy call to verifyAndBuildUTXO in NextBlockFeeTransaction and NextAcceptedIDMerkleRoot with a call to pastUTXO on the virtual.

* [NOD-164] Fixed erroneous comment.

* [NOD-164] Inserted the logic from buildAndSortAcceptedTxs into calculateAcceptedIDMerkleRoot, since the former was meaningless on its own.

* [NOD-164] Changed looping over txsAcceptanceData instead of over node.blues.
2019-05-14 15:31:23 +03:00
Svarog
ca0619bbcf [NOD-176] Moved daghash from dagconfig to util (#298)
* [NOD-176] Moved daghash from dagconfig to util

* [NOD-176] Fixed order of includes with gofmt
2019-05-14 14:05:36 +03:00
Ori Newman
d7a2ab52a1 [NOD-173] Add UTXO commitment to block header (#297) 2019-05-13 16:23:28 +03:00
Ori Newman
3b72aafbc6 [NOD-174] Measure difficulty window with chain height instead of height (#296) 2019-05-12 18:14:35 +03:00
stasatdaglabs
dfd12cdaac [NOD-165] In getBlockVerboseResult and getBlockHeaderVerboseResult, renamed merkleRoot to HashMerkleRoot and added idMerkleRoot and acceptedIdMerkleRoot. (#294) 2019-05-12 15:05:33 +03:00
Evgeny Khirin
08d94c7a47 [NOD-163] Added BlockHeader.AcceptedIDMerkleRoot (#293) 2019-05-12 11:53:47 +03:00
Ori Newman
b7b41f1a94 [NOD-159] Wrap all goroutines to handle panics (#290)
* [NOD-159] Wrap all goroutines to handle panics

* [NOD-159] Fix gofmt errors

* [NOD-159] Add comment to HandlePanic

* [NOD-159] Merge panics and gowrapper packages

* [NOD-159] Added missing initialization
2019-05-07 16:13:06 +03:00
stasatdaglabs
42109ec4d5 [NOD-162] Fixed calculateNodeHeight not handling the genesis block. (#291) 2019-05-07 14:36:27 +03:00
Ori Newman
39ccc4b225 [NOD-166] Remove dag.RLock() from addTransaction (#289) 2019-05-06 17:42:39 +03:00
Ori Newman
8acc738b27 [NOD-146] Remove unnecessary dag notifications (#288) 2019-05-06 11:19:19 +03:00
stasatdaglabs
945b3f8fbf [NOD-13] Notify failing builds in Telegram (#287)
* [NOD-13] Added notify_telegram to deploy.sh.

* [NOD-13] Made a test fail for testing.

* [NOD-13] Added some temporary logging.

* [NOD-13] Wrote a nice message for the bot to send.

* [NOD-13] Made the message nicer.

* [NOD-13] Made the message nicer still.

* [NOD-13] Added the build log as an attachment.

* [NOD-13] Actually added the build log as an attachment.

* [NOD-13] Added a delay to allow the build log to properly flush.

* [NOD-13] Disowned notify_telegram.

* [NOD-13] Disowning doesn't work. Using the "at" command instead.

* [NOD-13] Properly using the at command.

* [NOD-13] Actually properly using the at command.

* [NOD-13] Added a couple of prints to see whether the script is even being called.

* [NOD-13] More printouts...

* [NOD-13] Added a command to start atd if it stopped for some reason.

* [NOD-13] Added slashes in multiline echo command.

* [NOD-13] Added quotes where required and removed debug comments.

* [NOD-13] Revert "[NOD-13] Made a test fail for testing."

This reverts commit 9701e30e

* [NOD-13] Added some comments.
2019-05-05 17:12:55 +03:00
Evgeny Khirin
a73f218402 [NOD-150] Removed blockNode.workSum (#286)
* [NOD-150] Removed blockNode.workSum

* [NOD-150] Fixed comment
2019-05-05 16:02:07 +03:00
Ori Newman
eded4c2285 [NOD-161] Add flushDbCache rpc command (#285)
* [NOD-161] Add flushDbCache rpc command

* [NOD-161] Fix tests
2019-05-02 17:08:00 +03:00
Svarog
33036278ac [NOD-144] Use chainHeight in SelectedAncestor, and update all logic that uses it (#281)
* [NOD-144] Use chainHeight in SelectedAncestor, and update all logic that uses it

* [NOD-144] Moved UnminedHeight to blockdag, and updated all references
2019-05-02 16:50:54 +03:00
Ori Newman
6163d3b4ec [NOD-87] Rename hash to txID when necessary (#283)
* [NOD-87] Rename hash to txID when necessary

* [NOD-87] Fix NewTxIDFromStr error messages in msgtx_test.go
2019-05-02 14:59:59 +03:00
Evgeny Khirin
22046bebc5 [NOD-93] Removed "make go vet happy" comments (#282) 2019-05-02 13:45:40 +03:00
Evgeny Khirin
c67d4507b6 [NOD-132] Fixed getaddr command summary (#278) 2019-05-02 12:06:23 +03:00
Ori Newman
ea5e18ea11 [NOD-96] Convert txid to pointer where possible (#279)
* [NOD-96] Convert txid to pointer where possible

* [NOD-96] Make msgTx.TxID return a pointer

* [NOD-96] observedTransaction.id -> observedTransaction.txID
2019-05-02 10:54:18 +03:00
stasatdaglabs
1cc479dbf8 [NOD-152] Netsync correctly syncs only 500 first blocks (#276)
* [NOD-152] Stopped pushBlockMsg from sending tip inv to syncing nodes.

* [NOD-152] Fixed restartSyncIfNeeded not restarting if sync is needed.

* [NOD-152] Removed continueHash, as it is no longer required.
2019-05-01 18:01:58 +03:00
Ori Newman
b4e7b59e7b [NOD-155] Increase timeout in test.sh (#280) 2019-05-01 17:36:19 +03:00
Evgeny Khirin
8592ae9641 [NOD-126] Removed BlockHeader.SelectedParentHash() method (#274)
* [NOD-126] Removed BlockHeader.SelectedParentHash() method

* [NOD-126] Added TODO comments
2019-05-01 14:51:16 +03:00
Ori Newman
1362fc45e0 [NOD-148] Delete block index (#275) 2019-05-01 13:10:32 +03:00
stasatdaglabs
b34894e4da [NOD-140] Convert all logging to use btclog (#273)
* [NOD-140] Converted DNSSeeder to use btclog.

* [NOD-140] Converted MiningSimulator to use btclog.

* [NOD-140] Converted TxGen to use btclog.

* [NOD-140] Fixed log level in handlePanic in txgen.

* [NOD-140] Renamed logger to log everywhere. Removed superfluous flag-setting to go-log.
2019-05-01 11:15:45 +03:00
stasatdaglabs
30f5ebd6d1 [NOD-139] Fix the unorphaning mechanism (#271)
* [NOD-139] Made processOrphans not turn an error if one of the parents is still missing.

* [NOD-139] Made addOrphanBlock and removeOrphanBlock process all parents instead of only the selected parent.

* [NOD-139] Made addOrphanBlock remove excess orphans by their timestamp rather than their discovery time. Fixed orphans being added more than once.

* [NOD-139] Simplified removal from slice in removeOrphanBlock.

* [NOD-139] Made check for no-orphans-left come before assignment to prevOrphans.

* [NOD-139] Added Timestamp() to util.Block.

* [NOD-139] Fixed merge errors.
2019-04-30 18:53:03 +03:00
Ori Newman
4292bcac72 [NOD-149] Store phantom values in the database (#270)
* [NOD-149] Store phantom values in the database

* [NOD-149] Explain when zero hash is used when serializing blockNode

* [NOD-149] make deserializeBlockNode return a blockNode

* [NOD-149] use blockNode initializer instead of lots of assignments
2019-04-30 17:42:26 +03:00
Evgeny Khirin
8683258e4a [NOD-151] Removed VerifyDAG RPC API (#272) 2019-04-30 13:22:29 +03:00
Svarog
e9ec8cd39c [NOD-142] Convert Height and ChainHeight to uint64 (#269)
* [NOD-142] Updated util.FastLog2Floor to work on uint64

* [NOD-142] Convert height and chainHeight to uint64

* [NOD-142] A couple fixes in comments of TestFastLog2Floor

* [NOD-142] Make spendableOutOffset uint64 too
2019-04-30 12:50:46 +03:00
Evgeny Khirin
068a8d117d [NOD-145] Fixed deadlock in mempool (#268) 2019-04-29 11:57:50 +03:00
Ori Newman
83a012de12 [NOD-69] Delete BlockAddedNtfnMethod (#267) 2019-04-29 11:50:56 +03:00
Evgeny Khirin
f36ae25baf [NOD-121] Fix empty output script in fundTx (#266)
* [NOD-121] Fix empty output script in fundTx

* [NOD-121] Remove debugging stuff

* [NOD-121] Refactored minRelayTxFee
2019-04-29 11:19:45 +03:00
stasatdaglabs
298cda0617 Merge pull request #265 from daglabs/nod-143-get-block-template-bug
[NOD-143] Multiple fixes for GetBlockTemplate
2019-04-28 12:32:12 +03:00
Mike Zak
b9e3fff5d1 [NOD-143] Move check for sm.syncPeer==nil to syncManager.current() 2019-04-28 12:23:42 +03:00
Mike Zak
ed76e2c962 [NOD-143] SelectedAncestor now returns the first chain block under height if there's no chain block with exact hight 2019-04-28 11:53:48 +03:00
Svarog
77fae7b522 [NOD-138] Request relayed blocks if not recent but no syncPeer available (which usually means everybody are on genesis) (#264) 2019-04-24 15:17:50 +03:00
stasatdaglabs
cd71e80eb3 [NOD-133] BTCD node connects multiple times to the same address (#263)
* [NOD-133] Added addrTrying.

* [NOD-133] Fixed infinite look inside getAddress.

* [NOD-133] Reverted log level to trace.

* [NOD-133] Fixed failing test.

* [NOD-133] Added an explanation as to why devnet is exempt from same-CIDR checking.

* [NOD-133] Changed config.DevNet with activeNetParams.AcceptUnroutable for same-CIDR checking.
2019-04-24 14:27:53 +03:00
Svarog
3f7c73f331 [NOD-135] Even if net params allow unroutable - don't allow local IPs (#262) 2019-04-24 11:52:19 +03:00
Svarog
4845a7f16c [NOD-131] Allow override of dnsseed by command line or config (#261)
* [NOD-131] Allow override of dnsseed by command line or config

* [NOD-131] Moved tor.go from connmgr to util/network, to prevent dependancy loop

* [NOD-131] Typo fix

* [NOD-131] Clarify description for --dnsseed cli flag

* [NOD-131] Removed redundant line that somehow got into go.sum
2019-04-23 14:22:46 +03:00
stasatdaglabs
77fb901706 [NOD-129] Revert NOD-114 (#260) 2019-04-22 15:26:10 +03:00
Evgeny Khirin
d3e70810af [NOD-121] Do not handle transaction inputs for reward transactions in SearchRawTransactions RPC call (#258)
* [NOD-121] Do not handle transaction inputs for reward transactions in SearchRawTransactions RPC call

* [NOD-121] Do not get transaction inputs for fee transactions in SearchRawTransactions RPC call
2019-04-22 12:57:50 +03:00
Evgeny Khirin
daa4481282 [NOD-66] Transaction generator (#247)
* [NOD-66] Created TX generator

* [NOD-66] Created transaction generator

* [NOD-66] Improved TX generator against double spend. Created genaddr utility. Refactored

* [NOD-66] Save chenges before branch switch

* [NOD-66] Use log package instead of fmt

* [NOD-66] Fixed/restored docker files

* [NOD-66] Changed according to new WithLock/NoLock convention
2019-04-21 15:05:03 +03:00
Ori Newman
a3735da12a [NOD-122] Fix timeout for get block template requests (#254)
* [NOD-122] Handle each message in rpcclient with a separate goroutine

* [NOD-122] Stop listening to new blocks when not mining

* [NOD-122] Made RPC logging in mining simulator more explicit + some styling enhencement
2019-04-21 10:11:36 +03:00
stasatdaglabs
311c96122e [NOD-119] Add a call to "go mod download" before copying all files in Dockerfiles. (#255) 2019-04-18 17:20:58 +03:00
Ori Newman
b612426ead [NOD-67] Unexport blockheap (#257) 2019-04-18 17:19:10 +03:00
Ori Newman
e99af346bf [NOD-120] Invert WithLock standard (#256) 2019-04-18 17:06:34 +03:00
Ori Newman
e22bc9af8f [NOD-115] add timeout to rpcclient requests (#252)
* [NOD-115] Add timeout to rpcclient requests

* [NOD-115] Add timeout of half a second to mining simulator requests

* [NOD-115] Remove redundant allocation of responseChan
2019-04-17 17:51:50 +03:00
stasatdaglabs
89ca293dc1 [NOD-113] Added graceful shutdown to mining simulator. (#253) 2019-04-17 17:00:23 +03:00
stasatdaglabs
194ceace6f [NOD-71] Add support for Go modules (#251)
* [NOD-71] Replaced Gopkg.lock and Gopkg.toml with go.mod and go.sum.

* [NOD-71] Updated Dockerfiles to use go-modules instead of dep.
2019-04-17 13:45:29 +03:00
Ori Newman
a79c6cecdb [NOD-90] Update mining simulator to pull latest block template (#248)
* [NOD-90] Update mining simulator to pull latest block template

* [NOD-90] Refactor to reduce global state

* [NOD-90] Split onMinerSwitch func

* [NOD-90] Replace chooseClient with getRandomClient

* [NOD-90] Stop ranging over foundBlock to avoid code repetition
2019-04-17 12:19:14 +03:00
stasatdaglabs
c5827febf7 [NOD-114] On start, DNSSeeder should update its addresses more frequently (#250)
* [NOD-114] Added a minimum address amount GetAddrs.

* [NOD-114] Added smallNetwork intervals for when the network is small.

* [NOD-114] Fixed bad minimum address calculation.
2019-04-15 15:57:43 +03:00
Ori Newman
7353a49469 [NOD-109] Reverse the order of .PastUTXO() and .RestoreUTXO() parameters (#249) 2019-04-15 10:37:06 +03:00
Ori Newman
1a2166cddf [NOD-101] Create a bucket for utxo diffs (#245)
* [NOD-101] Create a bucket for utxo diffs

* [NOD-101] Add error when diff data is not found

* [NOD-101] Fix serialization comment
2019-04-11 16:09:44 +03:00
Ori Newman
9276494820 [NOD-85] Fix dag.NewFinalityPoint (#246)
* [NOD-85] Fix dag.NewFinalityPoint

* [NOD-85] change newFinalityPoint to updateFinalityPoint

* [NOD-85] Fix comment in updateFinalityPoint
2019-04-11 16:05:55 +03:00
Ori Newman
8630b65bb2 [NOD-102] Change mining simulator to use cli args and add DisableTLS option (#244)
* [NOD-102] Change mining simulator to work with cli arguments and add DisableTLS option

* [NOD-102] Add config.go

* [NOD-102] Change config missing aruments errors
2019-04-04 14:55:14 +03:00
stasatdaglabs
46f9604562 [NOD-100] Fixed a crash on DNSSeeder start. (#243)
* [NOD-100] Fixed a crash on DNSSeeder start.

* [NOD-100] Made SelectedTip for DNSSeeder return the active network's genesis block hash.
2019-04-04 11:47:52 +03:00
Ori Newman
aa22480d5e [NOD-88] Add getTopHeaders rpc command (#242)
* [NOD-88] Add getTopHeaders rpc command

* [NOD-88] Fix gofmt error

* [NOD-88] Remove unnecessary variable

* [NOD-88] Remove GetTopHeaders from rpcSyncMgr
2019-04-04 11:06:19 +03:00
Evgeny Khirin
7c30bc4301 [NOD-64] Remove subnetwork id supports all (#237)
* [NOD-64] Intermediate commit: need check tests on master

* [NOD-64] Commit before changing subnetwork IDS to new values

* [NOD-64] Fixed tests after changing subnetworks IDs constants

* [NOD-64] Extract duplicate code into functions

* [NOD-64] Renamed IsAllSubnetworks ==> IncludeAllSubnetworks
2019-04-03 17:03:48 +03:00
Svarog
67358d1e85 [NOD-95] Added docker file and scripts to run dev instances for debugging (#241)
* [NOD-95] Added docker file and scripts to run dev instances for debugging

* [NOD-95] Enabled debugging from dockers

* [NOD-95] Removed redundant entrypoint.sh file

* [NOD-95] Removed multi-stage build artifacts from Dockerfile.dev

* [NOD-95] Add --help to run-dev.sh
2019-04-03 12:46:00 +03:00
Ori Newman
ba03d512f3 [NOD-89] Remove BTC seeders from seeder list in all nets and remove D… (#240)
* [NOD-89] Remove BTC seeders from seeder list in all nets and remove DNSSeed type

* [NOD-89] Unremove daglabs dns seed
2019-04-03 12:45:49 +03:00
Ori Newman
254eab96cd [NOD-55] Change daghash hash to pointer in most places (#239)
* [NOD-55] Change daghash.Hash to pointer in most places

* [NOD-55] Fixed format error

* [NOD-55] Fixed merge error

* [NOD-55] Cancel copying hash in blockSet.hashes()
2019-04-02 13:49:47 +03:00
stasatdaglabs
58b427424f [NOD-82] Create docker-composes and aws.yamls for DNS Seeders, Miner Nodes, and Non-Miner Nodes (#238)
* [NOD-82] Added RPC files to the BTCD Docker container.

* [NOD-82] Generated new cert and key files that include *.daglabs.com.

* [NOD-82] Added a dagConfig parameter for accepting unroutable IPs.
2019-04-02 12:26:50 +03:00
Svarog
66fb7513f2 [NOD-76] Allow blocks that do not connect directly to all tips in CheckConnectBlockTemplate (#236)
* [NOD-76] Allow blocks that do not connect directly to all tips in CheckConnectBlockTemplate

* [NOD-76] Fix typos in comments
2019-04-01 14:02:30 +03:00
Ori Newman
20e24e3a23 [NOD-83] modify blocks parents children only after validation (#235)
* [NOD-83] Modify block parents' children only after validation

* [NOD-83] Creating CheckConnectBlockTemplateWithLock with RLock

* [NOD-83] Put updateParentsChildren inside updateParents

* [NOD-83] create updateParentsDiffs function
2019-04-01 13:03:13 +03:00
Ori Newman
62d14bf2bd [NOD-16] implement initial sync first version (#234)
* [NOD-58] Replace lastBlock with selected tip in version message (#210)

* [NOD-58] Replace lastBlock with selected tip in version message

* [NOD-58] Fix typo in comment

* [NOD-58] Add mutex to SelectedTipHash

* [NOD-58] Remove redundant comment

* [NOD-58] Remove wantStartingHeight from peerStats

* [NOD-58] Remove lock from SelectedTipHash

* Nod 53 change getheaders message to handle new block locator (#213)

* [NOD-53] Change getheaders message to handle the new block locator mechanism

* [NOD-53] Use heap in locateHeaders

* [NOD-53] Create a constructor for each heap direction

* [NOD-57] Check if a node is synced only by timestamps (#214)

* [NOD-60] implement isSyncCandidate (#218)

* [NOD-60] Implement isSyncCandidate

* [NOD-60] Fix typo

* [NOD-65] Fix netsync related tests and remove fields optionality from… (#220)

* [NOD-65] Fix netsync related tests and remove fields optionality from msgversion

* [NOD-65] gofmt rpcserver.go

* [NOD-65] add missing test for verRelayTxFalse

* [NOD-62] Change getblocks message to handle the new block locator mechanism (#219)

* [NOD-62] Change getblocks message to handle the new block locator mechanism

* [NOD-62] Add locateBlockNodes function

* [NOD-68] Adjust orphan parents requesting for a DAG (#222)

* [NOD-68] Adjust orphan parents requesting for a DAG

* [NOD-68] add sendInvsFromRequestedQueue and trigger it when requested blocks slice is empty, or immediatly if we're not in sync mode

* [NOD-68] Prevent duplicates from entering to state.requestQueue and add wrapping locks to addBlocksToRequestQueue

* [NOD-68] Fix Lock -> Unlock in sendInvsFromRequestedQueue

* [NOD-74] Starts syncing again when the current sync peer is done (#225)

* [NOD-74] Starts syncing again when the current sync peer is done

* [NOD-74] Unlock mtx before netsync is restarted

* [NOD-74] Fix name isSyncPeerFree -> isWaitingForBlocks

* [NOD-75] fixing netsync bugs (#227)

* [NOD-74] Starts syncing again when the current sync peer is done

* [NOD-74] Unlock mtx before netsync is restarted

* [NOD-75] Fixing netsync bugs

* [NOD-80] Request block data from block propagation just after you are… (#231)

* [NOD-80] Request block data from block propagation just after you are current

* [NOD-80] Fix adding to both queues in addInvToRequestQueue

* [NOD-81] Start to mine on top of genesis iff all peers selected tip is genesis (#232)

* [NOD-81] Start to mine on top of genesis only if all of your peers' selected tip is genesis

* [NOD-81] Explain forAllPeers/forAllOutboundPeers shouldContinue behaviour in comments

* [NOD-81] Add forAllInboundPeers and add return values for forAllPeers/forAllOutboundPeers/forAllInboundPeers functions

* [NOD-16] Add pushSet to the BlockHeap type

* [NOD-16] Fixed syntax error
2019-03-31 16:47:28 +03:00
Svarog
1fe1b11823 [NOD-78] Make minning.NewBlockTemplate read-lock the dag, so that it's not modified while the function is running (#233)
* [NOD-78] Use transfer some read-only functions from dagLock.Lock to dagLock.RLock

* [NOD-78] Make mining.NewBlockTemplate lock the dag up until the point it calls CheckConnectBlockTemplate

* [NOD-78] Removed locking from functions that are concurrency-safe, and added WithLock version to those that are not

* [NOD-78] Remove unused isLockHeld from CalcNextBlockVersion

* [NOD-78] Renamed UTXORLock/UTXORUnlock to RLock/RUnlock

* [NOD-78] Revesed dagUnlocket to isDagLocked
2019-03-28 16:26:47 +02:00
Svarog
dd3b693268 [NOD-77] Fix addrmanager behaviour when address's subnetworkID is in conflict with what is known (#230)
* [NOD-77] AddrManager.Good now updates subnetwork in case it was modified

* [NOD-77] Do not update subnetworkID in updateAddr if address already known

* [NOD-77] Restructure case where ka.tried = true for more readable code

* [NOD-77] Some corrections to comments

* [NOD-77] Fixd typo
2019-03-26 16:50:43 +02:00
Svarog
047a2c16c4 [NOD-70] Added mining simluator (#228)
* [NOD-70] Added GetBlockTemplate method to rpcclient

* [NOD-70] Basic infrastructure for mining simulator

* [NOD-70] Fix txFees in NewBlockTempalte: include value for fee transaction + don't set fee for coinbase = -totalFees.

* [NOD-70] Added capabilities parameter to Client.GetBlockTemplate call

* [NOD-70] Dirty version of mining simulator complete

* [NOD-70] cleaned up mining simulator

* [NOD-70] Added dockerfile to mining simulator

* [NOD-70] Updated base config path of mining simulator to mining_simulator

* [NOD-70] Remove error return from msgblock.AddTransaction - it never returns one

* [NOD-70] Renamed r -> random

* [NOD-70] Move paths initialization of mining simulator to main

* [NOD-70] Cleaned up mining simulator dockerfile

* [NOD-70] Add '--' to tini argument
2019-03-26 16:37:44 +02:00
stasatdaglabs
dc103f3d86 [NOD-54] Update DNS Seeder to work from within Docker containers. (#229) 2019-03-26 12:53:32 +02:00
Ori Newman
6225728138 [NOD-79] Detach parents from block template node after CheckConnectBlockTemplate (#226)
* [NOD-79] Detach parents from block template node after CheckConnectBlockTemplate

* [NOD-79] Fix typo
2019-03-25 14:45:33 +02:00
Evgeny Khirin
0a30837aac [NOD-56] Lower mining difficulty as preparation for devnet (#223)
* [NOD-56] Created devnet

* [NOD-56] Fixed tests

* [NOD-56] Fixed go vet errors

* [NOD-56] Added TestSolveGenesisBlock function

* [NOD-56] Created command line tool for solving genesis blocks
2019-03-24 10:30:52 +02:00
Svarog
bb3c1e36a2 [NOD-72] Fix nil errors in deserializePeers (#224)
* [NOD-72] Fix nil errors in deserializePeers

* [NOD-72] Replicate the way I prettified triedBuckets code into newBuckets code
2019-03-21 15:47:37 +02:00
Svarog
e93e60aa74 [NOD-63] Merge BlockAccepted and BlockConnected notifications into BlockAdded + remove BlockDisconnected notifications (#221)
* [NOD-63] Merge BlockAccepted and BlockConnected notifications into BlockAdded + remove BlockDisconnected notifications

* [NOD-63] Many instances of chain->DAG and similar

* [NOD-63] Some more chian -> DAG
2019-03-20 13:48:32 +02:00
Svarog
a2b69a84f4 [NOD-48] Make wire.NewMsgTx recieve all paramaters that go into MsgTx, and compute what can be computed. (#216)
* [NOD-48] Update wire.NewMsgTx to recieve all fields in msgTx

* [NOD-48] Fix all compilation errors resulting from modification of wire.NewMsgTx

* [NOD-48] Calculate payloadHash iff subnetworkID is not native

* [NOD-48] Update all places the instantiate wire.MsgTx to use wire.NewMsgTx

* [NOD-48] Remove 'wire.' calls inside wire package

* [NOD-48] Made newMsgTx with all parameters private, and added a few public functions that take various arguments for all common use-cases

* [NOD-48] Explicitly pass SubnetworkIDNative instead of nil to newMsgTx

* [NOD-48] Remove option to pass nil to newMsgTx
2019-03-19 12:28:24 +02:00
Svarog
122520b9a5 Nod 42 restructure block acceptance logic (#217)
* [NOD-42] Split checkFinalityRules and newFinalityPoint

* [NOD-42] Rename connectToDAG -> addBlock + move anything that is not actually connecting block to DAG out of connectBlock

* [NOD-42] Extract methods from PastUTXO

* [NOD-42] Give names to outputs in verifyAndBuildUTXO and propagate name
changes up the call tree

* [NOD-42] Split loop that creates UTXODiff and updates acceptance data into 2 separate methods

* [NOD-42] Removed from applyUTXOChanges any validation logic, moved in any logic related to updating the DAG and renamed to applyDAGChanges

* [NOD-42] Rename: CheckTransactionInputs -> CheckTransactionInputsAndCalculateFee

* [NOD-42] Revise some comments

* [NOD-42] Removed finalityErr constant - it's not needed

* Multiple chain -> dag corrections in comments

* [NOD-42] Removed redundant declaration of feeData

* [NOD-42] Reworded some comments

* [NOD-42] Rename MultiblockTxsAcceptanceData -> MultiBlockTxsAcceptanceData
2019-03-19 11:21:27 +02:00
Evgeny Khirin
a31c07e8f3 [NOD-59] Rename txEncodingExcludeSubnetworkData (#215) 2019-03-17 16:44:44 +02:00
Evgeny Khirin
ee44eb20de [NOD-49] Exclude payload from transaction hash (#212) 2019-03-17 11:59:44 +02:00
Evgeny Khirin
a381e8672c [NOD-4] Exit on out of disk space when storing a block (#211)
* [NOD-4] Exit on out of disk space when storing a block

* [NOD-4] Fixed log message

* [NOD-4] Fixed log message

* [NOD-4] Fixed log message
2019-03-17 11:55:39 +02:00
Ori Newman
2b09546f8a [NOD-51] Change block locator behaviour for new netsync protocol (#208)
* [NOD-51] Change block locator behaviour for new netsync protocol

* [NOD-51] Update SelectedAncestor comment
2019-03-13 17:45:30 +02:00
Ori Newman
55659022bb [NOD-47] Put dnsseeder package inside btcd package (#207)
* [NOD-47] Put dnsseeder package inside btcd package

* [NOD-47] Remove .gitignore and run_tests.sh from dnsseeder package
2019-03-13 13:15:58 +02:00
Ori Newman
c9ef176f4c [NOD-35] Refactor fmt.Sprintf to always have the template argument as string literal
* [NOD-35] Refactor fmt.Sprintf to always have the template argument as string literal

* [NOD-35] Remove redundant fmt.Sprintf's
2019-03-12 18:12:24 +02:00
Evgeny Khirin
2db55f219f [NOD-26] Zero payload for signature hash calculation (#205)
* [NOD-26] Zero payload for signature hash calculation

* [NOD-26] Zero payload in shallowCopyTx function

* [NOD-26] Zero payload in calcSignatureHash function

* [NOD-26] Updated comment
2019-03-12 15:39:20 +02:00
Ori Newman
1da045d1b9 [NOD-30] move wire.RandomUint64() and binarySerializer to util (#204)
* [NOD-30] move wire.RandomUint64() and binarySerializer to util

* [NOD-30] change const style
2019-03-12 13:03:08 +02:00
Evgeny Khirin
b4f50f4e48 [Nod-25] Add payload hash to wire transaction (#200)
* [NOD-25] Intermediate commit

* [NOD-25] Fixed tests crashes

* [NOD-25] Fixed tests

* [NOD-25] Removed temporary debug code

* [NOD-25] Fixed error message
2019-03-12 10:48:57 +02:00
Ori Newman
ae92cb06ea [NOD-33] remove t.Parallel() from TestCheckErrorCondition because it contains monkey patching
* [NOD-33] remove t.Parallel() from TestCheckErrorCondition because it contains monkey patching

* [NOD-33] remove redundant line
2019-03-12 10:02:04 +02:00
Ori Newman
aacb2ada43 [NOD-36] Convert util.MaxSatoshi to integer (#199) 2019-03-10 17:38:18 +02:00
Ori Newman
d62279192a [NOD-27] If node started with new sub-network - require to delete the database before proceeding (#198) 2019-03-10 17:31:53 +02:00
Ori Newman
1e447432a7 [NOD-28] Move subnetwork consts to subnetworkid package (#197)
* [NOD-28] Move SubnetworkID constants to subnetworkid package

* [NOD-28] change subnetwork id constants to pointers
2019-03-10 12:27:06 +02:00
Ori Newman
aa46c167c8 [NOD-46] Immediately associate received address to their subnetwork (#196)
* [NOD-46] Immediately associate received address to their subnetwork

* [NOD-46] fix comment

* [NOD-46] Disconnect peer if it sends an MsgAddr with no subnetwork

* [NOD-46] Disconnect peer if it sends an MsgAddr with incorrect subnetwork id

* [NOD-46] add parenthesis to condition

* [NOD-46] change order of conditions
2019-03-07 16:33:10 +02:00
Svarog
a5e304d3ed [NOD-43] Update dockerfile to go1.12 (#195) 2019-03-07 15:20:29 +02:00
Evgeny Khirin
00692a4236 [DEV-363] Refactoring DNS seeder (#192)
* [DEV-363] Refactoring DNS seeder

* [DEV-363] Fixed comment
2019-03-06 13:24:25 +02:00
Svarog
5e15b8208e [NOD-41] Extract methods under connectBlock (#191)
* [DEV-379] Refactored checkBlockContext: Mainly extracted methods and re-organized variable use to minimize clutter

* [DEV-379] Simplify the condition according to which we increment blockCount

* [DEV-379] Move all logic to save new block data to separate method

* [DEV-379] Refactored the checking of finality point

* [DEV-379] Minor styling changes

* [DEV-379] Extracted method in applyUTXOChanges subroutines

* [NOD-41] Moved update of finality point to after block was validated + some minor style fixes

* [NOD-41] call dag.checkFinalityRulesAndGetFinalityPoint(node) even if fastAdd

* [NOD-41] Fix in a comment

* [NOD-41] Some methods of dag that could have been just functions converted to function
2019-03-05 14:17:30 +02:00
Evgeny Khirin
45cffb4f69 Dev 363 add subnets to dns seeder (#190)
* [DEV-363] Added testnet seeders

* [DEV-363] Prepare BTCD to work with DNS seeder

* [DEV-363] Finished BTCD part of DNS seeder

* [DEV-363] Reverted protocol changes

* [DEV-363] Reverted protocol changes

* [DEV-363] Reverted protocol changes and fixed tests

* [DEV-363] Added tests and refactored

* [DEV-363] Small refactoring of p2p changes

* [DEV-363] Update NeedMoreAddresses function to take into account full peers + small refactoring

* [DEV-363] Removed IsDNSSeederPeer flag

* [DEV-363] Fixed comment
2019-03-05 13:11:10 +02:00
Svarog
58b1b01c3f Dev 378 optimize calculate fee (#189)
* [DEV-378] Added feeAccumulator structures

* [DEV-378] Added logic to create and store fee data when validating a block

* [DEV-378] Renamed feeAccumulator to compactFeeData, and all related entities accordingly

* [DEV-378] Converted MsgTx.TxHash() to pointer to hash and not hash

* [DEV-378] Restructured parameters to buildFeeTransaction and related entities

* [DEV-378] Finished the code that calculates fees for blocks

* [DEV-378] Fix TxIndex after changing the structure of AcceptedTxsData

* [DEV-378] For genesis block: Return empty AcceptedTxsData instead of nil

* [DEV-378] Off-by-one error

* [DEV-378] Length of compactFeeData should be determined by specific method, not

* [DEV-378] Multiple bugfixes in tx fee calculation

* [DEV-378] Calculate fee even if fastAdd, to save feeData

* [DEV-378] use IsEqual instead of == when comparing TxHash

* [DEV-378] txindex: if including block is the new block - don't fetch id from DB

* [DEV-378] Fixed a few typos and made some vars consts

* [DEV-378] Re-organized fee functions, removed redundant functions and constants, and revised a few comments

* [DEV-378] Recovered fmt string changes lost in merge

* [DEV-378] Renamed acceptedTxsData and related types and vars to txsAcceptanceData

* [DEV-378] Some comment fixes

* [DEV-378] Remove redundant .ToString()
2019-02-24 11:52:06 +02:00
Svarog
41647fd488 [DEV-376] Changed any instance of %v in format strings with a more specific format token (#188)
* [DEV-376] Changed any instance of %v in format strings with a more specific format token

* [DEV-376] Fixed some more wrong formatting strings + removed redundant
cast

* [DEV-376] Added fmt.Sprintf where it was missing

* [DEV-376] use %s for util.Amount, to invoke .String()

* [DEV-376] Some more fixes in format strings

* [DEV-376] fixed mruinvmap_test to expect the correct behaviour
2019-02-20 14:02:52 +02:00
Ori Newman
f615298453 [DEV-259] Allow to spend genesis coinbase, and use ProcessBlock to ad… (#187)
* [DEV-259] Allow to spend genesis coinbase, and use ProcessBlock to add genesis to the DAG like any other block

* [DEV-259] fix IsCurrent to check genesis timestamp if needed

* [DEV-259] add genesisPastUTXO as separate function
2019-02-14 18:20:49 +02:00
Ori Newman
f06513aad7 [DEV-369] Revert CheckTransactionSanity and CheckBlockSanity + move all Gas logic somewhere else (#185)
* [DEV-369] Revert CheckTransactionSanity and CheckBlockSanity + move all Gas logic somewhere else

* [DEV-369] Add TestGasLimit

* [DEV-369] Handle zero coinbase output in PrepareBlockForTest

* [DEV-369] add comments to TestGasLimit
2019-02-14 18:05:11 +02:00
Ori Newman
b74dc9dfd1 [DEV-316] Update JSON-RPC API to include new transaction fields (#186)
* [DEV-316] Update JSON-RPC API to include new transaction fields

* [DEV-316] Fix tests

* [DEV-316] Add txAcceptedVerbose test with subnetwork, gas and paylaod
2019-02-14 15:19:44 +02:00
Ori Newman
d5f393872c [DEV-374] Fix chained transaction check on NewBlockTemplate (#184)
* [DEV-374] fix chained transaction check on NewBlockTemplate

* [DEV-374] remove addTxToBlock
2019-02-12 16:19:30 +02:00
Ori Newman
fd5ca4ac1a [DEV-360] Allow only 1 wire protocol version (#183) 2019-02-12 13:16:35 +02:00
Ori Newman
0aa7c430e0 Dev 364 implement the dag fee structure (#181)
* [DEV-364] Add fee transactions validation

* [DEV-364] make NextBlockFeeTransactions for creating block templates

* [DEV-364] apply coinbase rules to fee transaction is some cases

* [DEV-364] Add comments

* [DEV-364] put getTXO as separate function

* [DEV-364] Make getParentsFeeData a separate function

* [DEV-364] fix calculateFees

* [DEV-364] force maximum sequence for fee transactions

* [DEV-364] add TestValidateFeeTransactions

* [DEV-364] change fee transaction to be one tx per block rather than one tx for each blue

* [DEV-364] fix tests

* [DEV-364] Use constants instead of inline numbers
2019-02-11 17:33:55 +02:00
Ori Newman
3a9c06afd2 [DEV-371] Allow zero outputs for transactions (#182) 2019-02-11 12:42:26 +02:00
Evgeny Khirin
3eeff11231 [DEV-317] Update JSON-RPC API call GetBlock to allow filtering by SubNetworkID (#180)
* [DEV-317] Update JSON-RPC API call GetBlock to allow filtering by SubNetworkID

* [DEV-317] Changed comments, messages and refactored code
2019-02-05 16:38:21 +02:00
Ori Newman
0bc7a11551 Dev 341 increase mining test coverage (#178)
* [DEV-341] Ensure 100% coverage for mining package

* [DEV-341] Add TestNewBlockTemplate

* [DEV-341] Add TestNewBlockTemplate

* [DEV-341] Test GasLimit filtering in NewBlockTemplate

* [DEV-341] Add comment to TestNewBlockTemplate
2019-01-29 11:16:40 +02:00
Ori Newman
1e09d470f7 [DEV-336] Update peer package to accommodate partial nodes (#176)
* [DEV-336] Split addresses in address manager by subnetwork id

* [DEV-336] Load DAG with subnetwork from the config file

* [DEV-336] Remove redundant checks in updateAddrNew and updateAddrTried
2019-01-28 13:55:58 +02:00
stasatdaglabs
a82e6ae24a [DEV-92] Package blockdag tests (#177)
* [DEV-92] Covered blocknode.go with tests.

* [DEV-92] Added test for blockSet.highest. Fixed a bug in it.

* [DEV-92] Added tests for blockSet.subtract and blockSet.addSet.

* [DEV-92] Covered blockset.go with tests.

* [DEV-92] Got rid of some old stuff related to STXOs.

* [DEV-92] Covered error.go with tests.

* [DEV-92] Covered utxoSet with tests.

* [DEV-92] Fixed formatting.
2019-01-27 18:04:31 +02:00
Evgeny Khirin
100fbbaaa4 [DEV-361] Create type TxID as alias to daghash.Hash. (#175)
* [DEV-361] Create type TxID as alias to daghash.Hash. Use it for transaction IDs

* [DEV-361] Fixed missed renames

* [DEV-361] Removed usage of zeroHash

* [DEV-361] Fixed more missed renames
2019-01-27 14:27:10 +02:00
stasatdaglabs
9a2eee78a4 [DEV-319] Update WS-API call notifyNewTransactions to allow filtering by SubNetworkID (#174)
* [DEV-319] Implemented transaction data sending logic.

* [DEV-319] Implemented NotifyNewTransactions command validation.

* [DEV-319] Reduced some duplication in notifyForNewTx.

* [DEV-319] Renamed a parameter for clarity.

* [DEV-319] Added a test for marshalling/unmarshalling the new varient of notifyNewTransactions.

* [DEV-319] Added a check in handleNotifyNewTransactions to avoid unnecessary validation.

* [DEV-319] Added comments to explain the initialization of marshalledJSONVerboseFull and marshalledJSONVerbosePartial.
2019-01-24 18:18:03 +02:00
Svarog
9f93a1c50b [DEV-311] Enforce gas limit in blocks (#172)
* [DEV-311] Moved subnetwork storage from directly in DAG to subnetworkStore

* Added gas validation in CheckBlockSanity

* [DEV-311] Add SubnetworkStore to last remaining call for CheckBlockSanity

* [DEV-311] Added subnetworkID to config in TestcheckBlockSanity

* [DEV-311] Moved CheckBlockSanity to be method of BlockDAG, and removed subnetworkStore argument

* [DEV-311] Removed SubnetworkID as parameter to CheckBlockSanity

* [DEV-311] Update gas usage before

* [DEV-311] some chain=>DAG updates in comments

* [DEV-311] Removed remaining dag-specific parameters from checkBlockSanity
2019-01-24 17:25:11 +02:00
Evgeny Khirin
6960e2469a [DEV-212] Removed looping by extraNonce in cpuminer (#173)
* [DEV-212] Removed looping by extraNonce in cpuminer

* [DEV-212] Fixed build
2019-01-24 12:25:37 +02:00
Evgeny Khirin
b7850b382d [DEV-339] Check how mempool handles dependent transactions, see if there's any problem there (if so - fix it), and tell the team about it (#169)
* [DEV-339] Handling of dependent transactions in mempool

* [DEV-339] Small fixes after code review

* [DEV-339] Fixed compilation

* [DEV-339] Removed extra loop in addTransaction function

* [DEV-339] Changed addTransaction do not loop on inputs second time
2019-01-23 16:38:46 +02:00
Svarog
9507ed0a97 [DEV-351] Disallow unrequested transactions (#171) 2019-01-23 16:34:57 +02:00
stasatdaglabs
349e62fcd5 [DEV-332] Create partial block message in wire and make sure partial nodes receive partial blocks (#170)
* [DEV-332] Added MerkleProof to MsgBlock and rejected full-node/partial-block type misbehaviors.

* [DEV-332] Fixed merge issues.

* [DEV-332] Got rid of MerkleProof. Turns out we no longer need it.

* [DEV-332] Got rid of NTBlockDisconnected, as no one was ever triggering it. (It was part of reorg)

* [DEV-332] Implemented clearing out the payloads of transactions of outgoing blocks for partial nodes.

* [DEV-332] Extracted ConvertToPartial to its own method. Added a test. Added a condition for converting to a partial block.

* [DEV-332] Fixed bad ConvertToPartial condition.
2019-01-23 14:51:05 +02:00
Ori Newman
b963c0d364 Dev 334 make id merkle root and transaction id, fix tests, and add new tests (#166)
* [DEV-329] Add TxID

* [DEV-329] Change Transaction inputs to reference by tx id instead of tx hash

* [DEV-329] Fix tests

* [DEV-329] change txhash to txid in mempool

* [DEV-334] Make IDMerkleRoot

* [DEV-329] Add txid that excludes payload, gas and ScriptSigs (#158)

* [DEV-329] Add TxID

* [DEV-329] Change Transaction inputs to reference by tx id instead of tx hash

* [DEV-329] Fix tests

* [DEV-329] change txhash to txid in mempool

* [DEV-329] replace thinEncoding bool with txEncoding bitmask

* [DEV-329] Change txencoding var names

* [DEV-329] change txEncodingexcludeSignatureScript -> txEncodingExcludeSignatureScript

* [DEV-334] Add IDMerkleRoot to blocknode and recalculate IDMerkleRoot when extraNonce is changed

* [DEV-334] Fix tests

* [DEV-334] Fix tests

* [DEV-334] fix SubnetworkDAGCoin -> SubnetworkIDNative

* [DEV-334] Add ID() function to Coin interface and rename hash to txID in a few places

* [DEV-334] Add Root method for merkle root

* [DEV-334] add comment to dag.SubnetworkID()

* [DEV-334] fix serializeSize comment
2019-01-23 14:04:23 +02:00
stasatdaglabs
4be23bff07 [DEV-348] Don't send transactions of wrong sub-network to partial peers, and reject messages of such (#164)
* [DEV-348] Removed a couple of unused methods.

* [DEV-348] Implemented validating incoming transactions for bad partial transactions.

* [DEV-348] Added a (incomplete) filter for propogation of transactions.

* [DEV-348] Implemented filtering inventory by subnetwork.

* [DEV-348] Fixed broken tests.

* [DEV-348] Added test for non-zero payload partial transactions.

* [DEV-348] Added a comment for Config.SubnetworkID.

* [DEV-348] Fixed formatting.

* [DEV-348] Renamed isRemoteTransactionFull to shouldTxBeFull.

* [DEV-348] Added a check for invalid transaction in maybeAcceptTransaction. Added handling for native networks.

* [DEV-348] Fixed formatting.

* [DEV-348] Fixed a bug in transaction validation.

* [DEV-348] Rephrased a comment.

* [DEV-348] Extracted subnetwork compatibility to a method. Wrote a test for it.

* [DEV-348] Removed an unnecessary check over the native subnetwork.
2019-01-22 13:56:40 +02:00
Ori Newman
7e739a6430 [DEV-352] Fix phantom hack (#167) 2019-01-21 15:46:23 +02:00
stasatdaglabs
f00651c4e3 [DEV-353] Rename subnetwork to subnetworkID everywhere in the code except for the command line flag (#165)
* [DEV-353] Renamed subnetwork -> subnetworkID.

* [DEV-353] Renamed dagcoin -> native.
2019-01-20 16:22:25 +02:00
stasatdaglabs
10ee7df252 [DEV-333] Update version message to include sub-network (#163)
* [DEV-333] Added subnetwork to local peer version message.

* [DEV-333] Fixed broken references.

* [DEV-333] Added serialization/deserialization of the version message.

* [DEV-333] Added rejection for peers with wrong subnetwork.

* [DEV-333] Fixed bad comment.

* [DEV-333] Renamed ChainParams to DAGParams.

* [DEV-333] Fixed partial nodes disconnecting from full nodes.

* [DEV-333] chain -> DAG

* [DEV-333] Possibly fixed outbound msgVersion rules.

* [DEV-333] Combined the two incompatible subnetwork cases into one if.

* [DEV-333] Reformatted the condition.
2019-01-15 16:25:56 +02:00
Evgeny Khirin
3a5abb6584 [DEV-338] Remove provisional nodes, and panic in case there's an error after verifying the block is valid (#162)
* [DEV-338] Remove provisional nodes, and panic in case there's an error after verifying the block is valid

* [DEV-338] Improved deffered blockNode cleanup and cosmetic changes

* [DEV-338] Fixed dag.index.SetStatusFlags for parents + cosmetic changes

* [DEV-338] Fixed build

* [DEV-338] Fixed comments
2019-01-14 16:28:05 +02:00
Svarog
e3994cddac [DEV-347] Normalize the way 'subnetwork' is capitalized (#161) 2019-01-13 18:00:34 +02:00
Svarog
6b55950901 [DEV-345] Enforce requirement for transactions from 1(DAGCoin) or 2(sub network registry) sub network to have gas = 0 (#157)
* [DEV-345] Validate that gas and payload are 0 when required by sub-network

* [DEV-345] Remove check for txOut.Value < 0, since txOut.Value is a uint64

* [DEV-345] Added tests for CheckTransactionSanity

* [DEV-345] Remove checks for Gas and Payload validity in wire.MsgTx.Decode

* [DEV-345] Verify that payload in Gas sub-network is always 8 bytes (uint64).

* [DEV-345] Renamed tstCheck{Script/Rule}Error to check{Script/Rule}Error

* [DEV-345] Improved formatting
2019-01-13 11:01:43 +02:00
Mike Zak
1743877b66 Merge remote-tracking branch 'origin/dev-304-change-subnetworkid-to-hash-instead-of-running-number' 2019-01-10 17:23:45 +02:00
stasatdaglabs
5171d26bba [DEV-343] Change SubNetworkRegistry to use SubNetworkID as Hash160 (#156)
* [DEV-343] Made sub-network registry use subNetworkIDs.

* [DEV-343] Removed an unnecessary clone.

* [DEV-343] Renamed buildSubNetworkID to txToSubNetworkID. Broke out of a loop when it was known that no further processing is required. Handled error cases from dbGetNetwork separately from the "not-found" case/

* [DEV-343] Added an error case in GasLimit() for where the sub-network is nil.

* [DEV-343] Fixed return nil instead of err. Used a better way to check whether we should continue checking accepted transactions.
2019-01-10 17:13:11 +02:00
Evgeny Khirin
7195160219 [DEV-346] Added missed push into priority queue (#155) 2019-01-10 11:46:37 +02:00
Ori Newman
0553f7e2e7 [DEV-342] change subnetworkid to hash160 (#154)
* [DEV-342] Make subnetworkid Hash160

* [DEV-342] Fix tests

* [DEV-342] Fix rpcserver.go mismatched types

* [DEV-342] Change subnetworkhash type and package to subnetworkid

* [DEV-342] Add subnetworkid package
2019-01-09 17:55:25 +02:00
Evgeny Khirin
e76ce06acd [DEV-337] enusre transaction not overuse gas (#152)
* [DEV-312] Take in account subnetwork's GAS limit, when adding
transactions to block. Try to do that optimally.

* [DEV-312] Fixed GAS overusage calculation

* [DEV-337] Make sure that a transaction that uses more gas than the total allowed for sub-network

* [DEV-337] Moved transaction GAS check to mempool

* [DEV-337] Added Unit test for gas usage in transaction

* [DEV-337] Fixed build

* [DEV-337] Fixed tests stuff

* [DEV-337] Removed TODO comment
2019-01-09 15:54:48 +02:00
stasatdaglabs
41d1a08365 [DEV-326] Fixed ffldb tests broken in Windows (#153)
* [DEV-326] Worked around a parallelism issue in go test. Fixed a test where a file was being deleted while still open.

* [DEV-326] Worked around a timer-related oddity.

* [DEV-326] Fixed closing a file twice.

* [DEV-326] Using pdb.closed instead of an additional variable.
2019-01-09 13:41:40 +02:00
stasatdaglabs
cc9b8da351 [DEV-355] Disable all mining-related API calls on partial nodes (#151)
* [DEV-355] Disallowed mining-related RPC commands from running on any sub-network besides the "all" sub-network.

* [DEV-335] Fixed formatting errors.

* [DEV-335] Fixed some more formatting errors.

* [DEV-335] Changed error type to ErrRPCInvalidRequest and rephrased the error message.

* [DEV-335] Fixed formatting.

* [DEV-335] Removed error from getGenerate.
2019-01-09 12:54:43 +02:00
Evgeny Khirin
6feea90946 Dev 312 update miner logic to fit new rules (#150)
* [DEV-312] Take in account subnetwork's GAS limit, when adding
transactions to block. Try to do that optimally.

* [DEV-312] Fixed GAS overusage calculation

* [DEV-312] Prepare to merge with master

* [DEV-312] Fix after merging
2019-01-07 18:07:49 +02:00
stasatdaglabs
d33d633e77 [DEV-307] Create the sub-network registry (#147)
* [DEV-307] Implemented adding pending registry transactions.

* [DEV-307] Implemented a skeleton for the sub-network registry algorithm.

* [DEV-307] Implemented the serialization/deserialization logic for pending sub-network transactions.

* [DEV-307] Implemented marking sub-network registry transactions as successfully registered.

* [DEV-307] Implemented clearing pending sub-network entries and writing registered sub-networks.

* [DEV-307] Added comments for all the dagio functions.

* [DEV-307] Fixed a couple of bugs and added a test for checking serialization/deserialization of pending sub-network transactions.

* [DEV-307] Implemented getting a registered sub-network's gas limit.

* [DEV-307] Updated sub-network announcement transaction validation rules.

* [DEV-307] Specified what is validated.

* [DEV-307] Added initialization for lastSubNetworkID.

* [DEV-307] Renamed extractValidSubNetworkRegistryTxs to validateAndExtractSubNetworkRegistryTxs to better reflect what it does.

* Dev 303 implement block finality (#139)

* [DEV-303] Implement block finality

* [DEV-303] Add finality tests

* [DEV-303] Make finality tests use maybeAcceptBlock

* [DEV-303] Only check finality rules if we are fastAdd mode

* [DEV-303] replace hasBiggerFinalityScoreThan checks with .finalityScore()

* [DEV-307] Forgot to actually create the sub-network buckets.

* [DEV-307] Wrote a full-flow test for sub-network registry and fixed a couple of bugs that it had uncovered.

* [DEV-307] Took firstSubNetworkID out of dagconfig.Params. It's unnecessary there.

* [DEV-307] Added persistance for dag.lastSubNetworkID.

* [DEV-307] Moved sub-network stuff to their own files. Got rid of firstSubNetworkID. Replaced SubNetworkReservedLast with SubNetworkUnreservedFirst. Added ErrSubNetworkRegistry.

* [DEV-307] Added a newline at the end of dag_test.go.

* [DEV-307] Renamed previousFinalityPoint to initialFinalityPoint.

* [DEV-307] Moved sub-network IO stuff to subnetworks.go and subnetworks_test.go.

* [DEV-307] Merged dbPutRegisteredSubNetworkTx and dbRegisterSubNetwork. Fixed a too-big allocation and a block double-processing bug.

* [DEV-307] Simplified the serialized representation of a slice of transactions.

* [DEV-307] Fixed comments.
2019-01-07 17:10:23 +02:00
Svarog
ade33a94f3 [DEV-331] Disallowed node to run in reserved sub-networks (#148)
* [DEV-331] Disallowed node to run in reserved sub-networks

* [DEV-331] Added test to make sure that constant are not modified without modifying help text
2019-01-06 16:15:37 +02:00
Evgeny Khirin
0e65e82372 [DEV-315] Added tests for mempool (#149)
* [DEV-315] Added tests for mempool

* [DEV-315] Improved test readability
2019-01-06 15:45:39 +02:00
Ori Newman
75a6d65075 [DEV-340] Make connectToDAG return RuleErrors (#144)
* [DEV-340] Make connectToDAG return RuleErrors

* [DEV-340] Add comment to make the error source clearer
2019-01-03 16:13:32 +02:00
Svarog
53e8e5a10a [DEV-330] dagconfig coverage (#142)
* [DEV-314] Added tests for DisasmPC and DisasmScript

* [DEV-314] Re-wrote TestCheckErrorCondition to cover the whole method

* [DEV-314] Fixed error message

* [DEV-330] Covered all methods in daghash with tests

* [DEV-330] Added tests for NewHashFromString

* [DEV-330] Added test case for TestNewHashFromStr that is not the default daghash.Hash value
2019-01-03 14:38:51 +02:00
Ori Newman
c0aafdf7e1 [DEV-303] Implement block finality (#139)
* [DEV-303] Implement block finality

* [DEV-303] Add finality tests

* [DEV-303] Make finality tests use maybeAcceptBlock

* [DEV-303] Only check finality rules if we are fastAdd mode

* [DEV-303] replace hasBiggerFinalityScoreThan checks with .finalityScore()
2019-01-03 13:22:31 +02:00
Evgeny Khirin
f0f79b1fcc [DEV-308] Enforce tx order in block to be by sub-network ID (#141)
* [DEV-308] Enforce tx order in block to be by sub-network ID

* [DEV-308] Use mutable block in TestCheckBlockSanity

* [DEV-308] Fixed comment

* [DEV-308] Fixed TestCheckBlockSanity: use invalid block instead of modifying good block

* [DEV-308] Changed name of block in TestCheckBlockSanity
2019-01-02 12:26:34 +02:00
Svarog
90c29829e8 [DEV-328] Added test to fully cover store.blockFile() in single-cpu applications (#138) 2018-12-31 12:17:55 +02:00
Svarog
418805aed3 [DEV-314] improved txscript coverage (#137)
* [DEV-314] Added tests for DisasmPC and DisasmScript

* [DEV-314] Re-wrote TestCheckErrorCondition to cover the whole method

* [DEV-314] Fixed error message
2018-12-30 18:16:41 +02:00
Evgeny Khirin
6d035fce29 [DEV-325] Add SubNetwork command-line and config flag (#135)
* [DEV-325] Added subnetwork cmd/config flag

* [DEV-325] Fixed subnetwork comments
2018-12-30 11:25:43 +02:00
Ori Newman
4a6fd8fad1 [DEV-320] add decoupling related fields to wire tx (#134)
* [DEV-320] Add decoupling-related fields to wire.Tx

* [DEV-320] Fix SerializeSize for MsgTx

* [DEV-320] Fix indexers test

* [DEV-320] Fix utils test

* [DEV-320] Fix ffldb tests

* [DEV-320] Fix mining tests

* [DEV-320] fix txsort tests

* [DEV-320] change default subnetwork id to 1 and remove payload for default subnetwork id

* [DEV-320] Add msgtx serialization tests

* [DEV-320] get rid of temp prints, and change error messages
2018-12-27 17:43:25 +02:00
Mike Zak
fa8002a644 Added DAGLabs to license file 2018-12-27 13:31:52 +02:00
Evgeny Khirin
a45c01f9ab [DEV-323] fix one off bug in sign rfc6979 (#133) 2018-12-26 14:25:21 +02:00
Evgeny Khirin
4f1b8c1248 [DEV-315] Added test for mempool.HandleNewBlock (#129)
* [DEV-315] Added test for mempool.HandleNewBlock

* [DEV-315] Styling improvements

* [DEV-315] Changed names of blockTx variables
2018-12-26 13:17:07 +02:00
Evgeny Khirin
bcd66a43df [DEV-321] btcec/signature: fix DoS bug with signature parsing (#132)
* [DEV-321] btcec/signature: fix DoS bug with signature parsing

* [DEV-321] Added test for short signature
2018-12-26 12:40:28 +02:00
Evgeny Khirin
ca084f595e [DEV-322] Backport btcsuite/btcd PR: connmanager: check Addr for nil (#131)
* [DEV-322] connmanager: check Addr for nil

* [DEV-322] Added crash recovering in TestConnReqString

* [DEV-322] Fixed 'go vet' errors
2018-12-26 12:38:10 +02:00
Mike Zak
c45cef0ffe Typo fixes in comments 2018-12-24 16:19:51 +02:00
Ori Newman
ef01f6410d [DEV-301] implement blockNode.chainHeight as the height of the selectedParent chain
* [DEV-301] implement blockNode.chainHeight as the height of the selected parent chain

* [DEV-301] add description to TestChainHeight

* [DEV-301] Delete comment on TestChainHeight and expalain about the dag
2018-12-23 16:54:45 +02:00
Mike Zak
b522d6934d Added coverage.txt to .gitignore 2018-12-20 18:45:52 +02:00
Ori Newman
45c212deb4 [DEV-257] Include transaction acceptance status in getBlock RPC call (#123)
* [DEV-257] Include transaction acceptance status in getBlock RPC call

* [DEV-257] change txRawResult-accepted to camel case

* [DEV-257] gofmt

* [DEV-257] change getBlock-acceptedtx help entry to getBlock-acceptedTx

* [DEV-257] delete TxRawResult.accepted and instead create TxRawResult.acceptedBy
2018-11-08 13:19:53 +02:00
Svarog
225f349e6a [DEV-81] 100% Database Coverage (#110)
* [DEV-81] Overwrite maxOpenFiles for testInterface to force tests to check the LRU-mechanism in openFile

* [DEV-81] Added database.UseLogger test

* [DEV-81] Completed coverage of reconcileDB()

* [DEV-81] Added some tests for dbcache

* [DEV-81] Moved init and UseLogger to separate file to make them more easily-testable + added tests

* [DEV-81] Added tests for deleteFile

* [DEV-81] Added tests to cursor.Delete + made sure it returns error when transaction is not writable

* [DEV-81] Moved database/error_test.go from database_test package to database package + added test for IsErrorCode

* [DEV-81] Added tests for handleRollback error-cases

* [DEV-81] Added tests for cursor.skipPendingUpdates

* [DEV-81] Added tests for various cursor edge-cases

* [DEV-81] tx.putKey no longer returns error, because there is no case when it does

* [DEV-81] Added tests to CreateBucket error cases

* [DEV-81] Added tests to bucket.Get and .Delete error cases + .Delete now returns error on empty key

* [DEV-81] Added test for ForEachBucket

* [DEV-81] Added tests to StoreBlock

* [DEV-81] Added test for deleting a double nested bucket

* [DEV-81] Removed log_test, as it is no longer necessary with the logging system re-design

* [DEV-81] Added test to some of writePendingAndCommit error-cases

* [DEV-81] Update references from btcutil to btcd/util

* [DEV-81] Add tests for dbCacheIterator{.Next(), .Prev(), .Key, .Value()} in cases when iterator is exhausted

* [DEV-81] Added tests for ldbIterator placeholder functions

* [DEV-81] Added test name to Error messsages in TestSkipPendingUpdates

* [DEV-81] Begin writing TestSkipPendingUpdatesCache

* [DEV-81] Added error-cases for DBCache.flush() and DBCache.commitTreaps()

* [DEV-81] Use monkey.patch from bou.ke and not from github

* [DEV-81] Rewrote IsErrorCode in both database and txscript packages to be more concise

* [DEV-81] Rename any database.Tx to dbTx instead of tx - to remove confusion with coin Tx

* [DEV-81] Fix typo

* [DEV-81] Use os.TempDir() instead of /tmp/ to be cross-platform

* [DEV-81] use SimNet for database tests + Error if testDB exists after deleting it

* [DEV-81] Removed useLogger - it's redundant

* [DEV-81] Added comment on how CRC32 checksums are calculated in reconcile_test.go

* [DEV-81] Added comment that explains what setWriteRow does

* [DEV-81] Use constant instead of hard-coded value

* [DEV-81] Fixed some typo's + better formatting
2018-11-06 18:18:42 +02:00
Mike Zak
60afa37acf Update dockerfile to go 1.11 2018-11-06 15:51:40 +02:00
Mike Zak
46ac9a6320 Gofmt fixes 2018-11-06 15:45:00 +02:00
Ori Newman
d68877ce7b [DEV-269] Remove NumParentBlocks from master (#124) 2018-11-06 13:08:15 +02:00
Ori Newman
05d46e7c01 [DEV-244] Remove BlockHeader.NumParentBlocks and use a method instead (#121) 2018-11-06 11:26:59 +02:00
stasatdaglabs
8a234bf4a3 [DEV-242] Parameter names should be also camelCase in JSON-RPC (#118)
* [DEV-242] Modified some help functionality to convert to lowercase camel case instead of just lowercase.

* [DEV-242] Corrected help functionality for struct field names.

* [DEV-242] Corrected help functionality for struct names.

* [DEV-242] Cleaned up toLowercaseCamelCase.

* [DEV-242] Renamed toLowercaseCamelCase to toCamelCase.

* [DEV-242] Converted the rest of the stuff in rpcserverhelp.go to camelCase. Fixed a bug in the camelCase converter.

* [DEV-242] camelCase-ified the last few RPC parameter names.

* [DEV-242] Fixed an off-by-one bug in toCamelCase.

* [DEV-242] Changed back from "jsonRpc" to "jsonrpc".

* [DEV-242] Moved toCamelCase into utils, wrote unit tests for it, and fixed an off-by-one bug.

* [DEV-242] Re-exported DefaultHomeDir because it's required in windows_service.go.

* [DEV-242] Added a comment above DefaultHomeDir to satisfy golint.

* [DEV-242] Formatted config/config.go.
2018-11-05 18:50:55 +02:00
Ori Newman
7093155c3a [DEV-234] Add to txindex method to return the block in which tx was accepted (or indication it was not accepted) (#116)
* [DEV-234] add TxAcceptedInBlock and TxBlocks

* [DEV-234] test TxAcceptedInBlock and TxBlocks

* [DEV-234] test TxAcceptedInBlock and TxFirstBlockRegion

* [DEV-234] rename selectedPathSet to selectedPathChain

* [DEV-234] set indexers db as part of index manager initialization

* [DEV-234] remove redudant dag instance in txindex

* [DEV-234] fix TestTxIndexConnectBlock and add DAGParams as part of config in DAGSetup

* [DEV-234] TestTxIndexConnectBlock make K=1 to make calculations easier

* [DEV-234] rename TxAcceptingBlock to BlockThatAcceptedTx

* [DEV-234] update block fields names in txindex_test.go

* [DEV-234] rename selectedPathChain -> selectedPathChainSet
2018-11-05 16:11:54 +02:00
Svarog
4c2ce469aa [DEV-265] Fixed dockerfile to actually run golint and gofmt + fix gofmt errors (#122) 2018-11-05 14:29:33 +02:00
Ori Newman
9519b9f2a1 [DEV-211] Change fields of serialized blocks and transactions to match spec (#104)
* [DEV-211] change block fields

* [DEV-211] change block fields

* [DEV-211] change comments to adhere to the new block field names
2018-11-05 13:11:42 +02:00
Ori Newman
d70e2be641 [DEV-236] add counter to blockdag that will increment for each valid … (#115)
* [DEV-236] add counter to blockdag that will increment for each valid block that is connected to the dag

* [DEV-236] increment dag.blockCount while building block index in initDagState

* [DEV-236] changed dag.BlockCount to return an unsigned value
2018-11-05 12:58:37 +02:00
Svarog
35546b62d0 [DEV-240] Style fixes to satisfy golint (#112)
* [DEV-240] Unexport BlockDAG/SelectedTip, since it returns unexported *blockNode

* [DEV-240] Fix blockdag package comment to satisfy golint

* [DEV-240] Add comment explaining blank import of ffldb in blockdag/test_utils.go

* [DEV-240] Add comment to FullUTXOSet.Get

* [DEV-240] Unexported config.DefaultHomeDir

* [DEV-240] Remove blank import of ffldb from config/config.go

* [DEV-240] Added comment to daghash.Strings()

* [DEV-240] Added missing comments in hdkeychain/extendedkey.go

* [DEV-240] Re-activate goline in CI Dockerfile

* [DEV-240] Fixed some typos in comments

* [DEV-240] Typo fix in comment
2018-11-05 12:58:17 +02:00
Ori Newman
78e8c6084c [DEV-238] Remove addrlocal from getPeerInfo RPC call (#117) 2018-11-04 10:09:39 +02:00
Svarog
8721f0d03f [DEV-253] Remove dependancy on github.com/pkg/errors (#113) 2018-11-01 14:45:59 +02:00
Ori Newman
4951c0bee0 [DEV-260] Don't update parents on initBlockNode (#114)
* [DEV-260] Don't update parents on initBlockNode

* [DEV-260] move addNodeAsChildToParents to blockdag/common_test.go

* [DEV-260] update newProvisionalNode comment
2018-11-01 14:43:38 +02:00
Ori Newman
3ebded9ae7 [DEV-255] create checkConnectToPastUTXO and move the required functionalities to it from checkConnectBlock1)
* [DEV-255] create checkConnectToPastUTXO and move the required functionalities to it from checkConnectBlock

* [DEV-255] get rid of checkConnectBlock

* [DEV-255] rename pNode -> node

* [DEV-255] add comment to describe ErrWithDiff
2018-10-29 17:58:09 +02:00
Ori Newman
3ff2ef19e4 [DEV-254] Defer any monkey unpatching (#109) 2018-10-29 12:16:58 +02:00
stasatdaglabs
3ace16ad23 [DEV-134] Implement Continuous Integration (#105)
* [DEV-134] Implement Continuous Integration

Squashed commit:

[5e41d830] Dev 223 fix txindex (#100)

* [DEV-201] In handleGetBlockDAGInfo calculate difficulty by the tip with the lowest bits

* [DEV-202] Move VirtualBlock.GetUTXOEntry to BlockDAG

* [DEV-203] Move VirtualBlock.SelectedTip() to BlockDAG

* [DEV-203] Move VirtualBlock.SelectedTip() to BlockDAG

* [DEV-204] Unexport VirtualBlock() and add CalcMedianTime method for DAG

* [DEV-204] add explanation about difficulty in CurrentBits() comment

* [DEV-204] unexport VirtualBlock type

* [DEV-223] make applyUTXOChanges return pastUTXOResults

* [DEV-223] add bluestxdata for current block as well

* [DEV-223] re-design tx index

* [DEV-223] edit txindex comments

* [DEV-223] rename BluesTxData -> AcceptedTxData, and return from applyUTXOChanges only transactions that got accepted

* [DEV-223] add unit test for txindex

* [DEV-223] fix comments and unite blueTransaction and AcceptedTxData to one type

* [DEV-223] use bucket cursor for dbFetchFirstTxRegion

* [DEV-223] use the same cursor instance for dbFetchFirstTxRegion

* [DEV-223] write in dbFetchFirstTxRegion's comment that it returns the first block region

* [DEV-223] rename type BlueBlockTransaction to TxWithBlockHash

* [DEV-223] add named returned value for applyUTXOChanges

[4c95e293] [DEV-134] Made golint ignore the vendor directory.

[21736dbc] [DEV-134] Renamed ExampleBlockChain_ProcessBlock to ExampleBlockDAG_ProcessBlock to satisfy go vet.

[beea6486] [DEV-134] Removed pushing the built docker to a remove repository. That's unnecessary at this stage.

[bee911ed] [DEV-134] Made all precompilation checks run on everything instead of only the root dir.

[585f92ae] [DEV-134] Added "github.com/pkg/errors" to dep.

[5f02f570] [DEV-134] -vendor-only is written with only one hyphen.

[3eee7f95] [DEV-134] go vet instead of go tool vet.

[0c2d4343] [DEV-134] Split all the pre-compile checks to separate lines to be able to tell which of them is failing.

[780519c8] [DEV-134] Ran gofmt on everything.

[8247146b] Dev 223 fix txindex (#100)

* [DEV-201] In handleGetBlockDAGInfo calculate difficulty by the tip with the lowest bits

* [DEV-202] Move VirtualBlock.GetUTXOEntry to BlockDAG

* [DEV-203] Move VirtualBlock.SelectedTip() to BlockDAG

* [DEV-203] Move VirtualBlock.SelectedTip() to BlockDAG

* [DEV-204] Unexport VirtualBlock() and add CalcMedianTime method for DAG

* [DEV-204] add explanation about difficulty in CurrentBits() comment

* [DEV-204] unexport VirtualBlock type

* [DEV-223] make applyUTXOChanges return pastUTXOResults

* [DEV-223] add bluestxdata for current block as well

* [DEV-223] re-design tx index

* [DEV-223] edit txindex comments

* [DEV-223] rename BluesTxData -> AcceptedTxData, and return from applyUTXOChanges only transactions that got accepted

* [DEV-223] add unit test for txindex

* [DEV-223] fix comments and unite blueTransaction and AcceptedTxData to one type

* [DEV-223] use bucket cursor for dbFetchFirstTxRegion

* [DEV-223] use the same cursor instance for dbFetchFirstTxRegion

* [DEV-223] write in dbFetchFirstTxRegion's comment that it returns the first block region

* [DEV-223] rename type BlueBlockTransaction to TxWithBlockHash

* [DEV-223] add named returned value for applyUTXOChanges

[bff68aa3] [DEV-134] Gave executable permission to deploy.sh

[638a99d9] [DEV-134] Added jenkinsfile and deploy script.

* [DEV-134] Added a robust testing script.

* [DEV-134] Fixed a bash-ism.

* [DEV-134] Disabled testing with coverage for now.

* [DEV-134] Disabled golint and removed removing debug symbols.

* [DEV-134] Disabled aligncheck.

* [DEV-134] Disabled structcheck and varcheck.

* [DEV-134] Added "don't inline functions" to compiler flags for testing.

* [DEV-134] Made build fail if gofmt prints out anything.

* [DEV-134] Fixed misleading comment.

* [DEV-134] Added comments to test.sh.

* [DEV-134] Renamed tm to measure_runtime and removed do_ prefixes from functions.

* [DEV-134] Fixed gofmt line in build script.

* [DEV-134] Fixed gofmt some more.

* [DEV-134]  Fixed gofmt not actually failing due to logical or.
2018-10-25 18:58:41 +03:00
Ori Newman
c4a541d093 [DEV-91] Increase mempool test coverage part 2 (#107)
* [DEV-91] add TestAddrIndex

* [DEV-91] add TestFeeEstimator

* [DEV-91] rename TestFeeEstimator -> TestFeeEstimatorCfg

* [DEV-91] added TestCount

* [DEV-91] add TestExtractRejectCode in mempool_test.go

* [DEV-91] get rid of fakeErr type and defer unpatching monkey patch

* [DEV-91] use the result of monkey.patch to unpatch
2018-10-25 18:13:02 +03:00
Evgeny Khirin
499adbf046 DEV-222: Changed type of TxOut.Value and util.Amount to uin64 (#108)
* [DEV-222] Changed type of TxOut.Value and util.Amount to uin64

* [DEV-222] Replaced math.MaxUint64 with 0

* [DEV-222] Fixed comment to reflect uint64 instead of int64

* [DEV-222] Fixed overflow comment
2018-10-24 16:29:50 +03:00
stasatdaglabs
ac01babfb1 [DEV-213] Converted all RPC commands to lowercaseCamelCase. (#106) 2018-10-23 12:24:57 +03:00
Ori Newman
5e41d83015 Dev 223 fix txindex (#100)
* [DEV-201] In handleGetBlockDAGInfo calculate difficulty by the tip with the lowest bits

* [DEV-202] Move VirtualBlock.GetUTXOEntry to BlockDAG

* [DEV-203] Move VirtualBlock.SelectedTip() to BlockDAG

* [DEV-203] Move VirtualBlock.SelectedTip() to BlockDAG

* [DEV-204] Unexport VirtualBlock() and add CalcMedianTime method for DAG

* [DEV-204] add explanation about difficulty in CurrentBits() comment

* [DEV-204] unexport VirtualBlock type

* [DEV-223] make applyUTXOChanges return pastUTXOResults

* [DEV-223] add bluestxdata for current block as well

* [DEV-223] re-design tx index

* [DEV-223] edit txindex comments

* [DEV-223] rename BluesTxData -> AcceptedTxData, and return from applyUTXOChanges only transactions that got accepted

* [DEV-223] add unit test for txindex

* [DEV-223] fix comments and unite blueTransaction and AcceptedTxData to one type

* [DEV-223] use bucket cursor for dbFetchFirstTxRegion

* [DEV-223] use the same cursor instance for dbFetchFirstTxRegion

* [DEV-223] write in dbFetchFirstTxRegion's comment that it returns the first block region

* [DEV-223] rename type BlueBlockTransaction to TxWithBlockHash

* [DEV-223] add named returned value for applyUTXOChanges
2018-10-21 16:01:22 +03:00
stasatdaglabs
d5787954ee [DEV-167] Create full docker-based environment for client team testing (#78)
* [DEV-167] Created Dockerfile, dockerignore, and docker-compose.

* [DEV-167] Updated docker-compose to use remote image.

* [DEV-167] Added --addrindex to docker-compose.

* [DEV-167] Switched to testnet and plugged in the correct address.

* [DEV-167] Removed the third and fourth nodes from docker-compose.

* [DEV-167] Exposed RPC port, added rpcuser and rpcpass.

* [DEV-167] Wrangled RPC stuff into shape. Moved docker stuff into btcd/docker.

* [DEV-167] Moved dockerignore back to root. Corrected path for rpc.cert and rpc.key.

* [DEV-167] Added Jenkins job stuff.

* [DEV-167] Added deploy.sh.

* [DEV-167] Removed .travis.yaml and a couple of files that shouldn't be in this branch.
2018-10-18 11:49:11 +03:00
Ori Newman
201d7e0207 [DEV-226] Fix sorting of hashes in blockset.hashes() (#98)
* [DEV-226] Fix sorting of hashes in blockset.hashes()

* [DEV-226] add TestHashes
2018-10-17 15:35:56 +03:00
Ori Newman
3deaea6c15 [DEV-224] Fix getblockcount RPC command to show real data (#97)
* [DEV-224] Fix getblockcount RPC command to show real data

* [DEV-227] provisionalNode.commit() should not update the original parents if the provisionalNode was created with withRelatives=false

* [DEV-224] remove debug info from TestBlockCount and BlockDAG.BlockCount()
2018-10-17 15:34:10 +03:00
Ori Newman
85d221f322 [DEV-227] provisionalNode.commit() should not update the original parents if the provisionalNode was created with withRelatives=false (#96) 2018-10-17 11:41:36 +03:00
Ori Newman
9ea9098fbc [DEV-204] Unexport VirtualBlock (#93)
* [DEV-201] In handleGetBlockDAGInfo calculate difficulty by the tip with the lowest bits

* [DEV-202] Move VirtualBlock.GetUTXOEntry to BlockDAG

* [DEV-203] Move VirtualBlock.SelectedTip() to BlockDAG

* [DEV-203] Move VirtualBlock.SelectedTip() to BlockDAG

* [DEV-204] Unexport VirtualBlock() and add CalcMedianTime method for DAG

* [DEV-204] add explanation about difficulty in CurrentBits() comment

* [DEV-204] unexport VirtualBlock type
2018-10-16 15:21:18 +03:00
Ori Newman
c3316715ae [DEV-225] Always maintain a block set of selected parents chain. (#99)
* [DEV-225] Always maintain a block set of selected parents chain.

* [DEV-225] Add comments

* [DEV-225] remove oldSelectedParent != nil condition

* [DEV-225] move updating selected path logic to updateSelectedPathSet

* [DEV-225] fix typo in comment
2018-10-16 12:12:24 +03:00
Ori Newman
29a7d66d81 [DEV-219] Remove testnet and mainnet checkpoints (#94)
* [DEV-219] remove testnet checkpoints

* [DEV-219] remove mainnet checkpoints
2018-10-15 17:00:40 +03:00
Ori Newman
0bb2030200 [DEV-220] Fix NTBlockAccepted bug (#95) 2018-10-15 16:52:19 +03:00
Ori Newman
65788d4dc0 [DEV-200] convert nonce to uint64 (#90) 2018-10-15 15:17:41 +03:00
Ori Newman
695c0e5a68 [DEV-206] allow block timestamp to be equal to past median time (#89)
* [DEV-206] allow block timestamp to be equal to past median time

* [DEV-206] make more specific error messages

* [DEV-206] make independent nodes in TestPastMedianTime
2018-10-14 15:33:36 +03:00
Ori Newman
b0aaa79ad0 [DEV-195] Implement addmanualnode, getallmanualnodesinfo, getmanualnodeinfo and removemanualnode RPC-API calls (#87)
* [DEV-195] Implement addmanualnode, getallmanualnodesinfo, getmanualnodeinfo and removemanualnode RPC-API calls

* [DEV-195] fix wrong types for handleRemoveManualNode and handleGetManualNodeInfo

* [DEV-195] fix addednode terminology to manualnode

* [DEV-195] add default values to details and onetry

* [DEV-195] fix comments
2018-10-11 12:38:54 +03:00
Ori Newman
bb10f8484c [DEV-199] Apply the minimum-if-policy for all transactions (#88)
* [DEV-199] apply minimum if

* [DEV-199] refactor popIfBool

* [DEV-199] use popIfBool in OP_NOTIF
2018-10-11 11:21:14 +03:00
Ori Newman
030469f035 [DEV-149] Add test case for CVE-2018-17144 (Bitcoin DoS/Double Spend bug) (#77)
* [DEV-149] Add test case for CVE-2018-17144 (Bitcoin DoS/Double Spend bug)

* [DEV-149] change t.Errorf + return to t.Fatalf

* [DEV-149] fix malformed blocks

* [DEV-149] change test blocks to use simnet genesis
2018-10-09 15:40:00 +03:00
Svarog
45e0e6707b [DEV-192] write 0 into indexesBucket when creating indexers to make mark indexer as initialized (#86) 2018-10-08 18:13:33 +03:00
Mike Zak
faf5efa455 [DEV-189] Fixed blockSet.highest() case when highest==nil 2018-10-08 18:03:58 +03:00
Ori Newman
ab7d9f2fa7 [DEV-170] Get rid of utxo collection functions (#79) 2018-10-08 15:51:20 +03:00
stasatdaglabs
6b302dad7c [DEV-92] Ensure 100% coverage in blockdag package (#71)
* [DEV-92] Covered lookupPreviousNodes in tests.

* [DEV-92] Covered accept.go in tests.

* [DEV-92] Fixed a typo.

* [DEV-92] Covered blockindex.go in tests.

* [DEV-92] Replaced Errorf + return with Errorf. Added the test case to the error messages.

* [DEV-92] Fixed grammar in a comment.

* [DEV-92] Split casting error checking and ErrorCode checking into separate tests.

* [DEV-92] Improved errors.

* [DEV-92] Made errors a tiny bit more descriptive.
2018-10-08 13:02:15 +03:00
Ori Newman
d414ce5522 [DEV-165] Implement pastMedianTimeCalculation (#81) 2018-10-08 12:24:30 +03:00
Ori Newman
d9a81b2a84 [DEV-193] Invert previous blocks order to be from low hash to high hash (#84)
* [DEV-189] invert arguments order or daghash.Less

* [DEV-189] invert arguments order of daghash.Less in blockset.highest

* [DEV-189] change to equivalent condition in Hash.Less to make it prettier

* [DEV-194] change prevblocks order to be from low hash to high hash
2018-10-08 12:14:18 +03:00
Ori Newman
0643b0d920 [DEV-189] invert arguments order of daghash.Less (#83)
* [DEV-189] invert arguments order or daghash.Less

* [DEV-189] invert arguments order of daghash.Less in blockset.highest

* [DEV-189] change to equivalent condition in Hash.Less to make it prettier
2018-10-07 16:44:29 +03:00
Ori Newman
3c88184b38 [DEV-169] add to BlockDAG TipHashes() and HighestTipHash() and remove Tiphashes() and SelectedTipHash from VirtualBlock (#82)
* [DEV-169] add to BlockDAG TipHashes() and HighestTipHash() and remove Tiphashes() and SelectedTipHash from VirtualBlock

* [DEV-169] move highest node logic to separate method of blockset
2018-10-07 13:07:30 +03:00
Ori Newman
2f3e0a609c [DEV-150] Created separate lock for UTXOSet, and write lock it only when calling meldToBase() (#76)
* [DEV-150] refactor utxo set locks

* [DEV-150] meldToBase inside new function updateVirtualUTXO
2018-10-04 17:48:50 +03:00
Ori Newman
ca7df552a6 [DEV-168] use dag.Height instead of VirtualBlock.Height() and VirtualBlock().SelectedTipHeight() (#75) 2018-10-04 15:22:29 +03:00
Ori Newman
df23d87503 [DEV-164] add bluest parent (#74)
* [DEV-164] use bluestParent from time calculations

* [DEV-164] use bluestParent next difficulty calculations

* [DEV-164] get rid of blockset.first()
2018-10-04 10:35:22 +03:00
Yuri
29b9f6a9b2 Change glide to dep in README.md (#80) 2018-10-04 10:13:22 +03:00
Ori Newman
c9dadfef1b [DEV-166] fix endless loop in validate parents (#73)
* [DEV-66] fix endless loop in validateParents

* [DEV-166] add TestValidateParents

* [DEV-166] use generateNode in TestValidateParents to avoid repetition

* [DEV-166] use blockDAG.genesis instead of blockDAG.virtual.SelectedTip()
2018-09-30 13:30:55 +03:00
Ori Newman
b2648cf1fd [DEV-132] change TargetTimePerBlock to 10 seconds (#66)
* [DEV-132] change TargetTimePerBlock to 10 seconds

* [DEV-132] change pow limits to regtest limits in all networks, enable cpu mining in testnet, and get rid of dns seeds in testnet
2018-09-30 12:34:08 +03:00
Ori Newman
18f54d13a4 [DEV-126] Get rid of tfSpent in txoFlags (#69) 2018-09-30 12:30:40 +03:00
Svarog
fae411706e [DEV-137] calculate height correctly in blockdag and mining packages (#72)
* [DEV-137] calculate height correctly in blockdag and mining packages

* [DEV-137] Fix tests that didn't set block height
2018-09-30 11:16:51 +03:00
Svarog
c8461dfcba [DEV-137] Calculate height correctly in blockdag and mining packages (#67) 2018-09-27 14:24:04 +03:00
Ori Newman
b348449f01 [DEV-91] Ensure 100% coverage in mempool package (#70)
* [DEV-91] add tests for RemoveOrphansByTag

* [DEV-91] Add tests for orphan tx expiration

* [DEV-91] add TestDoubleSpends

* [DEV-91] add TestFetchTransaction

* [DEV-91] added a test for MinHighPriority

* [DEV-91] test unparsable scripts

* [DEV-91] test MaxOrphanTxs=0

* [DEV-91] add TestRemoveTransaction

* [DEV-105] use utxodiff in mempool instead of utxoview

* [DEV-105] get rid of utxoview

* [DEV-105] fix tests to use utxoset

* [DEV-105] remove utxoview type

* [DEV-105] move DagSetup to test_utils.go

* [DEV-105] add comments and add blockdag/test_utils_test.go

* [DEV-105] change checkPoolDoubleSpend to check utxodiff

* [DEV-105] add restoreInputs arg to removeTransaction

* [DEV-91] add restoreInputs arg to removeTransaction in unit tests

* [DEV-105] add restoreInputs arg to removeTransaction

* [DEV-105] give more descriptive names to vars

* [DEV-115] close txChan outside of HandleNewBlock

* [DEV-105] rename DagSetup -> DAGSetup

* [DEV-91] remove IsSpentInDiff

* [DEV-91] fix comment

* [DEV-91] Make IsPushOnlyScript return an error if the script cannot be parsed

* [DEV-91] add more tests for IsPushOnlyScript

* [DEV-91] fix comments

* [DEV-91] fix NewAddressPubKeyHash
2018-09-27 14:11:26 +03:00
Ori Newman
1cd82fcd3b [DEV-91] Ensure 100% coverage in mempool package (#60)
* [DEV-91] add tests for RemoveOrphansByTag

* [DEV-91] Add tests for orphan tx expiration

* [DEV-91] add TestDoubleSpends

* [DEV-91] add TestFetchTransaction

* [DEV-91] added a test for MinHighPriority

* [DEV-91] test unparsable scripts

* [DEV-91] test MaxOrphanTxs=0

* [DEV-91] add TestRemoveTransaction

* [DEV-105] use utxodiff in mempool instead of utxoview

* [DEV-105] get rid of utxoview

* [DEV-105] fix tests to use utxoset

* [DEV-105] remove utxoview type

* [DEV-105] move DagSetup to test_utils.go

* [DEV-105] add comments and add blockdag/test_utils_test.go

* [DEV-105] change checkPoolDoubleSpend to check utxodiff

* [DEV-105] add restoreInputs arg to removeTransaction

* [DEV-91] add restoreInputs arg to removeTransaction in unit tests

* [DEV-105] add restoreInputs arg to removeTransaction

* [DEV-105] give more descriptive names to vars

* [DEV-115] close txChan outside of HandleNewBlock

* [DEV-105] rename DagSetup -> DAGSetup

* [DEV-91] remove IsSpentInDiff

* [DEV-91] fix comment

* [DEV-91] Make IsPushOnlyScript return an error if the script cannot be parsed

* [DEV-91] add more tests for IsPushOnlyScript

* [DEV-91] fix comments
2018-09-27 12:09:19 +03:00
Ori Newman
81453b221d [DEV-128] Fix BTCD sync (#65)
* [DEV-115] fix mistype mainCfg -> cfg in config/config.go

* [DEV-115] add config log

* [DEV-115] always handle block announcments

* [DEV-128] delete indexer tips bucket

* [DEV-128] disable cfilters by default

* [DEV-128] get rid of dbIndexDisconnectBlock and dbIndexConnectBlock

* [DEV-128] fix NTBlockConnected to send a block instead of TipHashes
2018-09-25 12:06:22 +03:00
stasatdaglabs
6dd3b815c1 [DEV-17] Move Bech32Prefix-related code from btcd/chaincfg/params.go to btcdutil (#62)
* [DEV-17] Moved Bech32 stuff from params.go to address.go.

* [DEV-17] Removed dagconfig dependency from address_test.go.

* [DEV-17] Removed dagconfig dependency from builder_test.go.

* [DEV-17] Removed dagconfig dependency from internal_test.go.

* [DEV-17] Removed dagconfig dependency from wif.go.

* [DEV-17] Removed dagconfig dependency from externdedkey.go.

* [DEV-17] Fixed compilation errors outside of hdkeychain.

* [DEV-17] Resolved circular dependencies.

* [DEV-17] Fixed failing tests.

* [DEV-17] Renamed DagXxx to Bech32PrefixDAGXxx

* [DEV-17] Fixed register_test.go.

* [DEV-17] Renamed HDKeyIDPairXxxNet to XxxNetHDKeyPair.

* [DEV-17] Renamed IsForNet to IsForPrefix.
2018-09-23 15:49:50 +03:00
Mike Zak
9b5aa66e5b Merge branch 'master' of github.com:daglabs/btcd 2018-09-23 15:44:58 +03:00
Ori Newman
e5545e2ee6 [DEV-130] convert searchrawtransaction ints to bools (#61) 2018-09-23 11:30:47 +03:00
Ori Newman
d739bf5034 [DEV-129] switch the locations in json between reqsigs and type (#63) 2018-09-23 11:27:38 +03:00
Ori Newman
21e9fde74d [DEV-127] remove getwork rpc command 2018-09-23 11:23:56 +03:00
Ori Newman
5fb220c38a Dev 105 get rid of utxoview (#58)
* [DEV-105] use utxodiff in mempool instead of utxoview

* [DEV-105] get rid of utxoview

* [DEV-105] fix tests to use utxoset

* [DEV-105] remove utxoview type

* [DEV-105] move DagSetup to test_utils.go

* [DEV-105] add comments and add blockdag/test_utils_test.go

* [DEV-105] add restoreInputs arg to removeTransaction

* [DEV-105] give more descriptive names to vars

* [DEV-115] close txChan outside of HandleNewBlock

* [DEV-105] rename DagSetup -> DAGSetup
2018-09-17 11:47:35 +03:00
Ori Newman
85265e1d9b [DEV-115] Make sure BTCD Node runs
* [DEV-115] fix mistype mainCfg -> cfg in config/config.go

* [DEV-115] add config log
2018-09-13 15:24:46 +03:00
Mike Zak
f137442c79 Merge branch 'master' of github.com:daglabs/btcd 2018-09-04 12:00:29 +03:00
Mike Zak
c1edbf7f20 Update Gopkg.lock to latest versions of dependencies 2018-09-04 12:00:15 +03:00
Ori Newman
86feb42cc4 [DEV-104] Disable chained txs (#57)
* [DEV-104] Disable chained transactions

* [DEV-104] add TestApplyUTXOChanges

* [DEV-104] remove isCompatible

* [DEV-104] add TestDiffFromTx

* [DEV-104] reorder variables in TestApplyUTXOChanges

* [DEV-104] rename node -> containgNode in diffFromTx
2018-09-03 16:57:23 +03:00
stasatdaglabs
37cd482db3 [DEV-75] Update UTXO model to work with Diffs (#53)
* [DEV-75] Removed fetchEntryByHash, which was no longer used.

* [DEV-75] Removed a few functions in manager.go that weren't used by anything.

* [DEV-75] checkConnectBlock will soon not accept a utxoViewpoint, so removed the call to view.Tip() so that it could be deleted.

* [DEV-75] Got rid of tips in UtxoViewpoint.

* [DEV-75] Added the full UTXO set to the virtual block.

* [DEV-75] Implemented UTXO-wrangling when adding a new block.

* [DEV-75] Added isCoinbase to utxoEntry creation.

* [DEV-75] Added blockHeight to utxoEntry creation.

* Implemented fetching the fullUTXOSet from the database.

* [DEV-75] Simplified DAGState because almost all of the state in it was unnecessary. Also got rid of dbDAGState.

* [DEV-75] Made the process around adding a new block, UTXO-wise, much safer.

* [DEV-75] Implemented melding the virtual UTXO diff to the database.

* [DEV-75] Fixed utxoSet loading from the wrong bucket.

* [DEV-75] Began pruning utxoviewpoint.go. Replaced FetchUtxoEntry with a fullUTXOSet-based GetUTXOEntry.

* [DEV-75] Removed fetchUtxos.

* [DEV-75] Moved GetUTXOEntry into the virtual block.

* [DEV-75] Updated IndexManager to not use utxoViewpoints.

* [DEV-75] Fixed some bad login in restoreUTXO involving nodeDiffs.

* [DEV-75] Got rid of the UTXO spend journal, which wasn't used anywhere.

* [DEV-75] Moved some STXO-related validation logic out of connectToDAG and into checkConnectBlock.

* [DEV-75] Renamed UtxoXxx to UTXOXxx. Removed a bunch of methods that were no longer used by anything.

* [DEV-75] Another Utxo -> UTXO rename.

* [DEV-75] Removed IsModified from UTXOView, which was not used anywhere.

* [DEV-75] Renamed nodeDiff to provisionalNode. Added a bunch of comments.

* [DEV-75] Removed the test for genesis in pastUTXO, since it can never happen.

* [DEV-75] Wrote tests for errors in pastUTXO.

* [DEV-75] Wrote tests for errors in restoreUTXO.

* [DEV-75] Improved testErrorThroughPatching.

* [DEV-75] Wrote tests for errors in verifyAndBuildUTXO.

* [DEV-75] Used TipHashes instead of tips().hashes(), fixed comments in a few places, and brought back an error return that I erroneously removed.

* [DEV-75] Removed UTXO set logs.

* [DEV-75] Recreated add/remove/contains functions for utxoCollection.

* [DEV-75] Changed newUTXOEntry to use an object initializer.

* [DEV-75] Changed the UTXO bucket version to 1.

* Added a comment that clarifies that the index is not initialized before initDAGState is called.

* [DEV-75] Renamed GetVirtualBlock to VirtualBlock.

* [DEV-75] Removed superfluous variables.

* [DEV-75] Combined connectBlockToParents with updateParentsDiffs.

* [DEV-75] Removed another superfluous variable.

* [DEV-75] In pastUTXO, first fetch transactions from the database and only then add them.

* [DEV-75] Reworded the comment for commit().

* [DEV-75] Made all the connectUTXO subfunctions not BlockDAG methods.

* [DEV-75] Updated the comment for connectUTXO.

* [DEV-75] Updated the comment explaining why we're creating provisionalNodes.

* [DEV-75] Removed a couple of unnecessary calls to toProvisionalNode.

* [DEV-75] Renamed connectUTXO to applyUTXOChanges.

* [DEV-75] Replaced allProvisionalNodes with provisionalNodeSet, an object that holds provisionalNodes for this particular call to applyUTXOChanges.

* [DEV-75] Changed createProvisionalNode to accept a boolean "withParents" instead of relying on the caller to supply the node's parents or an empty set.

* [DEV-75] Made most applyUTXOChanges subfunctions be methods on provisionalNode.

* [DEV-75] Fixed a couple of bad comments.

* [DEV-75] Added descriptive error messages to callers of applyUTXOChanges.

* [DEV-75] Fixed weird English.

* [DEV-75] Replaced DAGState with a slice of DAG tip hashes.

* [DEV-75] In createProvisionalNode, changed withParents to withRelatives to avoid certain kinds of attacks.

* [DEV-75] Renamed createdProvisionalNode to newProvisionalNode.

* [DEV-75] Added precalculating the amount of transactions inside a new block's blue set.

* [DEV-75] Pruned unnecessary variable.
2018-08-30 16:00:03 +03:00
Ori Newman
9bb40e1085 [DEV-107] Disallow situation where a parent is also an ancestor of another parent
* [DEV-107] Disallow situation where a parent is also an ancestor of another parent

* [DEV-107] Add comment for validateParents

* [DEV-107] move validateParents to checkBlockContext

* [DEV-107] break validateParents error to 2 lines

* [DEV-107] remove TestProcessBlock

* [DEV-107] fix comment that explains block 3c test

* [DEV-107] remove blk_3C from TestCheckConnectBlockTemplate
2018-08-30 11:20:29 +03:00
stasatdaglabs
e149a50f22 [DEV-101] Renamed server/rpcserver to server/rpc. (#55) 2018-08-29 15:20:42 +03:00
stasatdaglabs
b2d3ec69d8 [DEV-102] Fix logging in database drivers
[DEV-102] remove UseLogger from ffldb
2018-08-29 10:28:46 +03:00
Ori Newman
25d5ade7a8 [DEV-102] remove UseLogger from ffldb 2018-08-28 10:59:34 +03:00
Ori Newman
617530dd8f [DEV-76] handleGetBlockTemplateLongPoll to use tips instead of tip (#52)
* [DEV-76] handleGetBlockTemplateLongPoll to use tips instead of tip

* [DEV-76] move concatHashesToString to  daghash package as JoinHashesStrings

* [DEV-76] use strings.Join for JoinHashesStrings

* [DEV-76] rename templateID -> longPollID

* [DEV-76] add function prefix to errors
2018-08-27 17:32:53 +03:00
Ori Newman
b4b71eec01 [DEV-98] Move script flags from relay rules to consensus (#49)
* [DEV-98] Move script flags from relay rules to consensus

* [DEV-98] remove flags from script_tests.json

* [DEV-98] fix multisig and remove test that assume no minimal data rule

* [DEV-98] rename bip16 bool to isP2sh

* [DEV-98] add sighash type to overly long signature in script_tests.json

* [DEV-98] add test for NUMEQUAL for non equal numbers script_tests.json

* [DEV-98] remove debugging if

* [DEV-98] remove ErrCleanStack from EVAL_FALSE

* [DEV-98] change isP2sh to isP2SH to comply with Go style

* [DEV-98] add ScriptNoFlags to explictly indicate for empty ScriptFlags

* [DEV-98] rename ErrPubKeyType -> ErrPubKeyFormat

* [DEV-98] rename PUBKEYTYPE -> PUBKEYFORMAT
2018-08-23 17:45:49 +03:00
stasatdaglabs
c65d9aa168 [DEV-97] Combine btcd and btcutil repositories (#51)
* [DEV-97] Moved github.com/daglabs/btcutil into github.com/daglabs/btcd/btcutil.

* [DEV-97] Updated Gopkg.toml to no longer refer to btcutil.

* [DEV-97] Renamed btcutil to util.
2018-08-23 16:54:16 +03:00
stasatdaglabs
9438bd0d2e [DEV-93] Backported 'peer: ensure the version negotiation goroutine will always exit'. (#50)
Original PR: https://github.com/btcsuite/btcd/pull/1228/files
2018-08-23 15:25:58 +03:00
Ori Newman
ce35aabb70 [DEV-88] Empty out btcd/blockdag/upgrade.go 2018-08-20 15:38:35 +03:00
Ori Newman
4b78fd007d [DEV-84] validate block parents order
* [DEV-84] validate block parents order

* [DEV-84] change AreEqual to look for order equality as well
2018-08-14 16:15:12 +03:00
Ori Newman
6475e03b93 [DEV-68] Backport 'blockchain: Backport optimize exported header access' 2018-08-14 15:53:48 +03:00
Ori Newman
176aad5dcd [DEV-94] Always reply with get headers 2018-08-14 15:48:29 +03:00
Ori Newman
d9abfa0fd3 [DEV-68] Revert block version to 1 everywhere
* revert block version to 1

* [DEV-68] remove dersig flag

* [DEV-68] get rid of vbLegacyBlockVersion

* [DEV-68] remove isBIP0030Node
2018-08-13 12:35:20 +03:00
Ori Newman
56fb7f09c1 [DEV-82] Take RPC Server out of btcd package
* [DEV-82] break down main to packages

* [DEV-82] separate rpcserver and server

* [DEV-82] Fixed Windows-related code that failed to compile.

* [DEV-82] remove params.go and use only dagconfig.Params

* [DEV-82] fix log.go license
2018-08-12 16:24:43 +03:00
Ori Newman
713b01c69d [DEV-86] Update BlockDAG.initDAGState to load the whole dag
* [DEV-86] remove selectedPrevBlock from initDAGState

* [DEV-86] re-implement dagview to be dag compatible

* [DEV-86] change comments
2018-08-12 12:07:28 +03:00
stasatdaglabs
460216be65 [DEV-89] Convert DAGView into VirtualBlock (#41)
* [DEV-74] Implemented and written tests for utxoIterator.

* [DEV-74] Improved utxoIterator tests.

* [DEV-74] Implemented utxoCollection tests.

* [DEV-74] Implemented utxoDiff and its tests.

* [DEV-74] Implemented utxoSet.

* [DEV -74] Added tests for fullUTXOSet.

* [DEV-74] Added some tests for diffUTXOSet.

* [DEV-74] Wrote tests for diffUTXOSet iterator.

* [DEV-74] Added a negative test for diffUTXOSet.withDiff.

* [DEV-74] Wrote tests for addTx.

* [DEV-74] Wrote a toRemove test for addTx.

* [DEV-74] Changed blockNode.utxoDiff to be of type utxoDiff.

* [DEV-74] Removed superfluous whitespace.

* [DEV-74] Renamed confusing "previousHash" to "hash".

* [DEV-74] Fixed bad test and corrected failing test.

* [DEV-74] Moved confusing "negatives" test to be part of the general utxoCollection test.

* [DEV-74] Removed utxoDiff.inverted.

* [DEV-74] Renamed blockNode.utxoDiff to blockNode.diff.

* [DEV-74] Renamed diff to diffFrom for clarity's sake.

* [DEV-74] Converted the type of utxoCollection from map[daghash.Hash]map[uint32]*wire.TxOut to map[wire.OutPoint]*UtxoEntry.

* [DEV-74] Corrected test names in utxoCollection_test.

* [DEV-74] Removed superfluous utxoCollection iterator and moved utxoIterator into utxoset.go.

* [DEV-74] Renamed variables in utxoset.go.

* [DEV-74] Renamed verifyTx to areInputsInUTXO and removed a superfulous test.

* [DEV-74] Fixed bad test logic in TestDiffUTXOSet_addTx.

* [DEV-74] Added a few comments. Added reference-equals checks to clone functions.

* [DEV-74] Moved utxoCollection and utxoDiff into utxoset.go.

* [DEV-74] Wrote explanations for utxoCollection and utxoDiff tests.

* [DEV-74] Wrote explanations for all utxoSet tests besides addTx.

* [DEV-74] Wrote explanations for TestDiffUTXOSet_addTx.

* [DEV-74] Moved the documentation for utxoDiff into utxoset.go.

* [DEV-74] Wrote an explanation on utxoSet.

* [DEV-75] Found a typo.

* [DEV-75] Renamed dag -> virtual, dagView -> virtualBlock.

* [DEV-75] Renamed newDAGView to newVirtualBlock.

* [DEV-75] Moved queries for the genesis block from virtualBlock to BlockDAG.

* [DEV-75] Got rid of chainView height and findFork.

* [DEV-75] Renamed receivers from c to v.

* [DEV-75] Updated initBlockNode to allow for virtual (headerless) nodes, updated dbDAGState to contain multiple tip hashes, implemented virtualBlock.setTips.

* [DEV-75] Got rid of virtualBlock.equals, which was not used anywhere.

* [DEV-75] Got rid of virtualBlock.tip().

* [DEV-75] Got rid of SetTip everywhere except for tests.

* [DEV-75] Got rid of Next().

* [DEV-75] Got rid of Contains().

* [DEV-75] Got rid of HeightRange(), as no one was using it.

* [DEV-75] Made verifyDAG in rpcserver.go not use block height for iteration.

* [DEV-75] Got rid of the part of Manager.Init() that handled "catching up" for side chains, which allowed me to get rid of BlockDAG.BlockByHeight().

* [DEV-75] Dropped support for the RPC command getblockhash since it was getting blocks by their height.

* [DEV-75] Dropped getnetworkhashps since it was reliant on height, fixed another couple of RPC commands to return nextHashes instead of a nextHash, and got rid of nodeByHeight in virtualBlock.

* [DEV-75] Got rid of setTip().

* [DEV-75] Moved blockLocator() out of virtualBlock and into BlockDAG. Also removed TestLocateInventory().

* [DEV-75] Implemented addTip().

* [DEV-75] Cleaned up virtualblock.go a bit.

* [DEV-75] Erased irrelevant tests in virtualblock_test.go. Moved dag-related tests into dag_test.go.

* [DEV-75] Removed unnecessary nil check.

* [DEV-75] Wrote tests for virtualBlock.

* [DEV-75] Fixed bad test, added explanations to tests.

* [DEV-89] Fixed a comment.

* [DEV-89] Fixed another comment.

* [DEV-89] Removed the section in Manager::Init that handled rolling back indexes to the main chain if their tip is an orphaned fork. This could only happen during reorg, which no longer exists. Also removed BlockDAG::MainChainHasBlock, which was no longer used by anyone.

* [DEV-89] Removed the nil check inside initBlockNode() and amended the one place that called it with nil.

* [DEV-89] Renamed the receiver param for BlockDAG from b to dag.

* [DEV-89] Moved fastLog2Floor from dag.go to btcutil/btcmath.go.

* [DEV-89] Renamed tstTip to testTip.

* [DEV-89] Renamed phanom_test.go to phantom_test.go.

* [DEV-89] Fixed comments, renamed mainChainHeight to dagHeight.

* [DEV-89] Rewrote virtualBlock.addTip().

* [DEV-89] Fixed a comment. (chain -> DAG)

* [DEV-89] Fixed another chain -> DAG comment.
2018-08-09 18:21:03 +03:00
stasatdaglabs
f6eb9edecf [DEV-74] Added infrastructure for Diff-based UTXOs (#39)
* [DEV-74] Implemented and written tests for utxoIterator.

* [DEV-74] Improved utxoIterator tests.

* [DEV-74] Implemented utxoCollection tests.

* [DEV-74] Implemented utxoDiff and its tests.

* [DEV-74] Implemented utxoSet.

* [DEV -74] Added tests for fullUTXOSet.

* [DEV-74] Added some tests for diffUTXOSet.

* [DEV-74] Wrote tests for diffUTXOSet iterator.

* [DEV-74] Added a negative test for diffUTXOSet.withDiff.

* [DEV-74] Wrote tests for addTx.

* [DEV-74] Wrote a toRemove test for addTx.

* [DEV-74] Changed blockNode.utxoDiff to be of type utxoDiff.

* [DEV-74] Removed superfluous whitespace.

* [DEV-74] Renamed confusing "previousHash" to "hash".

* [DEV-74] Fixed bad test and corrected failing test.

* [DEV-74] Moved confusing "negatives" test to be part of the general utxoCollection test.

* [DEV-74] Removed utxoDiff.inverted.

* [DEV-74] Renamed blockNode.utxoDiff to blockNode.diff.

* [DEV-74] Renamed diff to diffFrom for clarity's sake.

* [DEV-74] Converted the type of utxoCollection from map[daghash.Hash]map[uint32]*wire.TxOut to map[wire.OutPoint]*UtxoEntry.

* [DEV-74] Corrected test names in utxoCollection_test.

* [DEV-74] Removed superfluous utxoCollection iterator and moved utxoIterator into utxoset.go.

* [DEV-74] Renamed variables in utxoset.go.

* [DEV-74] Renamed verifyTx to areInputsInUTXO and removed a superfulous test.

* [DEV-74] Fixed bad test logic in TestDiffUTXOSet_addTx.

* [DEV-74] Added a few comments. Added reference-equals checks to clone functions.

* [DEV-74] Moved utxoCollection and utxoDiff into utxoset.go.

* [DEV-74] Wrote explanations for utxoCollection and utxoDiff tests.

* [DEV-74] Wrote explanations for all utxoSet tests besides addTx.

* [DEV-74] Wrote explanations for TestDiffUTXOSet_addTx.

* [DEV-74] Moved the documentation for utxoDiff into utxoset.go.

* [DEV-74] Wrote an explanation on utxoSet.

* [DEV-74] Moved diffChild next to diff, improved their comments, moved the explanations for diffFrom and withDiff to the appropriate methods, got rid of utxoIterator, and renamed areInputsInUTXO to containsInputs.

* [DEV-74] Replaced boring old-fashioned reference equality with special, fancy reference equality for maps, slices, and channels.

* [DEV-74] Wrote additional explanations for test cases.
2018-08-08 11:06:09 +03:00
Ori Newman
ed5df3bf7d [DEV-66] Make only transaction version 1 a standard transaction (#40)
* [DEV-66] Make only transaction version 1 a standard transaction

* [DEV-66] change checkTransactionStandard to get policy as argument
2018-08-06 16:59:33 +03:00
Ori Newman
904f2cf2e3 [DEV-72] Write Blues()
* [DEV-62] add phantom constructs to blocknode

* [DEV-62] add phantom constructs to blocknode

* [DEV-72] write blues()

* [DEV-72] write blues()

* [DEV-72] write blues()

* [DEV-62] add comments to new phantom constructs in blocknode

* Fixed dbIndexConnectBlock. (#33)

* Fixed dbIndexConnectBlock.

* Removed redundant check in storeFilter.

* Created a new method to BlockHeader: IsGenesis.

* [DEV-71] Implement BlockHeap (#35)

* [DEV-71] Implemented BlockHeap.

* [DEV-71] Removed irrelevant comment.

* [DEV-71] Renamed variables in Pop() and split Less() to multiple lines.

* [DEV-72] write blues()

* [DEV-72] write blues()

* [DEV-72] write blues()

* [DEV-72] write blues tests

* [DEV-72] write blues tests

* [DEV-72] remove relevant past

* [DEV-72] write blues tests

* [DEV-72] write blues tests

* [DEV-72] write blues tests

* [DEV-72] write functions to order blockSet by hash and write blue tests

* [DEV-72] add secret mining and censorship attack tests

* [DEV-72] remove prints

* [DEV-72] remove K from dagconfig.Params

* [DEV-72] remove K from dagconfig.Params

* [DEV-72] change blueScore to uint64

* [DEV-72] block V was missing, so renamed w -> v, x -> w etc

* [DEV-72] use node.String instead of %v

* [DEV-72] block V was missing, so renamed w -> v, x -> w etc

* [DEV-72] add K to dagconfig.Params, and add expected reds to all phantom tests

* [DEV-72] set K=10 and add comments to phantom and phantom tests

* [DEV-72] fix formatting and add comments to TestPhantom

* [DEV-72] fix grammar
2018-08-02 16:34:40 +03:00
Svarog
2068ac299a [DEV-77] Wire converage
* [DEV-77] Added tests for ScriptFreeList

* [DEV-77] Fixed formatting in BlockHeader .SelectedPrevBlock and IsGenesis

* [DEV-77] Restructured returnScriptBuffers to be more idiomatic and testable

* [DEV-77] Added tests for binaryFreeList

* [DEV-77] Added test-cases to TestVarIntWireErrors to check for errors when writing the varint length

* [DEV-77] Added MsgSendHeaders and MsgFeeFilter test-cases for TestMessage

* [DEV-77] Added test for BlockHeader.IsGenesis()

* [DEV-77] using binaryFreeListMaxItems instead of freeListMaxItems in TestBinaryFreeList

* [DEV-77] Fixed error message copy-paste typo
2018-07-29 14:32:28 +03:00
Svarog
19a043602b Dev 80 dagconfig coverage (#37)
* [DEV-80] Added tests for Strings(hashes)

* [DEV-80] Added tests for Bech32Prefix.To/FromString + DNSSeed.String()

* [DEV-80] Fixed namings of test variables
2018-07-29 10:46:17 +03:00
stasatdaglabs
226abbc37b Merge pull request #32 from daglabs/dev-31-convert-time-int64
Dev 31 convert time to int64
2018-07-25 12:20:08 +03:00
Ori Newman
0c09c859b5 [DEV-31] fix nonce, bits, and timestamp comments 2018-07-25 12:16:57 +03:00
stasatdaglabs
ba0b4f61f3 Merge pull request #34 from daglabs/dev-62-add-phantom-constructs-to-blocknode
Dev 62 add phantom constructs to blocknode
2018-07-24 13:55:02 +03:00
Ori Newman
6a11450164 [DEV-62] fix phantom constructs comments 2018-07-24 13:53:22 +03:00
stasatdaglabs
5f800890ec [DEV-71] Implement BlockHeap (#35)
* [DEV-71] Implemented BlockHeap.

* [DEV-71] Removed irrelevant comment.

* [DEV-71] Renamed variables in Pop() and split Less() to multiple lines.
2018-07-22 17:26:18 +03:00
Ori Newman
c8db39e7d9 [DEV-62] add comments to new phantom constructs in blocknode 2018-07-22 16:13:12 +03:00
Ori Newman
9f315658df [DEV-31] remove irrelevant white spaces 2018-07-22 16:02:41 +03:00
Ori Newman
1e98d0dbfe [DEV-31] remove json transaction tests 2018-07-22 15:53:06 +03:00
stasatdaglabs
b4a9805157 Fixed dbIndexConnectBlock. (#33)
* Fixed dbIndexConnectBlock.

* Removed redundant check in storeFilter.

* Created a new method to BlockHeader: IsGenesis.
2018-07-22 15:06:13 +03:00
Ori Newman
9be0474913 [DEV-62] add phantom constructs to blocknode 2018-07-22 13:42:05 +03:00
Ori Newman
ce7fe1f26e [DEV-62] add phantom constructs to blocknode 2018-07-22 13:27:19 +03:00
Ori Newman
f1e24fcd43 Merge branch 'dev-31-20-tmp-branch' into dev-31-convert-time-int64 2018-07-22 13:02:24 +03:00
Ori Newman
16b9a36bcc [DEV-31] Fix conflicts with master 2018-07-22 13:02:22 +03:00
Mike Zak
290161b390 [DEV-20] Fix compilation errors in integration/rpctest due to BlockDAG 2018-07-22 12:45:08 +03:00
Mike Zak
3b95ec953e Merge remote-tracking branch 'origin/master' into dev-20-primitive-blockdag 2018-07-22 12:43:04 +03:00
Ori Newman
bed2d1b052 [DEV-31] Regenrate transactions for mining policy tests 2018-07-22 12:30:22 +03:00
Ori Newman
2c29cb2f20 Merge remote-tracking branch 'origin/dev-20-primitive-blockdag' into dev-31-20-tmp-branch 2018-07-22 12:02:33 +03:00
stasatdaglabs
f24673c396 [DEV-42] Update btcd package for DAG (#31)
* [DEV-42] Fixed rpcserverhelp.go.

* [DEV-42] Fixed GetBlockHeaderVerboseResult, GetBlockVerboseResult, and GetBlockTemplateResult.

* [DEV-42] Fixed rpcserver.go.

* [DEV-42] Got rid of the rescan command. Apparently it was deprecated and succeeded by the rescanBlocks command.

* [DEV-42] Modified handleRescanBlocks to satisfy compilation.

* [DEV-42] Fixed failing tests in rpcserver.go and rpcserverhelp.go.

* [DEV-42] Removed tests for rescan.

* [DEV-42] Removed rescan from notify.go.

* [DEV-42] Fixed a couple of comments I've missed.
2018-07-22 12:01:42 +03:00
Ori Newman
3989cc5dde Merge branch 'dev-31-convert-time-int64' into dev-31-20-tmp-branch 2018-07-22 11:59:16 +03:00
Ori Newman
9327fc01bb [DEV-31] Regenrate block databases and genesis blocks in order to be compatiable with the new structure (uint64 timestamps) 2018-07-22 11:58:21 +03:00
Ori Newman
75113a8d31 [DEV-31] Regenrate block databases and genesis blocks in order to be compatiable with the new structure (uint64 timestamps) 2018-07-20 20:09:30 +03:00
Ori Newman
552ffae636 Merge branch 'dev-31-convert-time-int64' into dev-31-20-tmp-branch 2018-07-20 10:25:49 +03:00
Ori Newman
e88d192f29 Merge remote-tracking branch 'origin/dev-20-primitive-blockdag' into dev-31-20-tmp-branch 2018-07-20 10:25:36 +03:00
Ori Newman
8e7d475bf9 [DEV-31] Regenrate block databases and genesis blocks in order to be compatiable with the new structure (uint64 timestamps) 2018-07-20 10:24:41 +03:00
Ori Newman
adb6c1fea9 [DEV-31] Regenrate block databases and genesis blocks in order to be compatiable with the new structure (uint64 timestamps) 2018-07-20 01:09:19 +03:00
Ori Newman
29f1b2baff [DEV-31] Regenrate block databases and genesis blocks in order to be compatiable with the new structure (uint64 timestamps) 2018-07-19 19:04:00 +03:00
stasatdaglabs
8b1897854a [DEV-41] Update netsync package for DAG (#30) 2018-07-18 17:18:45 +03:00
stasatdaglabs
67f19eb9d2 [DEV-36] Update cmd sub-packages for primitive BlockDAG (#29)
* [DEV-36] Fixed import.go.

* [DEV-65] Renamed BestState to State and GetSnapshot to GetCurrentState.

* [DEV-65] Implemented State and SelectedTip, updated dagState (the serializable version of State), and updated State creations.

* [DEV-65] Fixed references to GetCurrentState and fixed tests.

* [DEV-40] Created a new method AreEquals for comparing hash slices. Updated mining package to work with primitive DAG.

* [DEV-65] Renamed State to DAGState, corrected comments, created a new thread-safe method setDAGState, fixed broken references I missed.

* [DEV-65] Converted dagState (the object) to dbDAGState, and made it serialize/deserialize into/out of JSON.

* [DEV-65] Renamed Txns to Txs and fixed a couple of comments.

* [DEV-65] Renamed some more "Txns" and removed some dead code.

* [DEV-36] Fixed findcheckpoint.go.

* [DEV-36] Fixed indentation.

* [DEV-36] Fixed weird phrasing.

* [DEV-36] Fixed some more weird phrasing.

* [DEV-36] Removed haveBlocks variable from the function HaveBlocks.
2018-07-18 16:39:04 +03:00
stasatdaglabs
784839e8e7 [DEV-40] Update package mining and cpuminer for DAG (#28)
* [DEV-65] Renamed BestState to State and GetSnapshot to GetCurrentState.

* [DEV-65] Implemented State and SelectedTip, updated dagState (the serializable version of State), and updated State creations.

* [DEV-65] Fixed references to GetCurrentState and fixed tests.

* [DEV-40] Created a new method AreEquals for comparing hash slices. Updated mining package to work with primitive DAG.

* [DEV-65] Renamed State to DAGState, corrected comments, created a new thread-safe method setDAGState, fixed broken references I missed.

* [DEV-65] Converted dagState (the object) to dbDAGState, and made it serialize/deserialize into/out of JSON.

* [DEV-65] Renamed Txns to Txs and fixed a couple of comments.

* [DEV-65] Renamed some more "Txns" and removed some dead code.

* [DEV-40] Fixed formatting errors...

* [DEV-40] Reworded confusing test failure messages.

* [DEV-40] Fixed merge error.

* [DEV-40] Fixed style for AreEqual and TestAreEqual.
2018-07-18 11:54:17 +03:00
Ori Newman
417114edb4 [DEV-31] Convert all timestamp to uint64 2018-07-17 16:01:05 +03:00
Ori Newman
0878f98d1e [DEV-31] Convert all timestamp to uint64 2018-07-17 13:04:29 +03:00
stasatdaglabs
ead8a06219 [DEV-65] Convert BestState and GetSnapshot to State and GetCurrentState (#27)
* [DEV-65] Renamed BestState to State and GetSnapshot to GetCurrentState.

* [DEV-65] Implemented State and SelectedTip, updated dagState (the serializable version of State), and updated State creations.

* [DEV-65] Fixed references to GetCurrentState and fixed tests.

* [DEV-65] Renamed State to DAGState, corrected comments, created a new thread-safe method setDAGState, fixed broken references I missed.

* [DEV-65] Converted dagState (the object) to dbDAGState, and made it serialize/deserialize into/out of JSON.

* [DEV-65] Renamed Txns to Txs and fixed a couple of comments.

* [DEV-65] Renamed some more "Txns" and removed some dead code.
2018-07-17 10:44:44 +03:00
Ori Newman
90b71f1bb7 [DEV-31] Convert all timestamp to int64 2018-07-16 11:12:34 +03:00
Ori Newman
d488aebe0d [DEV-31] Convert all timestamp to int64 2018-07-15 18:00:21 +03:00
Ori Newman
d472600155 [DEV-18] Remove CSV and CLTV soft fork logic - convert to base protocol (#26)
* [DEV-18] changed CSV/CLTV to be regular op codes, and returned nop2 and nop3 to be regular nops

* [DEV-18] remove csv/cltv flags - part 1

* [DEV-18] remove csv/cltv flags - part 2

* [DEV-18] remove csv/cltv activation rules

* [DEV-18] remove csv/cltv activation rules

* [DEV-18] csv_fork_test fixes

* [DEV-18] readd chain params

* [DEV-18] readd chain params and remove csv activation rules

* [DEV-18] returned build flags to integration test

* [DEV-18] make csv/cltv to pop the the first element of the stack instead of peeking it

* [DEV-18] fix comments related to CSV/CLTV to remove any reference to soft fork

* [DEV-18] fix comments related to CSV/CLTV to remove any reference to soft fork

* [DEV-18] rename csv_fork_test.go to csv_fork.go

* [DEV-18] change mTx location

* [DEV-18] remove BIP0065Height

* [DEV-18] add function isUpgradableNop for readability
2018-07-15 13:56:34 +03:00
Mike Zak
1631998091 Merge remote-tracking branch 'origin/master' into dev-20-primitive-blockdag 2018-07-12 16:23:55 +03:00
stasatdaglabs
c9cdd2c81f Merge pull request #14 from daglabs/dev-38-peer-primitive-blockdag
Dev 38 peer primitive blockdag
2018-07-12 15:19:56 +03:00
Mike Zak
851d02ba72 Merge remote-tracking branch 'origin/dev-20-primitive-blockdag' into dev-38-peer-primitive-blockdag 2018-07-12 12:56:17 +03:00
stasatdaglabs
b07805cc12 [DEV-54] Rename "chainXXX", "bestXXX", etc. inside blockdag package (#25)
* [DEV-54] Renamed chainXXX to dagXXX.

* [DEV-54] Additional chain -> dag renamings.

* [DEV-54] Renamed a couple of more "chain"s I missed.
2018-07-12 12:46:03 +03:00
stasatdaglabs
f4a0ec175d [DEV-45] Update blockdag package tests for primitive blockDAG (#24)
* [DEV-45] Updated the BlockDB files to contain blocks that could be deserialized.

* [DEV-45] Fixed TestHaveBlock (finally)

* [DEV-45] Fixed TestLocateInventory and everything that's reliant on chainview_test::chainedNodes.

* [DEV-45] Fixed TestChainViewNil.

* [DEV-45] Fixed TestNotifications.

* [DEV-45] Fixed ExampleBlockChain_ProcessBlock.

* [DEV-45] Fixed TestCheckBlockScripts.

* [DEV-45] Fixed TestCheckConnectBlockTemplate.

* [DEV-45] Renamed the BlockDBs to their original names.

* [DEV-45] Skipping TestFullBlocks for until we have implemented Phantom.

* [DEV-45] Deleted a couple of methods that are no longer used. (They were previously used for reorganization)
2018-07-11 17:09:35 +03:00
Mike Zak
44ce294d6b Updated Gopkg.lock with latest versions of dependencies 2018-07-08 12:19:48 +03:00
Svarog
b7a9f39dc6 [DEV-58] removed addition of multisig dummy in signMultiSig
* [DEV-56] Remove any occurance of OP_CODESEPARATOR

* [DEV-56] Modified sighash tests to exclude OP_CODESEPARATOR

* [DEV-56] Fixed sighash.json to not include any codeseparators at all

* [DEV-56] Reformatted sighash.json

* [DEV-56] Rename any instance of subScript to script

* [DEV-56] Extracted check for opUnknown to separate function

* [DEV-58] removed addition of multisig dummy in signMultiSig

* [DEV-58] Removed temporary code for test cleanup
2018-07-08 10:33:02 +03:00
Svarog
0acf0eb516 [DEV-56] Remove any occurance of OP_CODESEPARATOR
* [DEV-56] Remove any occurance of OP_CODESEPARATOR

* [DEV-56] Modified sighash tests to exclude OP_CODESEPARATOR

* [DEV-56] Fixed sighash.json to not include any codeseparators at all

* [DEV-56] Reformatted sighash.json

* [DEV-56] Rename any instance of subScript to script

* [DEV-56] Extracted check for opUnknown to separate function

* [DEV-58] Removed temporary code for test cleanup
2018-07-05 18:58:13 +03:00
Svarog
927b5ec4ec [DEV-55] Removed FindAndDelete functionality that deletes signatures
* [DEV-50] Remove the Multisig bug requiring a dummy push

* [DEV-53] SigHashSingle now errors when the index is wrong

* [DEV-53] Fixed tests for SIGHASH_SINGLE

* [DEV-55] Removed FindAndDelete functionality that deletes sognatures

* [DEV-55] Removed some more redundant tests

* [DEV-55] Fixed redundant comment
2018-07-04 12:10:14 +03:00
Svarog
3f3a10c695 [DEV-53] SigHashSingle now errors when the index is wrong
* [DEV-50] Remove the Multisig bug requiring a dummy push

* [DEV-53] SigHashSingle now errors when the index is wrong

* [DEV-53] Fixed tests for SIGHASH_SINGLE

* [DEV-53] Removed redundant part of comment
2018-07-04 11:09:40 +03:00
stasatdaglabs
c9c81e1a82 * [DEV-44] Got rid of reorganize and some things related to best/side chains. 2018-07-03 18:08:17 +03:00
Svarog
4d099d51cb [DEV-50] Remove the Multisig bug requiring a dummy push
* [DEV-50] Remove the Multisig bug requiring a dummy push

* [DEV-50] Removed redundant comment, since dummy bug was fixed

* [DEV-50] Removed some more dummy-related comments and error message
2018-07-03 11:21:16 +03:00
Mike Zak
a4eed8bf99 Merge branch 'dev-20-primitive-blockdag' into dev-34-update-blockdag 2018-07-01 15:59:07 +03:00
Mike Zak
805752a9d8 Merge branch 'dev-20-primitive-blockdag' of github.com:daglabs/btcd into dev-20-primitive-blockdag 2018-07-01 15:58:20 +03:00
Mike Zak
d92690f7e5 Merge remote-tracking branch 'origin/master' into dev-20-primitive-blockdag 2018-07-01 15:58:02 +03:00
stasatdaglabs
79a0c1f124 [DEV-43] Change UTXOViewpoint.BestHash() to Tips() and update all related logic (#15)
* [DEV-43] Changed BestHash to Tips, fixed logic inside utxoviewpoint.go.

* [DEV-43] Fixed broken references.

* [DEV-43] Replaced blockNode slices and hash slices with BlockSets.

* [DEV-43] Did some renaming, unexported blockSet, and rewrote AppendTip as AddBlock.

* [DEV-43] Removed explicit contains check from AddBlock.
2018-07-01 15:52:56 +03:00
Svarog
8195acd0bb [DEV-49] Renamed all opcodes consts to be CamelCase (#16) 2018-07-01 15:52:05 +03:00
Svarog
d94fe1684b Merge pull request #17 from daglabs/dev-47-cleanup-upgrade
[DEV-47] Removed legacy doUpgrades contents
2018-07-01 15:51:36 +03:00
Mike Zak
fca8da0b15 [DEV-47] Removed legacy doUpgrades contents, and only left it as a placeholder for future generations 2018-07-01 11:38:40 +03:00
Mike Zak
dbcc15c552 [DEV-34] Updated peer test to compile and run when there are multiple prevBlockHashes 2018-06-26 16:00:45 +03:00
Stas Boutenko
4b5e99e486 [DEV-34] Fixed formatting error. 2018-06-26 13:02:05 +03:00
Stas Boutenko
935a286413 [DEV-34] Added SelectedTip() to chainView. 2018-06-26 13:00:15 +03:00
Stas Boutenko
fdeb87bb99 [DEV-34] Added SelectedPrevBlock() to BlockHeader. 2018-06-26 12:44:38 +03:00
Stas Boutenko
a289b72980 [DEV-34] Added selectedParent to blockNode. 2018-06-26 12:19:05 +03:00
Stas Boutenko
14287a1dd1 [DEV-34] Inlined a couple of things, did some renaming, exported PrevHashes(). 2018-06-24 14:38:15 +03:00
Stas Boutenko
328fab0231 [DEV-34] Changed chainView.Tip() to chainView.Tips(). 2018-06-24 13:40:28 +03:00
Stas Boutenko
57e7ad1287 [DEV-34] In blockNode, made parents a pointer slice, got rid of numPrevHashes, and extracted a lookupPreviousNodes function. Changed c-style loops to range loops. 2018-06-24 13:17:43 +03:00
Stas Boutenko
b0d766b7ab [DEV-34] Made a bunch of modifications just to satisfy the compiler. 2018-06-24 11:37:31 +03:00
Stas Boutenko
e1e6d7e0b8 Merge branch 'dev-20-primitive-blockdag' into dev-34-update-blockdag 2018-06-24 10:57:13 +03:00
Svarog
2db8d603f3 [DEV-37] Rename rpcclient/chain.go -> dag.go + update all references to xxxChainxxx commands to xxxDAGxxx (#12) 2018-06-21 17:42:54 +03:00
Stas Boutenko
a4108b88cd Merge branch 'dev-20-primitive-blockdag' into dev-34-update-blockdag 2018-06-21 16:47:14 +03:00
Svarog
dbd1aec9b8 [DEV-35] Updated btcjson package to work with DAG
* [DEV-35] Updated btcjson package to work with DAG

* [DEV-35] Rename all chainsvr* packages to dagsvr*
2018-06-21 16:45:09 +03:00
Stas Boutenko
4260d37158 [DEV-34] Modified blockindex.go to account for multiple previous hashes and changed accept.go to reflect those changes. 2018-06-21 13:25:40 +03:00
Stas Boutenko
ab75fcf5fb Merge branch 'dev-20-primitive-blockdag' into dev-34-update-blockdag 2018-06-21 12:29:12 +03:00
Svarog
7ed7685373 [DEV-27] Updated database package for DAG
* [DEV-27] Fixed dbtool to work with multiple previous blocks

* [DEV-27] Fix Example_blockStorageAndRetrieval expected bytes

* [DEV-27] Added generator for testdata, and generated new testdata to conform to DAG

* [DEV-27] Fixed FetchBlockHeader(s) and database interface test for DAG
2018-06-21 12:27:22 +03:00
Stas Boutenko
196d23e787 [DEV-34] Modified process.go to account for multiple previous hashes. 2018-06-21 10:35:48 +03:00
Stas Boutenko
bc5edffc92 [DEV-25] Modified accept.go to account for multiple previous hashes.. 2018-06-20 13:49:43 +03:00
Svarog
6917fac21e Merge pull request #9 from daglabs/dev-32-remame-chaincfg
[DEV-32] Renamed chaincfg to dagconfig and chainhash to daghash.
2018-06-19 18:23:04 +03:00
Stas Boutenko
a26fd21ae7 [DEV-32] Renamed chaincfg to dagconfig and chainhash to daghash. 2018-06-19 18:19:54 +03:00
stasatdaglabs
d9cebcf987 Merge pull request #8 from daglabs/dev-24-primitive-blockdag-wire
[DEV-24] completed fixing wire tests after updating to DAG
2018-06-19 17:50:46 +03:00
Mike Zak
f7a80acdd3 Merge remote-tracking branch 'origin/dev-20-primitive-blockdag' into dev-24-primitive-blockdag-wire 2018-06-19 17:06:12 +03:00
Mike Zak
1dfffaccad [DEV-24] completed fixing wire tests after updating to DAG 2018-06-19 16:23:43 +03:00
Svarog
90d1fbb869 Merge pull request #7 from daglabs/dev-28-update-chaincfg
Dev 28 update chaincfg
2018-06-19 13:19:47 +03:00
Stas Boutenko
4457a49f54 [DEV-28] Forgot to remove a couple of not-relevant comments. 2018-06-19 12:46:42 +03:00
Stas Boutenko
8708482939 [DEV-28] Fixed the simnet genesis block. 2018-06-19 12:10:46 +03:00
Stas Boutenko
a268612dfb [DEV-28] Fixed the testnet genesis block. 2018-06-19 12:07:07 +03:00
Stas Boutenko
77ee404fd1 [DEV-28] Fixed the regtest genesis block. 2018-06-19 12:03:21 +03:00
Stas Boutenko
0918d2bb12 [DEV-28] Fixed the mainnet genesis block. 2018-06-19 11:59:37 +03:00
Stas Boutenko
23eaae0373 Merge branch 'dev-20-primitive-blockdag' into dev-28-update-chaincfg 2018-06-18 12:43:45 +03:00
Stas Boutenko
5f53f1e687 [DEV-28] Assigned networks to empty prevBlocks. 2018-06-18 11:30:12 +03:00
Svarog
740f4dc6a4 Merge pull request #6 from daglabs/dev-26-rename-blockchain
[DEV-26] Renamed package blockchain to blockdag.
2018-06-17 18:53:24 +03:00
Stas Boutenko
6d64421965 [DEV-26] Renamed package blockchain to blockdag. 2018-06-17 18:46:47 +03:00
Mike Zak
f95eb9cac1 Merge branch 'dev-20-primitive-blockdag' into dev-24-primitive-blockdag-wire 2018-06-17 18:43:36 +03:00
stasatdaglabs
9581a31d5a Merge pull request #4 from daglabs/remove-segwit
Remove segwit
2018-06-17 17:56:29 +03:00
Mike Zak
bc780d8a3d [DEV-14] Update json_rpc_api documentation after removing segwit 2018-06-17 17:50:58 +03:00
Mike Zak
f4fc50f5d9 [DEV-14] Fix wire tests to work with new code changes 2018-06-17 17:29:57 +03:00
Mike Zak
e6e9deef22 [DEV-14] Made MaxNumPrevBlocks a const 2018-06-17 17:29:31 +03:00
Mike Zak
a655bd9861 [DEV-24] changed wire.BlockHeader to have list of previous hashes, instead of only 1 2018-06-17 14:18:23 +03:00
Mike Zak
e14712c99e Merge remote-tracking branch 'origin/master' into remove-segwit 2018-06-14 10:49:10 +03:00
Svarog
0fa8f05646 Merge pull request #3 from daglabs/DEV-13
Dev 13
2018-06-13 18:06:42 +03:00
Mike Zak
c66fb13c2a [DEV-14] Removed remaining Segwit code 2018-06-13 12:14:42 +03:00
Mike Zak
ca5100b459 [DEV-14] Removed segwit from chaincfg package 2018-06-13 11:52:32 +03:00
Mike Zak
9d6ea58b4c [DEV-14] Removed Segwit from wire package. 2018-06-13 11:20:46 +03:00
Stas Boutenko
da9960ff91 [DEV-13] Fixed lint errors. 2018-06-12 17:17:23 +03:00
Stas Boutenko
e850d4737c [DEV-13] Moved map initialization out of init and into an initializer. 2018-06-12 15:28:02 +03:00
Stas Boutenko
371be97a06 [DEV-13] Reimplemented prefixes as enums instead of string values. 2018-06-12 12:20:19 +03:00
Mike Zak
c104634182 [DEV-14] Removed Segwit from peer package 2018-06-12 10:44:52 +03:00
Mike Zak
8eb7866c98 [DEV-14] Removed Segwit from addrmanager 2018-06-12 10:31:23 +03:00
Stas Boutenko
6924403653 [DEV-13] Removed netID magic numbers. 2018-06-11 11:13:16 +03:00
Mike Zak
e3079c7e79 [DEV-14] Removed SegWit from txscript package! 2018-06-11 09:58:48 +03:00
Stas Boutenko
d1bec5ced5 (#DEV-13) Fixed failing tests. 2018-06-10 16:45:38 +03:00
Stas Boutenko
1fd78ec2c0 (#DEV-13) In Bech32, P2PKH always starts with 0 and P2SH always starts with 8. 2018-06-10 16:34:36 +03:00
Mike Zak
cea07f3b98 (#DEV-14) Removed segwit from blockchain package 2018-06-10 13:47:28 +03:00
Mike Zak
ffdb76ecec (#DEV-14) Removed anything concerning segwit from mining package 2018-06-07 18:36:27 +03:00
Stas Boutenko
7b87673f9f (#DEV-13) Added Bech13 prefixes to all networks. 2018-06-07 17:45:24 +03:00
Mike Zak
7930ceebd4 (#DEV-14) Removed segwit from mempool package + anything related to virtual size in btcjson and btcd 2018-06-07 15:48:21 +03:00
Mike Zak
82b87df13e (#DEV-14) Removed segwit from netwsync package 2018-06-07 15:48:21 +03:00
Mike Zak
734abd749b (#DEV-14) Remove anything Segwit related in btcd package + some edits in btcjson+rpcclient to make tests pass 2018-06-07 13:46:32 +03:00
stasatdaglabs
104654872e Merge pull request #2 from daglabs/dep-instead-of-glide
Moved to dep instead of glide
2018-06-07 13:02:02 +03:00
Mike Zak
3d003ca102 Ignore btcutil in Gopkg.toml 2018-06-07 12:57:22 +03:00
Mike Zak
ecf8ea0bee Moved to dep instead of glide 2018-06-07 11:23:05 +03:00
Svarog
443ace1049 Merge pull request #1 from daglabs/resources
Resources
2018-06-04 17:00:56 +03:00
Stas Boutenko
2901d302f3 Updated references. 2018-06-03 18:34:09 +03:00
Stas Boutenko
91d2c669f0 Added IntelliJ GoLand-specific files to .gitignore. 2018-06-03 18:29:00 +03:00
Olaoluwa Osuntokun
86fed78113 Merge pull request #1045 from davecgh/multi_utxoentry_outpoints
multi: Rework utxoset/view to use outpoints.
2018-05-30 19:59:44 -07:00
Dave Collins
a59ac5b18f multi: Rework utxoset/view to use outpoints.
This modifies the utxoset in the database and related UtxoViewpoint to
store and work with unspent transaction outputs on a per-output basis
instead of at a transaction level.  This was inspired by similar recent
changes in Bitcoin Core.

The primary motivation is to simplify the code, pave the way for a
utxo cache, and generally focus on optimizing runtime performance.

The tradeoff is that this approach does somewhat increase the size of
the serialized utxoset since it means that the transaction hash is
duplicated for each output as a part of the key and some additional
details such as whether the containing transaction is a coinbase and the
block height it was a part of are duplicated in each output.

However, in practice, the size difference isn't all that large, disk
space is relatively cheap, certainly cheaper than memory, and it is much
more important to provide more efficient runtime operation since that is
the ultimate purpose of the daemon.

While performing this conversion, it also simplifies the code to remove
the transaction version information from the utxoset as well as the
spend journal.  The logic for only serializing it under certain
circumstances is complicated and it isn't actually used anywhere aside
from the gettxout RPC where it also isn't used by anything important
either.  Consequently, this also removes the version field of the
gettxout RPC result.

The utxos in the database are automatically migrated to the new format
with this commit and it is possible to interrupt and resume the
migration process.

Finally, it also updates the tests for the new format and adds a new
function to the tests to convert the old test data to the new format for
convenience.  The data has already been converted and updated in the
commit.

An overview of the changes are as follows:

- Remove transaction version from both spent and unspent output entries
  - Update utxo serialization format to exclude the version
  - Modify the spend journal serialization format
    - The old version field is now reserved and always stores zero and
      ignores it when reading
    - This allows old entries to be used by new code without having to
      migrate the entire spend journal
  - Remove version field from gettxout RPC result
- Convert UtxoEntry to represent a specific utxo instead of a
  transaction with all remaining utxos
  - Optimize for memory usage with an eye towards a utxo cache
    - Combine details such as whether the txout was contained in a
      coinbase, is spent, and is modified into a single packed field of
      bit flags
    - Align entry fields to eliminate extra padding since ultimately
      there will be a lot of these in memory
    - Introduce a free list for serializing an outpoint to the database
      key format to significantly reduce pressure on the GC
  - Update all related functions that previously dealt with transaction
    hashes to accept outpoints instead
  - Update all callers accordingly
  - Only add individually requested outputs from the mempool when
    constructing a mempool view
- Modify the spend journal to always store the block height and coinbase
  information with every spent txout
  - Introduce code to handle fetching the missing information from
    another utxo from the same transaction in the event an old style
    entry is encountered
    - Make use of a database cursor with seek to do this much more
      efficiently than testing every possible output
- Always decompress data loaded from the database now that a utxo entry
  only consists of a specific output
- Introduce upgrade code to migrate the utxo set to the new format
  - Store versions of the utxoset and spend journal buckets
  - Allow migration process to be interrupted and resumed
- Update all tests to expect the correct encodings, remove tests that no
  longer apply, and add new ones for the new expected behavior
  - Convert old tests for the legacy utxo format deserialization code to
    test the new function that is used during upgrade
  - Update the utxostore test data and add function that was used to
    convert it
- Introduce a few new functions on UtxoViewpoint
  - AddTxOut for adding an individual txout versus all of them
  - addTxOut to handle the common code between the new AddTxOut and
    existing AddTxOuts
  - RemoveEntry for removing an individual txout
  - fetchEntryByHash for fetching any remaining utxo for a given
    transaction hash
2018-05-27 03:07:41 -05:00
Conner Fromknecht
bc09449045 glide: update btcutil deps 2018-05-23 20:51:14 -07:00
Olaoluwa Osuntokun
d52471044a build: update glide to point to latest btcutil 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
4cb1c950e0 rpc: add help for new gcs commands 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
5596b63846 multi: fix linter warnings 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
2b7326ae52 blockchain/indexers: use latest API for cf indexing 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
f48bc43421 build: restore glide.lock 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
b72e16f0d6 multi: correct all import paths 2018-05-23 16:46:15 -07:00
Johan T. Halseth
3082ae92c3 rpcwebsocket: notify spends already in the mempool
This commit adds a logic to the addSpentRequests that inspects the
mempool for any spends of the outputs. Before this commit, a spend would
only be checked when a transaction was first accepted into the mempool.
2018-05-23 16:46:15 -07:00
Johan T. Halseth
c758834800 mempool_test: add TestCheckSpend 2018-05-23 16:46:15 -07:00
Johan T. Halseth
5394ca1afe mempool: add CheckSpend method
This commit adds a new method CheckSpend to the mempool, which takes an
outpoint and returns any transaction in the mempool that spends this
outpoint.
2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
ceb1caeb24 wire: increase MaxGetCFiltersReqRange from 100 to 1000 2018-05-23 16:46:15 -07:00
Jim Posen
7b67f61fa6 rpcclient: Put CFHeader in PrevFilterHeader field in RPC response. 2018-05-23 16:46:15 -07:00
Jim Posen
4d0e856ea1 server: Handler for getcfcheckpt messages. 2018-05-23 16:46:15 -07:00
Jim Posen
0581e18840 wire: Define CFCheckpt message. 2018-05-23 16:46:15 -07:00
Jim Posen
336b18c584 wire: Define GetCFCheckpt message. 2018-05-23 16:46:15 -07:00
Jim Posen
4c991c8783 wire: Populate cfheaders message with filter hashes instead of headers. 2018-05-23 16:46:15 -07:00
Jim Posen
175af18043 multi: Modify CFHeaders message to have a PrevFilterHeader field. 2018-05-23 16:46:15 -07:00
Jim Posen
7a53a05878 multi: Redefine GetCFHeaders to have StartHeight and StopHash. 2018-05-23 16:46:15 -07:00
Jim Posen
daac60675e multi: Redefine GetCFilters to have StartHeight and StopHash. 2018-05-23 16:46:15 -07:00
Jim Posen
3425d33506 blockchain/indexers: Add methods on CfIndex for batch retrieval.
Fetching all items in one db transaction will save time when
responding to getcfilters or getcfheaders requests.
2018-05-23 16:46:15 -07:00
Jim Posen
e617483b44 blockchain/indexers: Store filter hashes with cfindex. 2018-05-23 16:46:15 -07:00
Jim Posen
d07fd2f333 blockchain/indexers: Reduce duplication in cfindex.
The index will hold three types of entries for each filter type, block
pair: filter, header, and hash. Since they all have similar methods
and implementations, refactor to reduce duplication.
2018-05-23 16:46:15 -07:00
Jim Posen
185577f4c2 blockchain: Implement IntervalBlockHashes method.
This will be used to respond to getcfcheckpt queries.
2018-05-23 16:46:15 -07:00
Jim Posen
07393c0dab blockchain: HeightToHashRange function for pulling range of hashes.
This functionality is required to implement BIP 157.
2018-05-23 16:46:15 -07:00
Jim Posen
621f347929 wire: Remove cftypes and getcftypes commands. 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
34d82682b0 mempool: switch fee estimation to use vsize as a base
In this commit, we modify the fee estimation to use vsize as a base
rather than size. A recent commit landed to track the fee rate using
vsize in the mempool, and also correct some incorrect unit math. This is
a follow up to that commit to ensure that fee estimation is uniform
throughout.
2018-05-23 16:46:15 -07:00
Johan T. Halseth
56be349be3 mempool: use vsize when setting FeePerKB for mempool txs 2018-05-23 16:46:15 -07:00
Johan T. Halseth
b7930a11ab mempool/estimatefee: make 1 Kb = 1000 bytes
This commit changes the value of bytesPerKb to 1000 from 1024.
This is done to ensure consistency between the fee estimator
and the mempool, where the feeRate is set to
    fee * 1000 / serializedSize
2018-05-23 16:46:15 -07:00
Conner Fromknecht
b26daffac9 connmgr: adds cancellation of pending requests
This commit extends the work started by roasbeef in the
 previous commit to bring full cancellation of pending
 connection requests. It also adds minor refactors to
 channel send/receives to help cleanup potentially
 lingering go routines.
2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
548c0f499b connmgr: add ability to remove pending connections
This commit adds the ability for callers to remove pending connections
via a call to the Remove() method. With this change, upstream users of
this package can use the connmgr for more elaborate connectivity needs
as they can now cancel pending connections that are no longer needed.
2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
ffe4c2f0ad integration/rpctest: if unable to build btcd, use one in PATH 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
5291c455c2 server: properly set feeEstimator in newServer 2018-05-23 16:46:15 -07:00
Daniel Krawisz
47113d428c It is now possible to save and restore the state of the FeeEstimator
and the server searches the database for a previous state to load
when the program is turned on.
2018-05-23 16:46:15 -07:00
Daniel Krawisz
4fd446028f Enable estimatefee rpc command. 2018-05-23 16:46:15 -07:00
Daniel Krawisz
1333ad7f78 FeeEstimator added to server. Mempool alerts the fee estimator of
new txs that it observes. The block manager alerts the fee estimator
of new and orphaned blocks.

Check for invalid state and recreate FeeEstimator if necessary.
2018-05-23 16:46:15 -07:00
Daniel Krawisz
e6d8b869aa feeEstimator changed to FeeEstimator. A number of optimizations and improvements.
Rollback takes a block hash rather than a BlockStamp.

Increase rounds in TestEstimateFeeRollback to test dropping txs that have been in the mempool too long.
2018-05-23 16:46:15 -07:00
Daniel Krawisz
4042921791 feeEstimator class added with tests. The feeEstimator can keep track
of how long it takes for unconfirmed txs to be mined into blocks. It
can also roll itself back in the case of an orphan block.
2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
10432160d5 multi: rebase to upstream, update API's 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
6f32a79fd6 log: update to latest log rotator API 2018-05-23 16:46:15 -07:00
Alex
f4060b107c blockchain/indexers: check for ErrNoData in extended filters
Since the tx hash has moved to the basic filter, generating an extended filter
can result in `ErrNoData`. This is handled by writing a nil filter and giving
it a zero hash.
2018-05-23 16:46:15 -07:00
Alex
e0a357abb5 server: add TODO for querying supported filter types 2018-05-23 16:46:15 -07:00
Alex
c7e7acc7fd multi: use hidden varint for cftypes count; make filter type enum, not uint8 2018-05-23 16:46:15 -07:00
Alex
8d2ce855eb wire: update service bits for xthin, bit 5, cfilters, and segwit2x 2018-05-23 16:46:15 -07:00
Alex
5772bdde86 server: add OnGetCFTypes callback to serverPeer 2018-05-23 16:46:15 -07:00
Alex
2b7ebfd698 peer: add OnCFTypes and OnGetCFTypes callbacks 2018-05-23 16:46:15 -07:00
Alex
5c93ca639a rpcclient: change "bool" to "uint8" for cfilter/cfheader type 2018-05-23 16:46:15 -07:00
Alex
5fb5acd643 btcjson: fix cfilter tests 2018-05-23 16:46:15 -07:00
Alex
1aa7a6166d wire: update messages to conform to BIP and fix comments 2018-05-23 16:46:15 -07:00
Alex
89d6696560 wire: add getcftypes and cftypes messages 2018-05-23 16:46:15 -07:00
Alex
621c73dad1 multi: change cfilter Extended bool to FilterType uint8
The cfilter BIP specifies that the filter type is a uint8. The
current code encodes it correctly on the wire, but everywhere else,
it's treated as a boolean (false for basic filter, true for
extended). This commit corrects that to account for possible
additional filter types in the future. All package changes are
done in one commit as they're all interdependent. The following
packages are updated:

* blockchain/indexers
* btcjson
* peer
* wire
* main (server.go and rpcserver.go)
2018-05-23 16:46:15 -07:00
Alex
e2f65acf02 integration/rpctest: check quit in solveBlock()'s solver() closure 2018-05-23 16:46:15 -07:00
Alex
6cea610774 integration/rpctest: queue block disconnected ntfns in memwallet 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
8aeb8c3ef2 peer: temporarily disable stall detection 2018-05-23 16:46:15 -07:00
Alex
c5c392c5c5 server: init rpcserver with cfindex 2018-05-23 16:46:15 -07:00
Alex
ac109cfb86 rpcserver: return correct error when --nocfilters is enabled 2018-05-23 16:46:15 -07:00
Alex
09723f4800 btcjson: add ErrRPCNoCFIndex for cases when cfindex is disabled 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
5ab49ca22c rpc: add indexers.CfIndex to config, utilize 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
c8fdf8bf59 rpcclient: add GetCFilterHeader and GetCFilter 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
7c2f7be2ea wire: update compact filter messages to match wire.Message interface 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
3d1caa2f83 multi: update to point to roasbeef forks 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
9a30e95d83 peer: don't inv trickle new blocks 2018-05-23 16:46:15 -07:00
Josh Rickmar
a741b4366b all: Remove seelog logger.
The btclog package has been changed to defining its own logging
interface (rather than seelog's) and provides a default implementation
for callers to use.

There are two primary advantages to the new logger implementation.

First, all log messages are created before the call returns.  Compared
to seelog, this prevents data races when mutable variables are logged.

Second, the new logger does not implement any kind of artifical rate
limiting (what seelog refers to as "adaptive logging").  Log messages
are outputted as soon as possible and the application will appear to
perform much better when watching standard output.

Because log rotation is not a feature of the btclog logging
implementation, it is handled by the main package by importing a file
rotation package that provides an io.Reader interface for creating
output to a rotating file output.  The rotator has been configured
with the same defaults that btcd previously used in the seelog config
(10MB file limits with maximum of 3 rolls) but now compresses newly
created roll files.  Due to the high compressibility of log text, the
compressed files typically reduce to around 15-30% of the original
10MB file.
2018-05-23 16:46:15 -07:00
Alex
6589cffb19 server: change logging in OnGetCFilter to be less verbose 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
ac4bd31ba0 peer: disable peer stalling for get blocks messages 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
4a6dc67067 txscript: add exported sighash calc func 2018-05-23 16:46:15 -07:00
Alex
7647f884a9 rpcserver: Return 0-length headers via RPC as they're now valid. 2018-05-23 16:46:15 -07:00
Alex
ec228f9ff9 rpctest: Add GenerateAndSubmitBlockWithCustomCoinbaseOutputs 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
9b9ef42f8a blockchain/indexers: proper handling of empty filters 2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
e0943a84bd blockchain/indexers: add a bit more line spacing to cfindex.go 2018-05-23 16:46:15 -07:00
Alex
1c5f25bbf2 blockchain/cfindex: Factor out filter/header calc. 2018-05-23 16:46:15 -07:00
Alex
f11e87c957 rpcserver: Fix getcfilterheader RPC method's hex encoding for hash. 2018-05-23 16:46:15 -07:00
Alex
92f1de6dd9 Fix case where 0 headers are retrieved. 2018-05-23 16:46:15 -07:00
Alex
dabb8000fb Add Extended flag to cfilter and cfheaders messages 2018-05-23 16:46:15 -07:00
Alex
936caad9c2 Add last blockhash to cfheaders and blockhash to cfilter messages 2018-05-23 16:46:15 -07:00
Alex
ba4a2f77a5 wire/server: allocate hash once per loop to prevent overwriting 2018-05-23 16:46:15 -07:00
Alex
71ccc95502 wire: Fix MsgCFHeaders.Decode() 2018-05-23 16:46:15 -07:00
Alex
29b5ece196 Changed getcfheaders/cfheaders messages to get multiple headers. 2018-05-23 16:46:15 -07:00
Alex
860100019f blockchain: fix --dropcfindex 2018-05-23 16:46:15 -07:00
Alex
8ad7aa5d5d Rename CFilterHeader to CFHeader on P2P side; fix some bugs/tests 2018-05-23 16:46:15 -07:00
Alex
ba7b5f3308 Use NBytes() instead of Bytes() in CFIndex 2018-05-23 16:46:15 -07:00
pedro martelletto
0c08ba2786 define MsgCFilterHeader.Deserialize(), required by btcrpcclient 2018-05-23 16:46:15 -07:00
pedro martelletto
b5de49aa73 define MsgCFilter.Deserialize(), required by btcrpcclient 2018-05-23 16:46:15 -07:00
Alex
9780ef5997 Skip TxIn on coinbase transaction when indexing 2018-05-23 16:46:15 -07:00
pedro martelletto
b8c3be740f Add CFilterHeader p2p counterparts 2018-05-23 16:46:15 -07:00
Alex
6102e129c5 Fixed a couple of bugs and added --dropcfindex option 2018-05-23 16:46:15 -07:00
pedro martelletto
763842329b Implement a GetCFilterHeader RPC command 2018-05-23 16:46:15 -07:00
pedro martelletto
a5bf8941d5 Define FilterHeaderByBlockHash() 2018-05-23 16:46:15 -07:00
pedro martelletto
e620538343 Generate and store filter headers 2018-05-23 16:46:15 -07:00
pedro martelletto
b53c42f5dc Define dbFetch{Basic,Extended}Header() 2018-05-23 16:46:15 -07:00
pedro martelletto
7673859108 Create db buckets for committed filter hashes 2018-05-23 16:46:15 -07:00
pedro martelletto
ddfaed7f6f Delete extended filters in DisconnectBlock() 2018-05-23 16:46:15 -07:00
pedro martelletto
0a841fefcf Generate and store extended filters in ConnectBlock() 2018-05-23 16:46:15 -07:00
pedro martelletto
cdb3d44fa8 p2p needs a new message to return committed filters 2018-05-23 16:46:15 -07:00
pedro martelletto
71c421db66 Differentiate between basic/extended filters in p2p/RPC 2018-05-23 16:46:15 -07:00
pedro martelletto
f703e18652 Populate filter #1 (extended) 2018-05-23 16:46:15 -07:00
pedro martelletto
f16da156c9 Include data pushes from pkscripts 2018-05-23 16:46:15 -07:00
pedro martelletto
396d28955c better separation between filters; comments 2018-05-23 16:46:15 -07:00
pedro martelletto
57995fd111 Start preparing the ground for layer {0,1} filters 2018-05-23 16:46:15 -07:00
pedro martelletto
472141f88d Switch to new GCS builder interface 2018-05-23 16:46:15 -07:00
pedro martelletto
6d6677b797 zap errCFEntry 2018-05-23 16:46:15 -07:00
pedro martelletto
92c8ec4094 cbfindex.go -> cfindex.go 2018-05-23 16:46:15 -07:00
pedro martelletto
6e5f650be9 CBFilter -> CFilter, discussed with davec@ 2018-05-23 16:46:15 -07:00
pedro martelletto
a77b1e00d5 FilterByBlockHash(), pointed out by davec@ 2018-05-23 16:46:15 -07:00
pedro martelletto
9f02951b0e CloneBytes() -> [:], pointed out by davec@ 2018-05-23 16:46:15 -07:00
pedro martelletto
8b8c7bcf05 Implement DisconnectBlock() for the CBF Indexer 2018-05-23 16:46:15 -07:00
pedro martelletto
618306116b Return filter in handleGetCBFilter() 2018-05-23 16:46:15 -07:00
pedro martelletto
cd7857b883 GetCBFilter RPC returns a string, not a []byte 2018-05-23 16:46:15 -07:00
pedro martelletto
f6dff40733 Instrument GetCBFilter RPC 2018-05-23 16:46:15 -07:00
pedro martelletto
76378e7167 Add a GetCBFilter RPC command 2018-05-23 16:46:15 -07:00
pedro martelletto
43bf8db793 Look up filters from p2p GetCBF message 2018-05-23 16:46:15 -07:00
pedro martelletto
76926f8904 Roll in a dbFetchCBFIndexEntry() 2018-05-23 16:46:15 -07:00
pedro martelletto
3b0038093a Hook CBF indexer to server code 2018-05-23 16:46:15 -07:00
pedro martelletto
9809f4ffdd Instrument basic logging 2018-05-23 16:46:15 -07:00
pedro martelletto
cf74c8c3ca Abstract filter generation to a separate function 2018-05-23 16:46:15 -07:00
pedro martelletto
05d2fdeb11 Rename a variable in CBF's ConnectBlock() 2018-05-23 16:46:15 -07:00
pedro martelletto
620ad5b6fb Store filter in a db block index 2018-05-23 16:46:15 -07:00
pedro martelletto
b2990e7999 Move filter generating code to CBF indexer 2018-05-23 16:46:15 -07:00
pedro martelletto
95cbe2a911 Add the skeleton of a CBF indexer 2018-05-23 16:46:15 -07:00
pedro martelletto
c8627cbee4 Use a harcoded key to generate filters
Pointed out by alex@. Using a dummy key for now.
2018-05-23 16:46:15 -07:00
pedro martelletto
dc25da8296 Generate a filter for a given block 2018-05-23 16:46:15 -07:00
pedro martelletto
e9298934b9 Add a stub GetCBFilter message.
Actual semantic and payload format yet to be defined.
2018-05-23 16:46:15 -07:00
pedro martelletto
333af136ef Create a knob to switch CBFs off.
While having them on by default. We may want to revisit this and
make no CBFs the default.
2018-05-23 16:46:15 -07:00
pedro martelletto
d82e76cec9 Introduce a service flag for CBFs.
Add a service flag for CBFs in the wire protocol.
2018-05-23 16:46:15 -07:00
Olaoluwa Osuntokun
1432d294a5 build: update glide to point to latest btcutil
In this commit, we update the glide.lock file to be pinned against the
latest btcutil commit hash. btcutil has recently been updated to pull in
all changes from roasbeef's fork. Notably, it now includes the code
necessary for creating GCS filters (BIP 158).
2018-05-15 21:01:05 -07:00
Olaoluwa Osuntokun
4b968f7e18 btcec: remove obsolete test 2018-05-15 20:47:29 -07:00
Olaoluwa Osuntokun
2842f933bb server: fix linter error 2018-05-15 20:47:29 -07:00
Olaoluwa Osuntokun
04f0b5338a build: switch to stable version of gometalinter 2018-05-15 20:47:29 -07:00
Olaoluwa Osuntokun
253b37c17f btcec: format btcec_test.go with gofmt -s 2018-05-15 20:47:29 -07:00
Olaoluwa Osuntokun
7580169cb6 build: update build to use go 1.9.5 and 1.10.1 2018-05-15 20:47:29 -07:00
Vadym Popov
675abc5df3 chaincfg: change hrp prefix for regtest network 2018-04-17 20:01:12 -07:00
Dave Collins
2be2f12b35 rpcclient: Update for go1.10 breaking changes.
Go 1.10 made some changes such that json.Unmarshal can no longer
unmarshal into exported fields that are themselves embedded via an
uninitialized unexported pointer.

Since rpcclient previously relied on this behavior, this updates the
client to create the pointers before unmarshalling into the struct.
2018-02-19 18:56:35 -06:00
Josh Rickmar
1cd648d784 Require atomic swap contracts to specify the secret size.
This allows redeeming parties to audit the secret size and ensure it
will be usable once revealed.
2018-02-16 16:18:43 -05:00
Kai
9866016012 Fix sub packages links 2018-02-13 13:36:48 -06:00
benma
99a26bf7e0 wire: typo in BlockHeader PrevBlock comment 2018-02-13 13:04:12 -06:00
Jim Posen
0ef0d8c59b blockchain: Initialize database with genesis block.
This fixes a bug introduced by #1066.
2018-02-09 11:34:30 -05:00
Alex Manuskin
50de9da05b blockchain: Fix typo in example_test.go. 2018-01-31 02:40:46 -06:00
Jim Posen
9aa9e79ebf blockchain: Enhance migration to v2 block index bucket.
This updates the block index migration to also store entries with
valid status bytes.
2018-01-28 23:34:56 -06:00
Jim Posen
52cddc19cd blockchain: Persist block status changes to disk.
The block index now tracks the set of dirty block nodes with status
changes that haven't been persisted and flushes the changes to the DB
at the appropriate times.
2018-01-28 23:34:56 -06:00
Jim Posen
31444f5890 blockchain: Add parent to blockNode constructor. 2018-01-28 23:34:56 -06:00
Jim Posen
a4d22d8384 blockchain: Delete block headers from old block index bucket.
As part of the migration to the new block index bucket, also delete
the redundant data from the old bucket.
2018-01-28 23:34:56 -06:00
Jim Posen
ad00e7ff82 database: Stop writing block header data to LevelDB.
Now that all headers are stored in the in-memory index, the database
bucket managed by blockchain, and in the flat files, it makes sense to
drop the redundant data from the block index bucket in ffldb.

To avoid modifying the database interface, this reimplements
FetchBlockHeader(s) to use header data stored in flat files. This can
be trivially implemented by delegating to FetchBlockRegion.
2018-01-28 23:34:56 -06:00
Jim Posen
74fb6e56da blockchain: Database migration to populate block index bucket.
This creates a migration function that populates the block index
bucket using data from the ffldb block index bucket if it does not
exist.
2018-01-28 23:34:56 -06:00
Jim Posen
6315cea70c blockchain: Load all block headers into block index on init.
Currently only the blocks in the active chain are loaded into the
block index on initialization. This instead iterates over the entire
block index bucket in LevelDB and loads all nodes.
2018-01-28 23:34:56 -06:00
Jim Posen
175fd940bb blockchain: Store block headers in bucket managed by chainio.
The bucket contains block headers keyed by the block height encoded as
big-endian concatenated with the block hash. This allows block headers
to be fetched from the DB in height order with a cursor.
2018-01-28 23:34:56 -06:00
Cédric Félizard
358aed20b7 sampleconfig: Remove duplicate setting.
`addrindex` was present twice.
2018-01-28 23:25:43 -06:00
Jeremiah Goyette
fe786c93b6 rpcclient: implement addwitnessaddress 2018-01-26 01:20:40 -06:00
nakagawa
e4a6228752 rpcclient: Add decodescript support. 2018-01-26 00:20:33 -06:00
Nicola 'tekNico' Larosa
11fcd83963 btcd/multi: fix a number of typos in comments. 2018-01-25 23:23:59 -06:00
Marko Bencun
16dbb2602a txscript: export calcSignatureHash
This is a useful function for users of this library, and deserves to
be public.
2018-01-25 23:14:55 -06:00
Leonardo Lazzaro
78d12c33f0 database: Update rpcclient link in README.md. 2018-01-25 22:43:53 -06:00
Mitchell Paull
f22a07b6cf blockchain: Fix typos in README.md. 2018-01-25 22:40:42 -06:00
Kamil Slowikowski
be04ac2370 rpcserver: Fix typo in generate handler.
"mine a block" instead of "main a block".
2018-01-25 22:39:33 -06:00
Shuai Qi
b6afec5e51 txscript: Fix comment typo. 2018-01-25 22:37:50 -06:00
Josh Rickmar
2e60448ffc txscript: Require SHA256 secret hashes for atomic swaps 2017-11-28 10:07:13 -05:00
Janus Troelsen
8cea3866d0 Update script_test.go
Typo fix
2017-10-26 14:26:30 +02:00
Jim Posen
c7588cbf76 blockchain: NodeStatus & Set/UnsetStatusFlags methods on blockIndex.
These method allows safe concurrent access to reading and modifying
block node statuses. When block statuses get persisted in a later
change, the setter methods can be used to mark block nodes as dirty.
2017-10-23 04:33:15 -05:00
Jim Posen
e1ef2f899b blockchain: Track block validation status in block index.
Each node in the block index records some flags about its validation
state. This is just stored in memory for now, but can save effort if
attempting to reconnect a block that failed validation or was
disconnected.
2017-10-23 04:33:15 -05:00
Jim Posen
11d7cae82b rpctest: Compile current version of btcd and run that.
Previously, rpctest would start a btcd node using the btcd executable
in the environment PATH. This caused difficult-to-find issues where
the code would be tested against an older version of btcd, or another
fork entirely. Now it compiles btcd the first time it is needed and
uses that fresh version when launching nodes.
2017-10-20 14:29:45 -07:00
Steven Roose
fb43a179cb btcec: Add case to signature serialization test
It adds the case where the S value of the signature is bigger than the
half of the order of the curve.
2017-10-13 03:37:29 -05:00
Steven Roose
79445fbd97 btcec: Prevent static initialization of S256
This is achieved by introducing a new variable `halfOrder` on the
KoblitzCurve struct that is half the order.
2017-10-13 03:37:29 -05:00
Jim Posen
64d60f2ef2 blockchain: Remove BFDryRun behaviour flag.
This was only used to test block proposals, which has been changed to
instead use CheckConnectBlockTemplate. The flag complicated the
implementation of some chain processing routines and would be
difficult to implement with headers-first syncing.
2017-10-12 06:07:46 -05:00
Jim Posen
04444c1d0e blockchain: Use CheckConnectBlockTemplate for RPC block proposals.
This renames CheckConnectBlock to CheckConnectBlockTemplate and
modifies it to be easily consumable by the getblocktemplate RPC
handler. Performs full block validation now instead of partial
validation.
2017-10-12 06:07:46 -05:00
Josh Rickmar
4803a8291c txscript: Add API to parse atomic swap contracts. 2017-09-20 12:44:35 -05:00
Craig Sturdy
91e5ba1a80 btcjson: Implement addwitnessaddress functionality. 2017-09-10 23:10:46 -05:00
Alex Bosworth
63d1550d42 txscript: Trivial typo fixes. 2017-09-08 12:56:38 -05:00
Dave Collins
8c883d1fca blockchain/indexers: Allow interrupts.
This propagates the interrupt channel through to blockchain and the
indexers so that it is possible to interrupt long-running operations
such as catching up indexes.
2017-09-05 11:02:46 -05:00
Jim Posen
30d4caeac6 server: Simplify logic to bind listeners.
The helper function parseListeners has been changed to return a
slice of net.Addrs with Network() returning tcp4 or tcp6 instead of
returning two slices of IPv4 and IPv6 addresses to simplify calling
code. Also improves how local addresses are added to the address
manager when listening on wildcard addresses.

Also splits some newServer logic into new method initListeners.
2017-09-04 11:55:37 -07:00
David Hill
a2085c68f8 config: Add --whitelist support. 2017-08-31 09:59:43 -04:00
Dave Collins
42b969a827 blockchain: Remove unused min memory nodes field.
This removes the minMemoryNodes field from the BlockChain struct since
it is no longer used anywhere.
2017-08-30 17:08:34 -05:00
Dave Collins
02a06a2cd8 blockchain: Update some comments to match reality. 2017-08-30 17:07:46 -05:00
Jim Posen
0a9fb53548 blockchain: Add node to block index in maybeAcceptBlock.
This has the same effect but makes it clearer that all blocks written to
the database end up in the block index.
2017-08-30 11:29:52 -07:00
Dave Collins
0b50802d52 btcjson/rpcserver: Encode witness stack as slice.
The modifies the encoding of witness stacks in JSON responses to use a
slice of strings instead of a single space-separated string for
compatibility with Core.
2017-08-30 03:05:35 -05:00
Dave Collins
9bd7bcfff6 travis: Update to go 1.8 and 1.9.
Also, make the gosimple linter happy while here.
2017-08-30 01:03:26 -05:00
Alok Menghrajani
3cb87afa2f rpcclient: fix links in README.md 2017-08-28 01:50:25 -07:00
Andy Weidenbaum
a1d1ea70dd rm extra word segwit, s/segwit segwit/segwit 2017-08-26 10:56:46 -07:00
Dave Collins
feee952cd3 readme: Remove mailing lists and include GPG key.
This updates the README to remove the mailing lists which no longer
exist and to link the GPG public key used to sign the release tags
directly from the repository instead of the no longer active website.
2017-08-25 14:08:55 -05:00
Jim Posen
46fd4ec358 netsync: Change name of blockManager to syncManager. 2017-08-25 13:41:22 -05:00
Jim Posen
f2fc24d0fc netsync: Use package-local logger. 2017-08-25 13:41:22 -05:00
Jim Posen
1cf7e233e5 netsync: Initialize netsync package.
Create doc.go, interface.go, and README for new package.
2017-08-25 13:41:22 -05:00
Jim Posen
3135a40371 MOVEONLY: Move blockmanager and blocklogger to netsync directory. 2017-08-25 13:41:22 -05:00
Dave Collins
9b0884286f Update deps to pull in additional logging changes.
This update adds additional callsite logging options via btclog and
fixes an error with the rotator package that caused it to stop running
when creating any log messages larger than 4096 bytes.

While here, switch to the new Write method of the Rotator object as
this is more efficient than using the Reader interface with a pipe.

Changes from @jrick.
2017-08-24 17:29:08 -05:00
Dave Collins
34b1373a68 blockchain: Refactor inv discovery for chain view.
This refactors the code that locates blocks (inventory discovery) out of
server and into blockchain where it can make use of the new much more
efficient chain view and more easily be tested.  As an aside, it really
belongs in blockchain anyways since it's purely dealing with the block
index and best chain.

Since the majority of the network has moved to header-based semantics,
this also provides an additional optimization to allow headers to be
located directly versus needing to first discover the hashes and then
fetch the headers.

The new functions are named LocateBlocks and LocateHeaders.  The former
returns a slice of located hashes and the latter returns a slice of
located headers.

Finally, it also updates the RPC server getheaders call and related
plumbing to use the new LocateHeaders function.

A comprehensive suite of tests is provided to ensure both functions
behave correctly for both correct and incorrect block locators.
2017-08-24 14:15:34 -05:00
Dave Collins
6b802379ec txscript: Shallow tx copy for signature hash calc.
This modifies calcSignatureHash to use a shallow copy of the transaction
versus a deep copy since the actual scripts themselves are not modified
and therefore don't need to be copied.

This is being done because profiling the most overall allocated space
shows that the deep copy performed in calcSignatureHash accounts for
nearly 20% of all allocations on a synced running instance.  Also,
copying all of the additional data makes it more time consuming as well.

With this change, that figure drops from ~20% to ~5% of all allocations.

The following benchmark shows the relative speedups and allocation
reduction as a result of the optimization on my system.  In particular,
the changes result in approximately a 15% speedup and a whopping 99.89%
reduction in allocations when using a large transaction with thousands
of inputs which was the worst case scenario.

benchmark                        old allocs    new allocs    delta
--------------------------------------------------------------------
BenchmarkCalcSignatureHash       11151         12            -99.89%

benchmark                        old ns/op     new ns/op     delta
--------------------------------------------------------------------
BenchmarkCalcSignatureHash       3599845       3056359       -15.10%
2017-08-24 12:48:11 -05:00
Dave Collins
20910511e9 blockchain: Refactor to use new chain view.
- Remove inMainChain from block nodes since that can now be efficiently
  determined by using the chain view
- Track the best chain via a chain view instead of a single block node
  - Use the tip of the best chain view everywhere bestNode was used
  - Update chain view tip instead of updating best node
- Change reorg logic to use more efficient chain view fork finding logic
- Change block locator code over to use more efficient chain view logic
  - Remove now unused block-index-based block locator code
  - Move BlockLocator definition to chain.go
  - Move BlockLocatorFromHash and LatestBlockLocator to chain.go
    - Update both to use more efficient chain view logic
- Rework IsCheckpointCandidate to use block index and chain view
- Optimize MainChainHasBlock to use chain view instead of hitting db
  - Move to chain.go since it no longer involves database I/O
  - Removed error return since it can no longer fail
- Optimize BlockHeightByHash to use chain view instead of hitting db
  - Move to chain.go since it no longer involves database I/O
  - Removed error return since it can no longer fail
- Optimize BlockHashByHeight to use chain view instead of hitting db
  - Move to chain.go since it no longer involves database I/O
  - Removed error return since it can no longer fail
- Optimize HeightRange to use chain view instead of hitting db
  - Move to chain.go since it no longer involves database I/O
- Optimize BlockByHeight to use chain view for main chain check
- Optimize BlockByHash to use chain view for main chain check
2017-08-23 23:43:37 -05:00
Jim Posen
08955805d5 blockmanager: Remove serverPeer from blockmanager completely.
The purpose is to remove the dependency of blockmanager on serverPeer,
which is defined in the main package. Instead, we split out some of
the fields from serverPeer into a separate struct called peerSyncState
in blockmanager.go. While they are in the same package now, this
change makes it easier to move blockManager into its own package along
with peerSyncState. The blockManager tracks a map of Peer pointers to
the peer state and keeps it updated as peers connect and disconnect.
2017-08-23 10:02:12 -07:00
Jim Posen
088ccfd828 blockmanager: Remove dependency on cfg in main package. 2017-08-23 11:45:25 -05:00
Janus Troelsen
45ea940039 docs: Correct rpcclient link. 2017-08-21 15:56:57 -05:00
Dave Collins
2d84a74d28 blockchain: Correct chainview locator comment. 2017-08-21 14:16:31 -05:00
Dave Collins
991a72e34e blockchain: Remove unused verify disable code.
This removes the DisableVerify function and related state since nothing
uses it anymore since the command line option was removed.  It is a
remnant of initial development.
2017-08-21 04:10:23 -05:00
Dave Collins
1a947c46b2 blockchain: Faster chain view block locator.
This exposes the ability to more efficiently create a block locator from
a chain view for a given block node by using their ability to do O(1)
lookups.

It also adds tests to ensure the behavior is correct.
2017-08-19 23:58:04 -05:00
Dave Collins
93d8dfc760 blockchain: Optimize block locator generation.
This significantly optimizes and simplifies the generation of block
locators by making use of the fact that all block nodes are now in
memory and therefore it is no longer necessary to consult the database
for the hashes or worry about issues related to dynamic loading of nodes.

Also, it slightly modifies the algorithm so that the doubling doesn't
start for one additional iteration in order to mirror other prominent
clients on the network.  Due to the way block locators are used, this
does not change any semantics in terms of requesting and locating
blocks.

Finally, the semantics of BlockLocatorFromHash have been changed to
return a locator for the current tip in the case the hash is unknown.
This is far preferable since only including the passed block hash, when
it isn't known, could end up leading to causing a redownload of the
entire chain under certain circumstances.
2017-08-19 23:19:58 -05:00
Dave Collins
e02fbcf5a1 blockchain: Consolidate tests into package.
Putting the test code in the same package makes it easier for forks
since they don't have to change the import paths as much and it also
gets rid of the need for internal_test.go to bridge.

While here, remove the reorganization test since it is much better
handled by the full block tests and is no longer needed and do some
light cleanup on a few other tests.

The full block tests had to remain in the separate test package since it
is a circular dependency otherwise.  This did require duplicating some
of the chain setup code, but given the other benefits this is
acceptable.
2017-08-19 23:05:17 -05:00
Dave Collins
f4fe6c373e blockchain: Convert seq lock tests to synthetic.
This introduces the concept of a synthetic block chain that can be used
in the tests to avoid needing setup a full blown chain instance with a
database and generate valid blocks and converts the sequence lock tests
in TestCalcSequenceLock to use it.

Not only does this speed up the test execution time, but it allows the
dependency on rpctest to be removed which will allow the sequence locks
tests to be consolidated into the main package without creating a
circular dependency.
2017-08-19 22:29:22 -05:00
Dave Collins
2a4be16b6c blockchain: Improve and correct chainview set tip.
This modifies the function to set the tip in the new chainview code to
bulk copy existing nodes when it needs to expand the cap rather than
simply creating a new empty slice and allowing the walk code below it to
repopulate it.  This is a nice optimization since, in practice, most of
the time expanding the cap is only required when the active chain is
being extended after having run for a while which means the end result
is that it will be able to bulk copy all the nodes and just add the most
recent one versus having to walk them all and add them back.

Also, while here expand the tests for setting the tip to ensure the
nodes contained in the resulting view are correct after forcing the
resizes and correct a bug they exposed where changing between a
longer-shorter-longer chain where the longer chain is the same chain
could result in not populating the view correctly.

Finally, update the fake nodes generated by the tests to use a
nonce generated by a deterministic prng in order to ensure the hashes of
all fake nodes are unique, but reproducible.
2017-08-19 22:17:30 -05:00
Anatoli Babenia
2804f4cffe Work on review comments
https://github.com/btcsuite/btcd/pull/1007#discussion_r133563489
2017-08-19 21:10:58 -05:00
Anatoli Babenia
55e0d5c298 Show info when JSON-RPC is not available 2017-08-19 21:10:58 -05:00
Dave Collins
2a753ae9c7 btcec: Regenerate and update precomputed data.
This regenerates the precomputed secp256k1 byte points used to optimize
scalar multiplication.  This should have been done as part of the
normalization correction.
2017-08-18 12:53:28 -05:00
Dave Collins
1d77a611e9 blockchain: Implement new chain view.
This implements a new type in the blockchain package that takes
advantage of the fact that all block nodes are now in memory to provide
a flat view of a specific chain of blocks (a specific branch of the
overall block tree) from a given tip all the way back to the genesis
block along with several convenience functions such as efficiently
comparing two views, quickly finding the fork point (if any) between two
views, and O(1) lookup of the node at a specific height.

The view is not currently used, but the intent is that the code will be
refactored to make use of these views to simplify and optimize several
areas such as best chain selection and reorg logic and finding successor
nodes.  They will also greatly simplify the process of disconnecting the
download logic from the connection logic.

A comprehensive suite of tests is provided to ensure the chain views
behave correctly.
2017-08-17 17:04:39 -05:00
Dave Collins
7e5e6b4610 blockchain: Cleanup a few sequence lock tests.
This cleans up the test in TestCalcSequenceLock in the following ways:
- Use calculated values instead of magic value so it is easier to update
  the tests as needed
- Make tests match the comments
- Change comments to be more consistent and fix some grammar errors
- Set mempool flag for unconfirmed tx tests since they are intended to
  mimic transactions in the mempool
2017-08-16 15:54:03 -05:00
Dave Collins
614b799198 rpcclient: Merge btcrpcclient repo. 2017-08-15 20:09:19 -05:00
Dave Collins
074b2374b8 Import btcrpcclient repo into rpcclient directory.
This commit contains the entire btcrpcclient repository along with
several changes needed to move all of the files into the rpcclient
directory in order to prepare it for merging.  This does NOT update btcd
or any of the other packages to use the new location as that will be
done separately.

- All import paths in the old btcrpcclient files have been changed to
  the new location
- All references to btcrpcclient as the package name have been changed to
  rpcclient
2017-08-15 19:51:58 -05:00
Dave Collins
72f2a3fe49 blockchain: Optimize checkpoint handling.
This modifies the code that determines the most recently known
checkpoint to take advantage of recent changes which make the entire
block index available in memory by only storing a reference to the
specific node in the index that represents the latest known checkpoint.

Previously, the entire block was stored and new checkpoints required
loading it from the database.
2017-08-15 17:06:16 -05:00
Dave Collins
349450379f blockchain: Remove threshold state db cache.
This completely removes the threshold state database caching code since
it can very quickly be calculated at startup now that the entire block
index is loaded first.
2017-08-15 16:55:40 -05:00
Dave Collins
296fa0a5a0 blockchain: Convert to full block index in mem.
This reworks the block index code such that it loads all of the headers
in the main chain at startup and constructs the full block index
accordingly.

Since the full index from the current best tip all the way back to the
genesis block is now guaranteed to be in memory, this also removes all
code related to dynamically loading the nodes and updates some of the
logic to take advantage of the fact traversing the block index can
longer potentially fail.  There are also more optimizations and
simplifications that can be made in the future as a result of this.

Due to removing all of the extra overhead of tracking the dynamic state,
and ensuring the block node structs are aligned to eliminate extra
padding, the end result of a fully populated block index now takes quite
a bit less memory than the previous dynamically loaded version.

The main downside is that it now takes a while to start whereas it was
nearly instant before, however, it is much better to provide more
efficient runtime operation since that is its ultimate purpose and the
benefits far outweigh this downside.

Some benefits are:

- Since every block node is in memory, the recent code which
  reconstructs headers from block nodes means that all headers can
  always be served from memory which is important since the majority of
  the network has moved to header-based semantics
- Several of the error paths can be removed since they are no longer
  necessary
- It is no longer expensive to calculate CSV sequence locks or median
  times of blocks way in the past
- It will be possible to create much more efficient iteration and
  simplified views of the overall index
- The entire threshold state database cache can be removed since it is
  cheap to construct it from the full block index as needed

An overview of the logic changes are as follows:

- Move AncestorNode from blockIndex to blockNode and greatly simplify
  since it no longer has to deal with the possibility of dynamically
  loading nodes and related failures
- Rename RelativeNode to RelativeAncestor, move to blockNode, and
  redefine in terms of AncestorNode
- Move CalcPastMedianTime from blockIndex to blockNode and remove no
  longer necessary test for nil
- Change calcSequenceLock to use Ancestor instead of RelativeAncestor
  since it reads more clearly
2017-08-15 15:42:34 -05:00
Jim Posen
28606122c3 main: Reduce shared state between server and blockManager.
Instead of having both server and blockManager be aware of the
txProcessed and blockProcessed channels, now the server passed them as
method arguments to blockProcessor.
2017-08-15 15:41:59 -05:00
Jim Posen
1b50c7300f peer: Test that witnessEnabled is set properly on peer connection. 2017-08-15 12:08:49 -07:00
Jim Posen
095bba1a25 peer: Move IsWitnessEnabled() from serverPeer to Peer.
serverPeer is a problematic struct because it is local to the main
package.
2017-08-15 11:34:17 -07:00
Dave Collins
3b5c2baa2e blockchain: Consistency pass on subscribe code.
This takes care of a few minor nits on the recently merged subscribe
code.  In particular:

- Avoid extra unnecessary allocation on notifications slice
- Avoid defer overhead on notification mutex in sendNotifications
- Make test function comment start with the name of the function per
  Effective Go guidelines
- Use constant for number of subscribers in test
- Don't exceed column 80 in test print
2017-08-15 11:12:56 -05:00
Dave Collins
c5c46376ba rpcserver: Refactor listener logic to server.
This refactors the RPC server to accept and take ownership of already
configured listeners and refactors the logic to setup those listeners to
the server.  This mirrors the logic used by the connection manager and
is desirable since it is another step closer to being able to split the
RPC server code out into a separate package and will make it much easier
to internally test since it allows creating mock listeners.
2017-08-15 01:07:38 -05:00
Dave Collins
ff700325ac rpcserver: Don't use activeNetParams.
This modifies all of the RPC code to use the chain parameters that are
associated with the RPC server instead of the global activeNetParams and
thus moves one step closer to being able to split the RPC server out
into a separate package.
2017-08-15 00:48:41 -05:00
Dave Collins
a7a1029445 rpcserver: Decouple from server.
This decouples the RPC server from the internal btcd server to move
closer to being able to split it out into a separate package.

In order to accomplish this, it introduces an rpcserverConfig type and
several new interfaces, named rpcserverPeer, rpcserverConnManager, and
rpcserverBlockManager, which are necessary to break the direct
dependencies on the main server and block manager instances.

It also adds concrete implementations of the new interfaces and uses
them to configure the RPC server.

Ultimately, the RPC server should ideally be decoupled even more such
that all of the types in the configuration struct use interfaces instead
of the concrete types.  Doing this would make the RPC server much easier
to internally test since it would allow creating lightweight stubs for
the various pieces.
2017-08-14 23:01:07 -05:00
Jim Posen
ca9baf77ec blockchain: Fix race condition in notifications_test 2017-08-14 22:48:32 -05:00
Jim Posen
b71d6c3010 Create blockManagerConfig struct passed to newBlockManager.
The config struct accepts an instance of server as an implementation
of the new PeerNotifier wrapping interface.
2017-08-14 20:19:02 -07:00
Jim Posen
49949d4c96 Remove references from blockManager to rpcServer.
Instead of having the block manager notify the RPC server about
accepted, connected, and disconnected blocks, the RPC server will
directly listen for notifications from the blockchain.
2017-08-14 20:14:42 -07:00
Jim Posen
22de1f6d08 Create blockmanager with reference to txMemPool.
The objective is to remove the reference from blockManager to
server. Since the blockManager is responsible for keeping the mempool
in sync, it should have a direct reference to it.
2017-08-14 20:14:42 -07:00
Jim Posen
19b42c3c9b Allow multiple subscribers to blockchain notifications.
The BlockChain struct emits notifications for various events, but
it is only possible to register one listener. This changes the
interface and implementations to allow multiple listeners.
2017-08-14 22:03:24 -05:00
Jim Posen
bc36cf51c6 peer: Don't send unsupported reject to old peers. 2017-08-14 16:58:57 -05:00
Dave Collins
05bdf3b741 mempool: Remove potential negative locktime check.
This removes the standardness check to reject transactions with a lock
time greater than a maxint32 because the old bitcoind nodes which it was
designed to protect against are no longer valid for other reasons and
thus there are no longer any of them on the network to worry about.
2017-08-14 16:49:08 -05:00
Jim Posen
f4d376d7af peer: Improve address sampling in PushAddrMsg.
This changes fixes shuffling algorithm in PushAddrMsg to select a
uniformly random subset of the given addresses using Fisher-Yates.
2017-08-14 15:44:08 -05:00
Dave Collins
fd081f5ae4 rpcserver: Cleanup getheaders handler.
This makes the code for getheaders more consistent with the rest of the
code in terms of making use of existing error functions and using the
same RPC error codes as other handlers.

While here, it also performs the fetching of headers directly instead of
using a function from server which makes it more tightly coupled.
2017-08-14 11:57:06 -05:00
Dave Collins
19eada0b4b blockchain: Combine ErrDoubleSpend & ErrMissingTx.
This replaces the ErrDoubleSpend and ErrMissingTx error codes with a
single error code named ErrMissingTxOut and updates the relevant errors
and expected test results accordingly.

Once upon a time, the code relied on a transaction index, so it was able
to definitively differentiate between a transaction output that
legitimately did not exist and one that had already been spent.

However, since the code now uses a pruned utxoset, it is no longer
possible to reliably differentiate since once all outputs of a
transaction are spent, it is removed from the utxoset completely.
Consequently, a missing transaction could be either because the
transaction never existed or because it is fully spent.
2017-08-14 11:40:39 -05:00
Jim Posen
e736ae125d Read RPC username/pass from correct config file for btcctl defaults.
If no existing btcctl.conf file exists, btcctl creates a default one
using the RPC username and password from the btcd.conf. If the
--wallet flag is passed, however, it should read from btcwallet.conf
instead.

https://github.com/btcsuite/btcd/issues/875.
2017-08-14 04:07:17 -05:00
Olaoluwa Osuntokun
01f26a142b build: update glide.lock to target latest btcutil commit 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
1405670930 integration/rpctest: update API usage due to segwit 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
fad0d1d56c blockchain: only populate hashcache if tx is segwitty 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
f5dec67086 mining: update GBT generation to include witness commitment
This commit updates the block template generation logic to only include
witness transactions once the soft-fork has activated and to also
include the OP_RETURN witness commitment (with additional block weight
accounting).
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
728c0a4398 blockchain: guard enforcement of segwit rules by a BIP0009 state check
This commit updates the new segwit validation logic within block
validation to be guarded by an initial check to the version bits state
before conditionally enforcing the logic based off of the state.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
b1893ed228 chaincfg+integration: add BIP0009 deployment parameters for segwit
This commit adds set of BIP0009 (Version Bits) deployment parameters
for all networks detailing the activation parameters for the segwit
soft-fork.

Additionally, the BIP0009 integration test has been updated to test for
the proper transitioning of version bits state for the segwit soft
fork. Finally, the `getblockchaininfo` test has also been updated to
properly display the state of segwit.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
1244c45b88 mining+config: modify GBT mining to limit by weight, add witness commitment
This commit modifies the existing block selection logic to limit
preferentially by weight instead of serialized block size, and also to
adhere to the new sig-op cost limits which are weighted according to
the witness discount.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
8b130ec4ea btcjson: update RPC calls to return segwit related data 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
26ff8ddce4 mempool: modify mempool sanity checks to be segwit aware 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
137aabd631 blockchain/indexers: add addrindex support for p2wkh and p2wsh 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
d0768abcc4 BIP0141+blockchain: implement segwit block validation rules
This commit implements the new block validation rules as defined by
BIP0141. The new rules include the constraints that if a block has
transactions with witness data, then there MUST be a commitment within
the conies transaction to the root of a new merkle tree which commits
to the wtxid of all transactions. Additionally, rather than limiting
the size of a block by size in bytes, blocks are now limited by their
total weight unit. Similarly, a newly define “sig op cost” is now used
to limit the signature validation cost of transactions found within
blocks.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
1b80b334bc BIP0141+blockchain: add functions for extracting and validating witness commitment 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
d38ae9ca0b BIP0141+blockchain: implement tx/block weight calculation funcitons
This commit implements the new “weight” metric introduced as part of
the segwit soft-fork. Post-fork activation, rather than limiting the
size of blocks and transactions based purely on serialized size, a new
metric “weight” will instead be used as a way to more accurately
reflect the costs of a tx/block on the system. With blocks constrained
by weight, the maximum block-size increases to ~4MB.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
a411bbcf85 chaincfg: add address bytes for p2wsh and p2wkh addresses 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
1d01c657dd BIP0141+wire: add a WitnessHash method to tx 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
3b4b4e7942 txscript: update reference tests to include new segwit related cases 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
ff6cb25e89 txscript: convert all new segwit error types to ErrorCode's 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
80dd96ac5d txscript: add new post segwit policies to standard script flags 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
0a7bbda6dd txscript: add verification of the post-segwit pub key type constraint
This commit adds verification of the post-segwit standardness
requirement that all pubkeys involved in checks operations MUST be
serialized as compressed public keys. A new ScriptFlag has been added
to guard this behavior when executing scripts.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
9367aedfd7 txscript: add verification of the post-segwit minimal if policy
This commit modifies the op-code execution for OP_IF and OP_NOTIF to
enforce the additional “minimal if” constraints which require the
top-stack item when the op codes are encountered to be either an empty
vector, or exactly [0x01].
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
65feec33e0 btcec: add new IsCompressedPubKey function
This commit adds a new function to btcec: IsCompressedPubKey. This
function returns true iff the passed serialized public key is encoded
in compressed format.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
9054ef8354 BIP 147: enforce NULLDUMMY w/ segwit verification
This commit implements the flag activation portion of BIP 0147. The
verification behavior triggered by the NULLDUMMY script verification
flag has been present within btcd for some time, however it wasn’t
activated by default.

With this commit, once segwit has activated, the ScriptStrictMultiSig
will also be activated within the Script VM. Additionally, the
ScriptStrictMultiSig is now a standard script verification flag which
is used unconditionally within the mempool.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
aaf187427e BIP0141+txscript: implement witness program validation
This commit implements full witness program validation for the
currently defined version 0 witness programs. This includes validation
logic for nested p2sh, p2wsh, and p2wkh. Additionally, when in witness
validation mode, an additional set of constrains are enforced such as
using the new sighash digest algorithm and enforcing clean stack
behavior within witness programs.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
b564111aff txscript: fix off-by-one error due to new OP_CODESEPARATOR behavior in segwit
This commit fixes an off-by-one error which is only manifested by the
new behavior of OP_CODESEPARATOR within sig hashes triggered by the
segwit behavior. The current behavior within the Script VM
(txscript.Engine) is known to be fully correct to the extent that it has
been verified. However, once segwit activates a consensus divergence
would emerge due to *when* the program counter was incremented in the
previous code (pre-this-commit).

Currently (pre-segwit) when calculating the pre-image to a transaction
sighash for signature verification, *all* instances of OP_CODESEPARATOR
are removed from the subScript being signed before generating the final
sighash. SegWit has additional nerfed the behavior of OP_CODESEPARATOR
by no longer removing them (and starting after the last instance), but
instead simply starting the subScript to be directly *after* the last
instance of an OP_CODESEPARATOR within the pkScript.

Due to this new behavior, without this commit, an off-by-one error
(which only matters post-segwit), would cause txscript to generate an
incorrect subScript since the instance of OP_CODESEPARATOR would remain
as part of the subScript instead of being sliced off as the new behavior
dictates. The off-by-one error itself is manifested due to a slight
divergence in txscript.Engine’s logic compared to Bitcoin Core.  In
Bitcoin Core script verification is as follows: first the next op-code
is fetched, then program counter is incremented, and finally the op-code
itself is executed. Before this commit, btcd flipped the order
of the last two steps, executing the op-code *before* the program
counter was incremented.

This commit fixes the post-segwit consensus divergence by incrementing
the program-counter *before* the next op-code is executed. It is
important to note that this divergence is only significant post-segwit,
meaning that txscript.Engine is still consensus compliant independent of
this commit.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
653459c810 BIP0141+txscript: implement signature operation cost calculations 2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
469e53ca27 BIP0141+txscript: awareness of new standard script templates, add helper funcs
This commit introduces a series of internal and external helper
functions which enable the txscript package to be aware of the new
standard script templates introduced as part of BIP0141. The two new
standard script templates recognized are pay-to-witness-key-hash
(P2WKH) and pay-to-witness-script-hash (P2WSH).
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
98cae74275 BIP0143+txscript: add segwit sighash, signing, and HashCache integration
This commit implements most of BIP0143 by adding logic to implement the
new sighash calculation, signing, and additionally introduces the
HashCache optimization which eliminates the O(N^2) computational
complexity for the SIGHASH_ALL sighash type.

The HashCache struct is the equivalent to the existing SigCache struct,
but for caching the reusable midstate for transactions which are
spending segwitty outputs.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
0db14c740b BIP0144: properly fetch witness data from witness-enabled peers
This commit modifies the logic within the block manager and service to
preferentially fetch transactions and blocks which include witness data
from fully upgraded peers.

Once the initial version handshake has completed, the server now tracks
which of the connected peers are witness enabled (they advertise
SFNodeWitness). From then on, if a peer is witness enabled, then btcd
will always request full witness data when fetching
transactions/blocks.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
7a1456aae5 BIP0144+peer: specify the wire encoding type when reading/writing messages
This commit modifies the base peer struct to ascertain when a peer is
able to understand the new witness encoding, and specify the peer’s
supported encoding explicitly before/after the version handshake.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
192bfbf123 BIP0144+wire: implement witness encoding/decoding for transactions
This commit implements the new witness encoding/decoding for
transactions as specified by BIP0144. After segwit activation, a
special transaction encoding is used to signal to upgraded nodes that
the transaction being deserialized bares witness data. The prior
BtcEncode and BtcDecode methods have been extended to be aware of the
new signaling bytes and the encoding of witness data within
transactions.

Additionally, a new method has been added to calculate the “stripped
size” of a transaction/block which is defined as the size of a
transaction/block *excluding* any witness data.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
28c5ce8e5c BIP0144+wire: introduce new segregated witness inventory types
This commit adds the new inventory types for segwit which are used by
the responder to explicitly request that transactions/blocks sent for a
particular inv hash should include all witness data.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
48abfdf87c BIP0144+wire: add a MessageEncoding variant for serialization/deserialization
This commit modifies the existing wire.Message interface to introduce a
new MessageEncoding variant which dictates the exact encoding to be
used when serializing and deserializing messages. Such an option is now
necessary due to the segwit soft-fork package, as btcd will need to be
able to optionally encode transactions/blocks without witness data to
un-upgraded peers.

Two new functions have been introduced: ReadMessageWithEncodingN and
WriteMessageWithEncodingN which wrap BtcDecode/BtcEncode with the
desired encoding format.
2017-08-13 23:17:40 -05:00
Olaoluwa Osuntokun
1b359e1131 BIP0144+wire: introduce the new SFNodeWitness service bit
This commit introduces the new SFNodeWitness service bit which has been
added to the protocol as part of BIP0144. The new service bit allows
peers on the network to signal their acceptance and adherence to the
new rules defined as part of the segwit soft-fork package.
2017-08-13 23:17:40 -05:00
Gregory Trubetskoy
1e331153b4 Fix the unknown new rules activation warning 2017-08-09 19:10:28 -04:00
Johan T. Halseth
47885ab870 chaincfg: Add IsBech32SegwitPrefix utility method.
The IsBech32SegwitPrefix method takes a string prefix and
determines if it is a valid prefix for a Bech32 encoded segwit
address for any of the default or registered networks.
2017-07-08 10:26:15 +02:00
Johan T. Halseth
9822ffad68 add Bech32HRPSegwit (human-readable part for segwit addresses) to chaincfg.Params 2017-07-05 16:33:50 -05:00
Ruben de Vries
c72658166a add InvalidateBlock RPC command 2017-07-04 15:56:36 +02:00
Ricardo Velhote
948d80b198 New RPC command to display the uptime of the server
Version 0.15.0 of Bitcoin Core will include a new RPC command that will
allow us to obtain the amount of time (in seconds) that the server has
been running.
2017-07-03 00:07:27 +01:00
Dave Collins
6487ba1047 TravisCI: Don't run tests with race.
This modifies the goclean.sh script that is executed on Travis to
only run the tests without the race detector.

While it is nice to run the race detector on the tests, unfortunately
there is a limit to the number of goroutines that can be launched while
running it.  Since Travis is now much slower than it once was, this
causes a ton of false positive failures.
2017-07-01 16:13:35 -05:00
Dave Collins
9f0e66ee3f blockchain: Correct invalid assertion.
This corrects the assertion in the decodeSpentTxOut function so it does
not improperly cause a panic when unwinding transactions during a reorg
under certain circumstances.  In particular, the provided transaction
version that is passed when a stxo entry does not exist is now -1 in
order to properly distinguish it from the zero value.

It also updates the tests accordingly.

This was discovered by the reorg on testnet from block
00000000000018c58c2d2816f03dac327d975a18af6edf1a369df67ecddaf816 to
0000000000001c1161a367156465cc6226e9f862d9c585f94db5779fdf5455ff.
2017-07-01 02:25:41 -05:00
Dave Collins
01cb59c67d multi: Simplify code per gosimple linter.
This simplifies the code based on the recommendations of the gosimple
lint tool.
2017-07-01 02:25:10 -05:00
Josh Rickmar
a6965d493f all: Remove seelog logger.
The btclog package has been changed to defining its own logging
interface (rather than seelog's) and provides a default implementation
for callers to use.

There are two primary advantages to the new logger implementation.

First, all log messages are created before the call returns.  Compared
to seelog, this prevents data races when mutable variables are logged.

Second, the new logger does not implement any kind of artifical rate
limiting (what seelog refers to as "adaptive logging").  Log messages
are outputted as soon as possible and the application will appear to
perform much better when watching standard output.

Because log rotation is not a feature of the btclog logging
implementation, it is handled by the main package by importing a file
rotation package that provides an io.Reader interface for creating
output to a rotating file output.  The rotator has been configured
with the same defaults that btcd previously used in the seelog config
(10MB file limits with maximum of 3 rolls) but now compresses newly
created roll files.  Due to the high compressibility of log text, the
compressed files typically reduce to around 15-30% of the original
10MB file.
2017-06-19 16:46:50 -04:00
Josh Rickmar
45b9cb481d Track btclog API updates. 2017-06-19 16:43:38 -04:00
Jimmy Song
1bdb713285 btcec: Slightly optimize NAF and add several tests.
This slightly optimizes the NAF function by avoiding returning the
unused bit when there is not a carry.

It also adds a bunch of additional unit tests which I made while
debugging.
2017-06-07 20:43:48 -05:00
Dave Collins
1238b7e55a btcec: Optimize and correct normalize.
This modifies the normalize function of the internal field value to
both optimize it and address an issue where the reduction could
lead to an incorrect result with a small range of values.  It also adds
tests to ensure the behavior is correct.

The following benchmark shows the relative speedups as a result of the
optimization on my system.  In particular, the changes result in
approximately a 14% speedup in Normalize, which ultimately translates to
a 2% speedup in signature verifies.

benchmark                        old ns/op     new ns/op     delta
--------------------------------------------------------------------
BenchmarkAddJacobian             1364          1289          -5.50%
BenchmarkAddJacobianNotZOne      3150          3091          -1.87%
BenchmarkScalarBaseMult          134117        132816        -0.97%
BenchmarkScalarBaseMultLarge     135067        132966        -1.56%
BenchmarkScalarMult              411218        402217        -2.19%
BenchmarkSigVerify               671585        657833        -2.05%
BenchmarkFieldNormalize          36.0          31.0          -13.89%
2017-06-07 20:43:36 -05:00
Dave Collins
711e7dbb2e btcec: Add benchmark for field normalization. 2017-06-07 20:43:27 -05:00
Steven Roose
ef87de9d88 Fix comparator that could cause a panic 2017-06-07 17:59:33 -05:00
Steven Roose
bf43e56f2f Fix warnings from ineffassign
I left one at the end of fullblocktest, since I suspected the unused
variable assignments there were set for the possibility of extending the
tests.
2017-06-07 17:59:33 -05:00
Steven Roose
3d0dfed40b Fix a ton of typos accumulated over time 2017-05-30 16:59:51 +02:00
Dave Collins
b14827fd64 Update markdown files for GFM changes.
The github markdown interpreter has been changed such that it no longer
allows spaces in between the brackets and parenthesis of links.  This
updates the markdown files accordingly.

While here, it also corrects a couple of inconsistencies in regards to
other README.md files in the project.
2017-05-25 12:55:45 -05:00
Dave Collins
9918e2a561 multi: Update markdown files for GFM changes.
The github markdown interpreter has been changed such that it no longer
allows spaces in between the brackets and parenthesis of links and now
requires a newline in between anchors and other formatting.  This
updates all of the markdown files accordingly.

While here, it also corrects a couple of inconsistencies in some of the
README.md files.
2017-05-25 12:06:16 -05:00
Dave Collins
f8673776ab database: Update overview documentation in doc.go.
This updates the overview documentation to remove reference of the
outside Namespace bits.
2017-05-21 18:22:33 -05:00
Steven Roose
45967f8641 btcctl: Inherit notls config from btcd.conf 2017-05-18 12:18:30 -05:00
Dave Collins
1fee394e08 btcjson: Update GetNetworkInfoResult.
This updates the GetNetworkInfoResult structure to include the latest
fields added to Core for compatibility purposes.

While here, also move the definitions of the subtypes for the result
before their use for consistency.
2017-05-18 09:12:05 -05:00
Dave Collins
7d3ca6df7b chaincfg: Remove bitseed.xf2.org DNS seed.
This DNS seed does not appear to resolve IPv6 and the returned addresses
rarely seem correspond to functioning nodes.
2017-05-18 05:27:55 -05:00
Steven Roose
53f55a4634 config: Add user agent comments flag --uacomment
Just like Core's -uacomment, this flag allows to specify user agent
comments like defined in BIP 14.
2017-05-17 13:22:26 +02:00
Dave Collins
40f4997b95 addrmgr: Fix a couple of typos in overview doc. 2017-05-15 14:26:18 -05:00
Dave Collins
b85d2dc7f0 peer: Use prng to generate self detection nonce.
This changes the nonce generated to detect self connections over to use
pseudo randoms instead of a cryptographically random nonce.

There is really not a good reason for it to be cryptographically strong,
using the prng is much faster, and the prng also doesn't burn entropy.
2017-05-15 14:20:57 -05:00
Dave Collins
8efd6b15ed peer: Remove unused version sent field.
This removes the field that tracks whether the version has been sent
since it is no longer used after the version negotiation was separated
from the main read and write code.
2017-05-13 03:48:14 -05:00
Dave Collins
1ae306021e rpctest: Make subpackage of integration.
This makes the rpctest package a subpackage of the integration package
since its primary purpose is for integration testing.
2017-05-11 15:17:29 -05:00
Olaoluwa Osuntokun
89728419b0 integration: add integration tests for the CSV-package soft-fork 2017-05-10 15:37:19 -07:00
Olaoluwa Osuntokun
75569f599f blockchain: enforce CSV soft-fork validation based on versionbits state
This commit modifies the existing block validation logic to examine the
current version bits state of the CSV soft-fork, enforcing the new
validation rules (BIPs 68, 112, and 113) accordingly based on the
current `ThesholdState`.
2017-05-10 15:37:11 -07:00
Olaoluwa Osuntokun
7d0b8081dc rpcserver: add csv case for getblockchaininfo 2017-05-10 15:37:07 -07:00
Olaoluwa Osuntokun
d767af1509 blockchain: update calcSequenceLocks test to activate CSV first 2017-05-10 15:37:04 -07:00
Olaoluwa Osuntokun
ca5e14da6a rpctest: publicly export the CreateBlock funciton
This commit publicly exports the CreateBlock function as it can be very
useful for generating blocks for tests. Additionally, the behavior of
the function has been modified slightly to build off of the genesis
block for the specified chain if the `prevBlock` paramter is nil.
2017-05-10 15:37:00 -07:00
Olaoluwa Osuntokun
528cc07a00 chaincfg: add BIP0009 deployment parameters for the CSV soft-fork
This commit adds BIP-9 deployment parameters for all registered
networks for the CSV soft-fork package.

The mainnet and testnet parameters have been set in accordance to the
finalized BIPs.

For simnet, and the regression net, the activation date is back-dated
in order to allow signaling for the soft-fork at any time. Additionally
the expiration time for simnet and regrets has been set to
math.MaxInt64, meaning they’ll never expire.
2017-05-10 15:36:55 -07:00
Dave Collins
0ea4a6ebd4 multi: Switch to upstream golang.org/x/crypto.
Now that glide is used for version management and a specific commit of
the upstream repository can be locked it is no longer necessary to
maintain a fork of the package specifically to keep a stable dependency.

While here, update the glide dependency for btcutil as well since it was
switched to use the upstream path as well.
2017-05-09 11:35:01 -05:00
esemplastic
2a01456189 Fix docs/README.md
Invalid tokens: github had made some changes the past months, the old style is not rendering at all, so I fixed that for you. 

Other contributors can do the same for all of the project's documents.

Thanks.
2017-05-07 22:04:30 -05:00
Alex
4b348c1d33 rpctest: Add P2PAddress() for Harness to get P2P listen address 2017-03-22 14:54:42 -06:00
Alex Bosworth
efb4ab430c main: Improve comment for headers peer message. 2017-03-22 15:45:28 -05:00
Dave Collins
6788df79f1 TravisCI: Only run tests once.
This modifies the goclean.sh script that is executed on Travis to
only run the tests once.

While it is nice to see coverage reports in the log, unfortunately it
appears that both the -race and -cover flags can't be used together, and
the tests have grown in complexity such that they are starting to get
close to TravisCI time limits.
2017-03-22 15:34:47 -05:00
Dave Collins
efa50e6abc multi: Simplify code per gosimple linter.
This simplifies the code based on the recommendations of the gosimple
lint tool.

Also, it increases the deadline for the linters to run to 10 minutes and
reduces the number of threads that is uses. This is being done because
the Travis environment has become increasingly slower and it also seems
to be hampered by too many threads running concurrently.
2017-03-22 15:34:13 -05:00
Alex
583684b21b rpcserver: Allow limited user access to getblockheader command. 2017-02-16 11:10:28 -07:00
David Hill
e3e4c726af Add GetMempoolEntry API 2017-02-03 14:27:05 -05:00
Dave Collins
d06c0bb181 blockchain: Use hash values in structs.
This modifies the blockNode and BestState structs in the blockchain
package to store hashes directly instead of pointers to them and updates
callers to deal with the API change in the exported BestState struct.

In general, the preferred approach for hashes moving forward is to store
hash values in complex data structures, particularly those that will be
used for cache entries, and accept pointers to hashes in arguments to
functions.

Some of the reasoning behind making this change is:

- It is generally preferred to avoid storing pointers to data in cache
  objects since doing so can easily lead to storing interior pointers
  into other structs that then can't be GC'd
- Keeping the hash values directly in the block node provides better
  cache locality
2017-02-03 11:36:33 -06:00
Dave Collins
05c7c14023 blockchain: Ignore side chains in exists check.
Since the code base is currently in the process of changing over to
decouple download and connection logic, but not all of the necessary
parts are updated yet, ensure blocks that are in the database, but do
not have an associated main chain block index entry, are treated as if
they do not exist for the purposes of chain connection and selection
logic.
2017-02-03 11:02:35 -06:00
Dave Collins
1ecfea4928 blockchain: Refactor main block index logic.
This refactors the block index logic into a separate struct and
introduces an individual lock for it so it can be queried independent of
the chain lock.
2017-02-01 13:14:41 -06:00
Dave Collins
59169540c3 blockchain: Reconstruct headers from block nodes.
This modifies the block node structure to include a couple of extra
fields needed to be able to reconstruct the block header from a node,
and exposes a new function from chain to fetch the block headers which
takes advantage of the new functionality to reconstruct the headers from
memory when possible.  Finally, it updates both the p2p and RPC servers
to make use of the new function.

This is useful since many of the block header fields need to be kept in
order to form the block index anyways and storing the extra fields means
the database does not have to be consulted when headers are requested if
the associated node is still in memory.

The following timings show representative performance gains as measured
from one system:

new: Time to fetch 100000 headers:   59ms
old: Time to fetch 100000 headers: 4783ms
2017-02-01 12:27:56 -06:00
David Hill
d9241e91a9 btcjson: Add getmempoolentry API 2017-02-01 11:54:51 -06:00
Dave Collins
d552176e1d blockchain: Remove unused block node removal func.
This removes the removeBlockNode function since it is no longer used
after the addition of CLTV removed block node pruning.
2017-02-01 11:25:29 -06:00
Dave Collins
0d0f50c464 blockchain: Remove unused median time calc func.
This removes the CalcPastMedianTime since it is now exposed much more
efficiently via the MedianTime field of the BestState snapshot returned
from the BestSnapshot function.
2017-02-01 10:50:27 -06:00
Dave Collins
5ffd552214 blockchain: Use int64 timestamps in block nodes.
This modifies the block nodes used in the blockchain package for keeping
track of the block index to use int64 for the timestamps instead of
time.Time.

This is being done because a time.Time takes 24 bytes while an int64
only takes 8 and the plan is to eventually move the entire block index
into memory instead of the current dynamically-loaded version, so
cutting the number of bytes used for the timestamp by a third is highly
desirable.

Also, the consensus code requires working with unix-style timestamps
anyways, so switching over to them in the block node does not seem
unreasonable.

Finally, this does not go so far as to change all of the time.Time
references, particularly those that are in the public API, so it is
purely an internal change.
2017-01-31 17:51:29 -06:00
David Hill
9f71f090e6 txscript: Drop the mutex before doing crypto 2017-01-31 13:47:41 -05:00
Dave Collins
554460feda rpctest: Update to use new filtered block ntfns.
This modifies the rpctest harness and its associated memwallet to make
use of the new filter-based notifications since the old notifications
are now deprecated.

It also updates the glide.lock file to require the necessary
btcrpcclient version.
2017-01-31 11:04:43 -06:00
Dave Collins
6a54323258 blockchain: Store side chain blocks in database.
This modifies the blockchain code to store all blocks that have passed
proof-of-work and contextual validity tests in the database even if they
may ultimately fail to connect.

This eliminates the need to store those blocks in memory, allows them to
be available as orphans later even if they were never part of the main
chain, and helps pave the way toward being able to separate the download
logic from the connection logic.
2017-01-31 10:25:43 -06:00
David Hill
ecd348b2a7 btcd: disable mempool requests if bloom filtering is disabled 2017-01-27 20:51:25 -06:00
Dave Collins
eef39394b7 Update ws example to use new filtered blocks ntfns.
The old notifications are now deprecated, so the examples shouldn't use
them.
2017-01-27 17:31:10 -06:00
Alex
47b5478cfc rpcserver: implement rescanblocks command backported from dcrd 2017-01-27 15:32:15 -07:00
Alex
03a8bf2eb4 btcjson/docs: add rescanblocks command backported from dcrd 2017-01-27 15:32:15 -07:00
Alex
4943ed11b3 rpcserver: implement loadtxfilter backported from dcrd 2017-01-27 15:32:15 -07:00
Alex
5c689c8b77 btcjson/docs: add loadtxfilter command backported from dcrd 2017-01-27 15:32:15 -07:00
Alex
abcdfb702a Port rescanblocks JSON-RPC command from dcrd 2017-01-27 14:43:43 -07:00
Alex
a835a9ca8b Port loadtxfilter JSON-RPC command from dcrd 2017-01-27 14:39:20 -07:00
David Hill
db5b9aef91 btcd: fix rebroadcasting of local txs. 2017-01-27 10:28:48 -05:00
David Hill
9bedd7720c btcd: only allow one getaddr request per peer 2017-01-25 12:05:55 -05:00
David Hill
e8b9147ba2 btcjson: Add more fields to ListTransactionsResult
These include Abandoned, BIP125Replaceable, and Trusted.
2017-01-23 15:56:45 -05:00
Dave Collins
07f8c84fb1 TravisCI: Use release version of glide.
This modifies the .travis.yml file to install a release version of glide
instead of using its latest master branch.  This will prevent upstream
changes from inadvertently breaking the CI builds.
2017-01-23 13:09:42 -06:00
Dave Collins
c065733c31 multi: Improvements to configurable checkpoints.
This contains a bit of cleanup and additional logic to improve the
recently-added ability to specify additional checkpoints via the
--addcheckpoint option.

In particular:
- Improve error messages in the checkpoint parsing
- Correct the mergeCheckpoints function to weed out duplicate height
  checkpoints while using the most-recently provided one as described by
  its comment
- Add an assertion to blockchain.New that the provided checkpoints are
  sorted as required
- Keep comments to 80 columns and use two spaces after periods in them to
  be consistent with the rest of the code base
- Make the entry in doc.go match the actual btcd -h output
2017-01-23 12:07:54 -06:00
Dave Collins
28d42ba23c build: Exclude .glide from test coverage.
The most recent version of glide creates a working directory named
.glide that includes .go files and thus must be excluded from the find
operation that generates merged test coverage of all packages.
2017-01-23 11:42:38 -06:00
Steven Roose
c2af640c95 blockchain: Allow adding additional checkpoints
Introduces a `--checkpoint` flag that allows the user to specify
additional checkpoints or override the default ones provided in the
chain params.
2017-01-18 23:56:43 +01:00
Javed Khan
8caa921ac3 server: Fix connections to onion nodes.
A DNS lookup was being attempted on onion addresses causing
connections to fail. This has been fixed by introducing type
onionAddr (which implements a net.Addr interface) and passing
it to btcdDial.

Also, the following onion related fixes have been made:

* getaddednodeinfo - updated to handle onion addrs.
* TorLookupIP - fixed err being shadowed.
* newServer - rename tcpAddr to netAddr
* addrStringToNetAddr - skip if host is already an IP addr.
* addrStringToNetAddr - err if tor is disabled
* getaddednodeinfo - check if host is already an IP addr.
2017-01-18 21:40:41 +05:30
Javed Khan
91b7f5c1c8 config: Refactor tor config options.
* Remove unnecessary onionlookup func.
* Disallow --noonion and --onion flag combination.
* Tor isolation flag - prefer onion proxy.
* Proper argument names in cfg.oniondial closure.
* btcdLookup - error on onion address.
* Bridge mode - use onion for lookup
2017-01-18 13:32:31 +05:30
David Hill
e15ac5024a travis: test against latest releases.
While here, clean up goclean.sh.
2017-01-15 08:38:08 -05:00
David Hill
0efea24aa6 txscript: Implement ScriptVerifyNullFail
ScriptVerifyNullFail defines that signatures must be empty if a
CHECKSIG or CHECKMULTISIG operation fails.

This commit also enables ScriptVerifyNullFail at the mempool policy
level.
2017-01-13 14:19:11 -05:00
Dave Collins
153dca5c1e txscript: Convert reference tests to new format.
This updates the data driven transaction script tests to use the most
recent format and test data as implemented by Core so the test data can
more easily be updated and help prove cross-compatibility correctness.

In particular, the new format combines the previously separate valid and
invalid test data files into a single file and adds a field for the
expected result.  This is a nice improvement since it means tests can
now ensure script failures are due to a specific expected reason as
opposed to only generically detecting failure as the previous format
required.

The btcd script engine typically returns more fine grained errors than
the test data expects, so the test adapter handles this by allowing
expected errors in the test data to be mapped to multiple txscript
errors.

It should also be noted that the tests related to segwit have been
stripped from the data since the segwit PR has not landed in master yet,
however the test adapter does recognize the new ability for optional
segwit data to be supplied, though it will need to properly construct
the transaction using that data when the time comes.
2017-01-12 13:13:21 -06:00
Dave Collins
fdc2bc867b txscript: Significantly improve errors.
This converts the majority of script errors from generic errors created
via errors.New and fmt.Errorf to use a concrete type that implements the
error interface with an error code and description.

This allows callers to programmatically detect the type of error via
type assertions and an error code while still allowing the errors to
provide more context.

For example, instead of just having an error the reads "disabled opcode"
as would happen prior to these changes when a disabled opcode is
encountered, the error will now read "attempt to execute disabled opcode
OP_FOO".

While it was previously possible to programmatically detect many errors
due to them being exported, they provided no additional context and
there were also various instances that were just returning errors
created on the spot which callers could not reliably detect without
resorting to looking at the actual error message, which is nearly always
bad practice.

Also, while here, export the MaxStackSize and MaxScriptSize constants
since they can be useful for consumers of the package and perform some
minor cleanup of some of the tests.
2017-01-12 13:12:39 -06:00
David Hill
283706b3dc chaincfg: add testnet checkpoints 2017-01-12 11:30:13 -05:00
David Hill
406ddd17d3 blockchain: TimeSource is required. 2017-01-12 11:19:51 -05:00
David Hill
7c0fd83c87 btcd: use DialTimeout
If a host is down and doesn't send a TCP RST, the net.Dial function
blocks until the OS times out the connection. Convert to using
DialTimeout with a 30 second default timeout.
2017-01-11 16:54:37 -05:00
Alex Bosworth
2dfda48b94 Correct typo 2017-01-11 13:21:14 -08:00
David Hill
4cb933d035 travis: test against latest patch release. 2017-01-11 15:54:27 -05:00
David Hill
ab0f30c00d mining: drop getwork support.
Since the Midstate is no longer needed, switch to using
crypto/sha256.
2017-01-11 13:51:57 -05:00
Alex
5ce0ed6009 Port getheaders JSON-RPC command from dcrd 2017-01-10 19:47:28 -07:00
Alex
7c44b6472f Port getheaders JSON-RPC command from dcrd 2017-01-10 19:11:18 -07:00
Chris Pacia
765ca28711 Add MerkleBlock deadline to CmdGetData 2017-01-10 12:29:43 -06:00
David Hill
4f12c97d0f Drop btcsuite/go-flags in favor of upstream 2017-01-09 14:10:18 -06:00
Alex
9f962b60d6 Add version command to JSON-RPC API for API versioning 2016-12-22 14:48:53 -05:00
Alex
0519b86a9d Add version command to JSON-RPC API for API versioning 2016-12-22 12:38:38 -05:00
Alex Bosworth
602232500e Update rpcserver.go
correct comment typo
2016-12-13 13:49:52 -08:00
David de Kloet
7a4cc89bbc goclean.sh: Make it more robust
Fail fast if glide or gometalinter isn't available.
And also fail if running glide or gometalinter fails.

Before goclean.sh could pass even if glide or gometalinter wasn't
available at all. It seems that even though we use `set -ex`, their
failure was hidden either inside the $() or behind the pipe.
2016-12-09 23:06:51 +01:00
David de Kloet
4021ae2f6e server.go: Optimize newAddressFunc
Change the order of conditions to avoid calling fmt.Sprintf
unnecessarily.
2016-12-08 22:55:43 +01:00
Dave Collins
f65788d0a1 integration: Add BIP0009 state transition tests
This adds tests which exercise the BIP0009 state transitions using the
newly available soft fork status information in the getblockchaininfo
RPC.

The following is an overview of the tests added:
- Assert the chain height is 0 and the state is ThresholdDefined
- Generate 1 fewer blocks than needed to reach the first state
  transition
  - Assert chain height is expected and state is still ThresholdDefined
- Generate 1 more block to reach the first state transition
  - Assert chain height is expected and state moved to ThresholdStarted
- Generate enough blocks to reach the next state transition window, but
  only signal support in 1 fewer than the required number to achieve
  ThresholdLockedIn
- Assert chain height is expected and state is still ThresholdStarted
- Generate enough blocks to reach the next state transition window with
  only the exact number of blocks required to achieve locked in status
  signalling support
- Assert chain height is expected and state moved to ThresholdLockedIn
- Generate 1 fewer blocks than needed to reach the next state transition
- Assert chain height is expected and state is still ThresholdLockedIn
- Generate 1 more block to reach the next state transition
- Assert chain height is expected and state moved to ThresholdActive

In addition, it updates the existing BIP0009 mining tests to include
extra assertions that the chain height is at the expected height in
addition to checking that the bits are correctly set according to the
expected state.
2016-12-07 10:41:40 -06:00
Dave Collins
cb05e9b9cf rpctest: Prevent race during teardown. 2016-12-07 10:41:35 -06:00
John C. Vernaleo
7903290fd9 Remove -v from go test.
This caused unhelpful clutter in travis output.

Closes #106
2016-12-07 10:14:06 -05:00
John C. Vernaleo
53edcec224 Remove -v from go test.
This caused unhelpful clutter in travis output.

Closes #877
2016-12-07 10:10:06 -05:00
Olaoluwa Osuntokun
7b0380cdd3 rpcserver: implement the getblockchaininfo RPC call 2016-12-06 16:24:51 -08:00
Olaoluwa Osuntokun
ec1c93ecc0 btcjson: update the fields of GetBlockChainInfoResult
This commit updates the fields of GetBlockChainInfoResult to reflect
the current state of the RPC as widely implemented by other full-node
implementations.
2016-12-06 16:24:43 -08:00
David de Kloet
c5751b75a9 wire: Treat NetAddress more like immutable
Replace assignments to individual fields of wire.NetAddress with
creating the entire object at once, as one would do if the type was
immutable.

In some places this replaces the creation of a NetAddress with a
high-precision timestamp with a call to a 'constructor' that converts
the timestamp to single second precision. For consistency, the tests
have also been changed to use single-precision timestamps.

Lastly, the number of allocations in readNetAddress have been reduced by
reading the services directly into the NetAddress instead of first into
a temporary variable.
2016-12-06 20:48:02 +01:00
Dave Collins
712399c0db blockchain: Correct startup threshold state warns.
The thresholdState and deploymentState functions expect the block node
for the block prior to which the threshold state is calculated, however
the startup code which checked the threshold states was using the
current best node instead of its parent.

While here, also update the comments and rename a couple of variables to
help make this fact more clear.
2016-12-03 13:42:26 -06:00
Dave Collins
bc576b13b4 txscript: Only do CSV txver check if enabled.
The CSV consensus rules dictate that the opcode fails when the
transaction version is not at least version 2, however that only applies
if the disable flag is not set in the sequence.

This is not an issue at the current time because we do not yet enforce
CSV at a consensus level, however, I noticed this discrepancy when doing
a thorough audit of the CSV paths due to the ongoing work to add full
consensus-enforced CSV support.

As a result, this must be merged prior to enabling consensus enforcement
for CSV or it would open up the potential for a hard fork.
2016-12-03 12:33:57 -06:00
Dave Collins
318c4760c0 blockchain: Only enforce BIP0030 prior to BIP0034.
This modifies the code to only enforce the fairly expensive BIP0030
(duplicate transcactions) checks when the chain has not yet reached the
BIP0034 activation height (and is not one of the 2 special historical
blocks that break the rule and prompted BIP0034 to being with) since
that BIP made it impossible to create duplicate coinbases and thus
removed the possibility of creating transactions that overwrite older
ones.

This is a rather large optimization because the check is expensive due
to involving a ton of cache misses in the utxoset.  For example, the
following are times it took to perform the BIP0030 check on blocks
425490 - 425502 and a system with a relatively old Hitachi spinner HDD:

block 425490: 674.5857ms
block 425491: 726.5923ms
block 425492: 827.6051ms
block 425493: 680.0863ms
block 425494: 722.0917ms
block 425495: 700.0889ms
block 425496: 647.5823ms
block 425497: 445.0565ms
block 425498: 602.5765ms
block 425499: 375.0476ms
block 425500: 771.0979ms
block 425501: 461.5586ms
block 425502: 603.0766ms

As can be seen from these numbers, this reduces the block validation
time by an average of just over half a second for the given
representative data set and hardware.

Signed-off-by: Dave Collins <davec@conformal.com>
2016-12-02 15:18:07 -06:00
David Hill
2e93ea6ca6 btcjson: Add versionHex to getblock[header] results
rpcserver:  Set versionHex in responses.
2016-12-02 12:49:02 -05:00
Dave Collins
9cdc1b8afd blockchain: Remove isMajorityVersion code.
Now that all softforking is done via BIP0009 versionbits, replace the
old isMajorityVersion deployment mechanism with hard coded historical
block heights at which they became active.

Since the activation heights vary per network, this adds new parameters
to the chaincfg.Params struct for them and sets the correct heights at
which each softfork became active on each chain.

It should be noted that this is a technically hard fork since the
behavior of alternate chain history is different with these hard-coded
activation heights as opposed to the old isMajorityVersion code.  In
particular, an alternate chain history could activate one of the soft
forks earlier than these hard-coded heights which means the old code
would reject blocks which violate the new soft fork rules whereas this
new code would not.

However, all of the soft forks this refers to were activated so far in
the chain history there is there is no way a reorg that long could
happen and checkpoints reject alternate chains before the most recent
checkpoint anyways.  Furthermore, the same change was made in Bitcoin
Core so this needs to be changed to be consistent anyways.
2016-11-30 14:26:13 -06:00
Dave Collins
bfbaff9fae integration: Add BIP9 mining tests. 2016-11-30 13:52:23 -06:00
Dave Collins
25520b40ad rpctest: Update for removal of wire.BlockVersion
This adds constant for the block version to the rpctest package since it
is no longer available via the wire package due to the new BIP0009 code.
2016-11-30 13:52:20 -06:00
Dave Collins
c440584efc Implement infrastructure for BIP0009.
This commit adds all of the infrastructure needed to support BIP0009
soft forks.

The following is an overview of the changes:

- Add new configuration options to the chaincfg package which allows the
  rule deployments to be defined per chain
- Implement code to calculate the threshold state as required by BIP0009
  - Use threshold state caches that are stored to the database in order
    to accelerate startup time
  - Remove caches that are invalid due to definition changes in the
    params including additions, deletions, and changes to existing
    entries
- Detect and warn when a new unknown rule is about to activate or has
  been activated in the block connection code
- Detect and warn when 50% of the last 100 blocks have unexpected
  versions.
- Remove the latest block version from wire since it no longer applies
- Add a version parameter to the wire.NewBlockHeader function since the
  default is no longer available
- Update the miner block template generation code to use the calculated
  block version based on the currently defined rule deployments and
  their threshold states as of the previous block
- Add tests for new error type
- Add tests for threshold state cache
2016-11-30 13:51:59 -06:00
Steven Roose
95e6de00b8 btcd: fix error in mempool response inv counting 2016-11-25 10:30:24 +01:00
Dave Collins
afec1bd124 build: Give linters longer and cleanup goclean.sh.
This modifies the goclean.sh script to expand the deadline for the
linters to run to 4 minutes and ensures the comments match reality.
2016-11-21 09:33:32 -06:00
David de Kloet
34b9721494 docs: Clarify model commit message summary
Add a paragraph about prefixing commit message summaries with the
package/subsystem that they're changing to
code_contribution_guidelines.md.
2016-11-21 09:02:40 -06:00
dskloet
e1b2ceca80 addrmgr: Remove unused param from GetAddress()
addrmgr.GetAddress() had a parameter `class string` originally intended
to support looking up addresses according to some type of filter such as
IPv4, IPv6, and only those which support specific wire.ServiceFlags
(full nodes, nodes that support bloom filters, nodes that support
segwit, etc). But currently the parameter is unused and also has an
inappropriate type `string`.

If it would ever be used, it's easy to add back and should then get an
appropriate type such as something that allows bitflags to be set so
that the caller could request combinations such as peers that support
IPv6, are full nodes, and support bloom filters.
2016-11-21 08:56:46 -06:00
Javed Khan
7f237aa5e5 peer: disconnected peer - return non-nil LocalAddr 2016-11-19 15:05:51 -06:00
Dave Collins
1b800b69cf build: Add unconvert linter to goclean.sh.
This modifies the goclean.sh script to include the unconvert lint tool
in the gometalinter configuration.
2016-11-16 15:20:07 -06:00
Dave Collins
36af96160d Remove unnecessary convs found by unconvert.
This removes all unnecessary typecast conversions as found by the
unconvert linter.
2016-11-16 15:16:16 -06:00
Dave Collins
f9ac1a7786 build: Add gosimple linter to goclean.sh.
This modifies the goclean.sh script to include the gosimple lint tool to
the gometalinter configuration.
2016-11-16 15:07:40 -06:00
Dave Collins
b83f837ad2 Simplify code per gosimple linter.
This simplifies the code based on the recommendations of the gosimple
lint tool.
2016-11-16 15:05:11 -06:00
John C. Vernaleo
5c3e647618 Add gofmt to metalinter.
Fix one issue it caught.

This depends on PR#97
2016-11-16 16:01:47 -05:00
John C. Vernaleo
441bb918ec Modify goclean to use gometalint like btcd 2016-11-16 15:00:41 -06:00
David Hill
b134beb3b7 txscript: reduce allocs in calcSignatureHash 2016-11-16 12:28:22 -06:00
David Hill
187355448a wire: reduce allocs in BlockHash() 2016-11-16 11:29:09 -06:00
David Hill
807d344fe9 Unassign some TODO's 2016-11-15 17:47:33 -06:00
David Hill
d9a674e1b7 btcjson: add ErrRPCClientNodeNotAdded 2016-11-14 18:44:38 -05:00
Steven Roose
7cc630b335 docs: Change error in heading 2016-11-12 14:52:47 +01:00
Dave Collins
0d4546c965 server: Return error on address lookup fails.
This corrects an issue introduced by commit
e8f63bc295 where a failure to lookup a
hostname could lead to a panic in certain circumstances.  An error is
now returned in that case as expected.
2016-11-11 20:54:25 -06:00
Dave Collins
089611a61b peer: No error log on unexpected EOF.
This modifies the error handling path in the peer read loop such that it
will no longer log an error when the error is io.ErrUnexpectedEOF.  This
is being done because that error is almost always the result of a peer
being remotely disconnected and thus it isn't useful information to log.

However, since it might actually be due to a malformed message, a reject
message is still queued up to be sent back to the peer (which will
simply be discarded if the peer disconnected) before it is disconnected.

While it would be ideal to only log if it's not due to a disconnect, and
the code already attempts to handle that, it's not 100% possible to
detect upon the read returning an error without attempting to read again
which will not happen until the next loop iteration.
2016-11-10 19:00:37 -06:00
Olaoluwa Osuntokun
e8f63bc295 connmgr: switch to using net.Addr interface throughout for addresses
This commit modifies the `ConnManager` to use the `net.Add` interface
through the package instead of a plain string to represent and
manipulate addresses. This change makes the package much more general as
users of the package can possibly utilize custom implementations of the
`net.Addr` interface to establish connections.

More precisely, the `ConnReq` struct has been modified to use a net.Addr
instance explicitly, and the `DialFunc` type has also been modified to
take a `net.Addr` directly. This latter change gives functions that
adhere to the `DialFunc` type more flexibility as to exactly how the
connection is established.

Additionally, the `connmgr.Config.GetNewAddress` configuration option
now directly returns a `net.Addr. This change allows the `connmgr` to be
decoupled from all DNS queries which allows callers to preferentially
select more secure methods like performing DNS lookups over a Tor proxy.
2016-11-10 11:22:36 -08:00
Olaoluwa Osuntokun
c6e1d711da review: fix typos 2016-11-09 11:13:10 -08:00
Olaoluwa Osuntokun
01050a2f82 add support for getblockchaininfo 2016-11-06 16:07:47 -08:00
Dave Collins
df33d4340e server: Ensure callbacks use the server peer.
This modifies the signatures of all serverPeer callbacks that are
provided as peer.Listeners to use _ for the first parameter name which
ensures the passed peer can't be used within the function and updates
all references to the server peer.

This helps ensure any overridden methods that might be defined on a
serverPeer will be invoked where directly calling methods on the passed
peer would not.

Also, while here, add a comment to the OnFeeFilter function.
2016-11-04 13:38:13 -05:00
Dave Collins
b65881c137 connmgr: Add tests for new inbound listener logic. 2016-11-04 13:14:50 -05:00
Dave Collins
d98430d8ca connmgr: Implement inbound connection handling.
This modifies the connection manager to provide support for accepting
inbound connections on a caller-provided set of listeners and notify the
caller via a callback.

This is only the minimum work necessary to get inbound support into the
connection manager.  The intention for future commits is to move more
connection-related logic such as limiting the maximum number of overall
connections and banned peer tracking into the connection manager.
2016-11-04 13:14:44 -05:00
Dave Collins
ea9bf748bb connmgr: Remove type defs for callbacks.
This removes the type definitions for the callback functions in favor of
declaring them directly in the Config struct.  This is more consistent
with the rest of the code base and is preferred since it means callers
reviewing the documentation don't have to follow another level of
indirection to figure out the signature.
2016-11-04 13:14:39 -05:00
Dave Collins
2c6f864b55 wire: Change NewNetAddress to accept a *net.TCPConn.
Rather than accepting a net.Addr interface and returning an error when
it's not specifically a *net.TCPConn, just accept a *net.TCPConn
directly so the compiler will assert it.  Also, remove the error return
since it can no longer occur.
2016-11-04 11:06:07 -05:00
Javed Khan
a041b4349b server: Use Disconnect in handleDonePeerMsg 2016-11-04 00:36:13 -05:00
Javed Khan
b320129e9b server: Check max peers before connecting 2016-11-04 00:36:13 -05:00
Javed Khan
d8a6de461f connmgr: Retry only if below target outbound conns 2016-11-04 00:36:13 -05:00
Javed Khan
aca9fc040c connmgr: Rename max outbound to target outbound 2016-11-04 00:36:13 -05:00
Dave Collins
6bb8d297a6 wire: Remove unused NewMsgVersionFromConn.
This function is a legacy function from way back during initial
development.  Nothing actually uses it and it's not very useful anyways
since it requires the connection to be a specific type (net.TCPConn) and
therefore doesn't work right with things that typically provide their
own net.Conn implementation like proxies.
2016-11-03 20:14:16 -05:00
David Hill
2510baac35 btcd: support feefilter requests.
This only adds support for handling remote peer requests.
2016-11-03 14:47:30 -04:00
Dave Collins
fbb49ae349 build: Add unconvert linter to goclean.sh.
This modifies the goclean.sh script to include the unconvert lint tool
in the gometalinter configuration.

It also bumps the deadline to 45 seconds to give slower TravisCI
build servers more breathing room.
2016-11-03 13:32:35 -05:00
Dave Collins
c180551348 build: Add gosimple linter to goclean.sh.
This modifies the goclean.sh script to include the gosimple lint tool to
the gometalinter configuration.
2016-11-03 13:24:06 -05:00
Dave Collins
915fa6639b multi: Simplify code per gosimple linter.
This simplifies the code based on the recommendations of the gosimple
lint tool.
2016-11-03 13:00:35 -05:00
Dave Collins
af524fb3e7 multi: Remove unnecessary convs found by unconvert.
This removes all unnecessary typecast conversions as found by the
unconvert linter.
2016-11-03 11:59:38 -05:00
Dave Collins
aa53c14a19 addrmgr/btcd: Updates for staticcheck results.
This makes two modifications based on the results of staticcheck.

The first is to use a simplified zero value for the time in the addrmgr
package tests.

The second is check any errors when opening a directory while attempting
to perform old version upgrades.
2016-11-03 11:29:08 -05:00
Dave Collins
82cba61bad bmgr: Remove unused struct fields.
Found by structcheck.
2016-11-02 18:50:35 -05:00
Dave Collins
e320330d29 multi: Remove unused code found by deadcode. 2016-11-02 17:37:31 -05:00
John C. Vernaleo
1e38d7fd4b Switch to gometalint in goclean
This switches `goclean` to use `gometalint` instead of running each checking tool one at a time.  The `gometalint` tool runs them concurrently.

More importantly it will allow us to easily add additional linters as desired.
2016-11-02 17:36:32 -05:00
Javed Khan
d1c39edee8 server: Cap max outbound in connmgr cfg at max peers 2016-11-02 15:10:14 -05:00
David Hill
6951d8e235 connmgr: unexport the DynamicBanScore mutex. 2016-11-01 14:53:15 -05:00
Jonathan Gillham
83432785f3 peer: Separate ping ticker functionality from outHandler.
This is part of a series of commits to make the internals of the peer
package more modular, testable and tunable. No functionality has changed.
2016-10-29 23:28:12 +01:00
Dave Collins
760c5299c7 mempool: Modify default orphan tx policy.
The current max orphan transaction size causes problems with dependent
transaction relay due to its artificially small size in relation to the
max standard transaction size.

Consequently, this modifies the orphan transaction policy by increasing
the max size of each orphan to the same value allowed for standard
non-orphan transactions and reducing the default max allowed number of
orphans to 100.

From a memory usage standpoint, the worst case max mem usage prior to
this change was 5MB plus structure and tracking overhead (1000 max
orphans * 5KB max each).  With this, that is raised to 10MB (100 max
orphans * 100KB max each) in the worst case.

It is important to note that the values were originally implemented as a
naive means to control the size of the orphan pool before several of the
recent enhancements which more aggressively remove orphans from the
orphan pool were added, so they needed to be evaluated again.

For a very long time prior to recent changes, the orphan pool would
quickly reach the max allowed worst-case usage and effectively stay
there forever whereas with more recent changes, the actual run-time
orphan pool usage is usually much smaller.

Finally, as another point in favor of this change, as the network has
evolved, nodes have generally become better about orphan management and
as such missing ancestors will typically either be broadcast or mined
fairly quickly resulting in fewer overall orphans.
2016-10-28 15:41:59 -05:00
Dave Collins
6d5714e1b7 server/mempool: Evict orphans on peer disconnect.
This removes any remaining orphan transactions that were sent by a peer
when it disconnects since it is extremely unlikely that the missing
parents will ever materialize from elsewhere.
2016-10-28 15:27:57 -05:00
Dave Collins
2124accf62 mempool: Expose RemoveOrphansByTag function. 2016-10-28 15:27:57 -05:00
Dave Collins
e992d55822 mempool: Associated tag with orphan txns.
This allows a caller-provided tag to be associated with orphan
transactions.  This is useful since the caller can use the tag for
purposes such as keeping track of which peers orphans were first seen
from.

Also, since a parameter is required now anyways, it associates the peer
ID with processed transactions from remote peers.
2016-10-28 15:27:57 -05:00
David Hill
2615fa0849 mempool: Return type TxDesc instead of type btcutil.Tx
This will provide callers more information on the accepted transaction.
2016-10-28 14:52:31 -04:00
Dave Collins
1a69eb0617 cpuminer: Refactor code to its own package.
This does the minimum work necessary to refactor the CPU miner code into
its own package.  The idea is that separating this code into its own
package will improve its testability and ultimately be useful to other
parts of the codebase such as the various tests which currently
effectively have their own stripped-down versions of this code.

The API will certainly need some additional cleanup and changes to make
it more usable outside of the specific circumstances it was originally
designed to support (namely the generate RPC), however it is better to
do that in future commits in order to keep the changeset as small as
possible during this refactor.

Overview of the major changes:

- Create the new package
- Move cpuminer.go -> cpuminer/cpuminer.go
- Update mining logging to use the new cpuminer package logger
- Rename cpuminerConfig to Config (so it's now cpuminer.Config)
- Rename newCPUMiner to New (so it's now cpuminer.New)
- Update all references to the cpuminer to use the package
- Add a skeleton README.md
2016-10-28 11:06:11 -05:00
Dave Collins
a755305a2e rpcserver: Move error check for generate RPC.
This moves the error check for an attempt to call the generate RPC when
on a network that there is virtually no chance of mining a block with
the CPU into the RPC server where it more naturally belongs.  The caller
of the CPU should be the one to determine if it wants to allow mining or
not.  While here, use a more accurate RPC error code of ErrDifficulty
instead of ErrInternal.

This change is a step towards being able to separate the CPU mining code
into its own subpackage.
2016-10-28 10:58:51 -05:00
Dave Collins
05126f6034 peer: Use debug log for inability to start peer.
The inability for a peer to negotiate is not something that should be a
warning which implies something is wrong.  On the contrary, it is quite
expected that various peers will connect (or be connected to) that are
unable to properly negotiate for a variety of reasons.  One example would
be a peer that is too old.

Also, while here take care of a few style nits.
2016-10-28 10:49:00 -05:00
Dave Collins
214d975adf server: Notify connmgr if server peer assoc fails.
This corrects a few issues introduced with the connection manager where
the server was not notifying the connection manager when a connection
request is available again.

The cases resolved are:
- Unable to initialize a server peer instance in response to the connection
- Failure to associate the connection with the server peer instance
- Disconnection of a non-persistent outbound peer

It also changes the log message to a debug in the former case because
it's not something that should be shown to the user as an error given
it's not due to anything the user has misconfigured nor is it even
unexpected if an invalid address is provided.
2016-10-28 10:49:00 -05:00
Dave Collins
a1f014c9e1 peer: Track and return advertised protocol version
This adds a new field to the peer struct which stores the protocol
version advertised by the remote peer and updates the StatsSnapshot to
return the advertised version instead of the negotiated version.
2016-10-28 10:15:38 -05:00
Dave Collins
14b51fc5f8 multi: Correct misspellings detected by misspell. 2016-10-28 09:43:38 -05:00
Dave Collins
55e2c5da2f btcd: Remove deps.txt.
Now that glide is used for version management the deps.txt file is no
longer needed.  It will still be available in the git history if it is
ever needed.
2016-10-28 09:33:27 -05:00
Dave Collins
51a6309cdd blockchain: Run gofmt -s.
Also compress some definitions a bit while simplifying.
2016-10-27 23:20:25 -05:00
Dave Collins
f3c442deb2 server: Remove unused wakeup channel.
This channel is no longer used since the outgoing connection logic is
handled by the connmgr package now.
2016-10-27 20:04:58 -05:00
Dave Collins
f6ad7eb2c9 wire: Make NewMsgTx accept the tx version.
This modifies the NewMsgTx function to accept the transaction version as
a parameter and updates all callers.

The reason for this change is so the transaction version can be bumped
in wire without breaking existing tests and to provide the caller with
the flexibility to create the specific transaction version they desire.
2016-10-27 14:09:29 -05:00
Dave Collins
58a98630e7 mining: Add basic priority calculation tests.
This adds some basic tests for the priority calculation code in the
mining policy.
2016-10-27 13:13:37 -05:00
Dave Collins
61ca40e0e9 mining: Refactor template code into mining package.
This does the minimum work necessary to refactor the block template
generation code into the mining package.  The idea is that separating
this code into the mining package will greatly improve its testability,
allow independent benchmarking and profiling, and open up some
interesting opportunities for future development related to mining.

There are some areas related to policy and other configuration that
could be further refactored, however it is better to do that in future
commits in order to keep the changeset as small as possible during this
refactor.

Overview of the major changes:

- Move mining.go -> mining/mining.go
- Move mining_test.go -> mining/mining_test.go
- Add logger to mining package
- Update the MINR subsystem to use the new mining package logger
- Export CoinbaseFlags from the mining package
- BlkTmplGenerator is now mining.BlkTmplGenerator
- Update all references to the mining code to use the package
2016-10-27 11:48:48 -05:00
Dave Collins
a952a3a148 mining: Use generator state versus local state.
This change is a step towards being able to separate the mining code
into its own subpackage.  No functional change.
2016-10-27 11:48:42 -05:00
Dave Collins
a18f883c1f mining/mempool: Export MinHighPriority from mining.
This move the export for MinHighPriority from the mempool package to the
mining package.  This should have been done when the priority
calculation code was moved to the mining package.
2016-10-27 11:48:37 -05:00
Dave Collins
660467259e mining: Break dependency on block manager instance.
This modifies the block template generate for the mining code such that
it takes chain instance and params instead of requiring a fully
initialized blockManager instance.

Also, in preparation for being able to more easily separate the code, it
exposes and makes use of two new functions:
- BestSnapshot which returns the state snapshot from the underlying
  chain instance
- TxSource which returns the underlying transaction source

This is a step towards being able to separate the mining code into its
own package.  No functional change.
2016-10-27 11:48:32 -05:00
Dave Collins
671901486c cpuminer: Introduce cpuminerConfig.
This introduces a cpuminerConfig type which contains the necessary
information to break the direct dependency on the main server instance.

This change is a step towards being able to separate the cpu miner into
its own subpackage.  No functional change.
2016-10-27 09:37:30 -05:00
Olaoluwa Osuntokun
e3eeb4a34a mempool: add policy config option for transaction version
This commit adds a new option to the mempool’s policy configuration
which determines which transaction versions should be accepted as
standard.

The default version set by the policy within the server is 2; this
allows accepting transactions which have version 2 enabled in order to
utilize the new sequence locks feature.
2016-10-26 21:49:14 -07:00
Olaoluwa Osuntokun
6cd8955498 mempool: enforce relative lock-time semantics
This commit introduces behavior which enforces sequence num based
relative lock-time semantics when accepting transaction to the mempool.
2016-10-26 21:49:07 -07:00
Olaoluwa Osuntokun
7eb0ab5f8d mempool: add function to config for computing sequence locks 2016-10-26 21:49:04 -07:00
Olaoluwa Osuntokun
ecdaf9726c blockchain: add basic tests for SequenceLockActive 2016-10-26 21:49:00 -07:00
Olaoluwa Osuntokun
e855c0dd82 blockchain: add table driven unit tests for CalcSequenceLock 2016-10-26 21:48:57 -07:00
Olaoluwa Osuntokun
de709f28f6 blockchain: add new LockTimeToSequence function
This commit adds a new function to the package: `LockTimeToSequence`.
The function is a simple utility function which aides the caller to
mapping a targeted time or block based relative lock-time to the
appropriate sequence number.
2016-10-26 21:48:54 -07:00
Olaoluwa Osuntokun
1914200080 blockchain: introduce SequenceLocks for relative lock-time calcs
This commit introduces the concept of “sequence locks” borrowed from
Bitcoin Core for converting an input’s relative time-locks to an
absolute value based on a particular block for input maturity
evaluation.

A sequence lock is computed as the most distant maturity height/time
amongst all the referenced outputs within a particular transaction.

A transaction with sequence locks activated within any of its inputs
can *only* be included within a block if from the point-of-view of that
block either the time-based or height-based maturity for all referenced
inputs has been met.

A transaction with sequence locks can only be accepted to the mempool
iff from the point-of-view of the *next* (yet to be found block) all
referenced inputs within the transaction are mature.
2016-10-26 21:48:44 -07:00
Dave Collins
74fe2a4dfd mining: Introduce a block template generator.
This introduces a new type named BlkTmplGenerator which encapsulates the
various state needed to generate block templates.

This is useful since it means code that needs to generate block
templates can simply accept the generator rather than needing access to
all of the additional state which in turn will ultimately make it easier
to split the mining code into its own package.
2016-10-26 15:17:21 -05:00
Dave Collins
2274d36333 bmgr: Remove block manager chain state.
This removes the block manager chain state in favor of using the
blockchain.BlockChain instance now that it is safe for concurrency.
2016-10-26 14:23:14 -05:00
Dave Collins
2cfc6478ce mining/mempool: Move priority code to mining pkg.
This moves the priority-related code from the mempool package to the
mining package and also exports a new constant named UnminedHeight which
takes the place of the old unexported mempoolHeight.

Even though the mempool makes use of the priority code to make decisions
about what it will accept, priority really has to do with mining since
it influences which transactions will end up into a block.  This change
also has the side effect of being a step towards enabling separation of
the mining code into its own package which, as previously mentioned,
needs access to the priority calculation code as well.

Finally, the mempoolHeight variable was poorly named since what it
really represents is a transaction that has not been mined into a block
yet.  Renaming the variable to more accurately reflect its purpose makes
it clear that it belongs in the mining package which also needs the
definition now as well since the priority calculation code relies on it.
This will also benefit an outstanding PR which needs access to the same
value.
2016-10-26 12:01:49 -05:00
Jonathan Gillham
c2fb0cb18e mining: Stop transactions losing their dependants.
This fixes a bug where a transaction would lose reference to other
transactions dependant on it when being considered for inclusion in a
new block template. The issue only occurs when the transaction being
considered triggers a change of priority queue ordering to ordering by
fee. It results in none of the dependant transactions being considered
for inclusion in the new block template.
2016-10-26 10:57:48 -05:00
David Hill
18bb90b4ad peer: Advertise _our_ max protocol version, 2016-10-26 08:17:46 -04:00
Dave Collins
54b454ed3d mining: Fix duplicate transactions in prio heap 2016-10-25 16:43:40 -05:00
Dave Collins
e306158e25 mempool: Implement orphan expiration.
This implements orphan expiration in the mempool such that any orphans
that have not had their ancestors materialize within 15 minutes of their
initial arrival time will be evicted which in turn will remove any other
orphans that attempted to redeem it.

In order to perform the evictions with reasonable efficiency, an
opportunistic scan interval of 5 minutes is used.  That is to say that
there is not a hard deadline on the scan interval and instead it runs
when a new orphan is added to the pool if enough time has passed.

The following is an example of running this code against the main
network for around 30 minutes:

23:05:34 2016-10-24 [DBG] TXMP: Expired 3 orphans (remaining: 254)
23:10:38 2016-10-24 [DBG] TXMP: Expired 112 orphans (remaining: 231)
23:15:43 2016-10-24 [DBG] TXMP: Expired 95 orphans (remaining: 206)
23:20:44 2016-10-24 [DBG] TXMP: Expired 90 orphans (remaining: 191)
23:25:51 2016-10-24 [DBG] TXMP: Expired 71 orphans (remaining: 191)
23:30:55 2016-10-24 [DBG] TXMP: Expired 70 orphans (remaining: 105)
23:36:19 2016-10-24 [DBG] TXMP: Expired 55 orphans (remaining: 107)

As can be seen from the above, without orphan expiration on this data
set, the orphan pool would have grown an additional 496 entries.
2016-10-25 15:37:29 -05:00
Dave Collins
c0def9d613 integration: Initial implementation.
This introduces a new package to house tests which use the rpctest
package to programmatically drive end-to-end integration testing and
move the existing RPC integration tests into the new package.
2016-10-25 14:36:23 -05:00
Dave Collins
70db324663 mempool: Stricter orphan evaluation and eviction.
This modifies the way orphan removal and processing is done to more
aggressively remove orphans that can no longer be valid due to other
transactions being added or removed from the primary transaction pool.

The net effect of these changes is that orphan pool will typically be
much smaller which greatly improves its effectiveness.  Previously, it
would typically quickly reach the max allowed worst-case usage and
effectively stay there forever.

The following is a summary of the changes:
- Modify the map that tracks which orphans redeem a given transaction to
  instead track by the specific outpoints that are redeemed
- Modify the various orphan removal and processing functions to accept
  the full transaction rather than just its hash
- Introduce a new flag on removeOrphans which specifies whether or not
  to remove the transactions that redeem the orphan being removed as
  well which is necessary since only some paths require it
- Add a new function named removeOrphanDoubleSpends that is invoked
  whenever a transaction is added to the main pool and thus the outputs
  they spent become concrete spends
- Introduce a new flag on maybeAcceptTransaction which specifies whether
  or not duplicate orphans should be rejected since only some paths
  require it
- Modify processOrphans as follows:
  - Make use of the modified map
  - Use newly available flags and logic work more strictly work with tx
    chains
  - Recursively remove any orphans that also redeem any outputs redeemed
    by the accepted transactions
- Several new tests to ensure proper functionality
  - Removing an orphan that doesn't exist is removed both when there is
    another orphan that redeems it and when there is not
  - Removing orphans works properly with orphan chains per the new
    remove redeemers flag
  - Removal of multi-input orphans that double spend an output when a
    concrete redeemer enters the transaction pool
2016-10-25 10:44:18 -05:00
Dave Collins
60355258a7 mempool: Refactor pool membership test logic.
This introduces a new pool membership test function to the mempool
testing infrastructure and refactors the tests to make use of it.

It is useful since it is common logic that is not only needed in the
existing tests, but will be needed by most mempool-related tests.
2016-10-25 10:42:51 -05:00
Tibor Bősze
6b8a24918e rpcserver: Improve JSON-RPC compatibility
Avoid compatibility issues with software that relies on the behavior of
bitcoind's JSON-RPC implementation.

The JSON-RPC 1.0 spec defines that notifications must have their "id"
set to null and states that notifications do not have a response.

A JSON-RPC 2.0 notification is a request with "json-rpc":"2.0", and
without an "id" member. The specification states that notifications
must not be responded to. JSON-RPC 2.0 permits the null value as a
valid request id, therefore such requests are not notifications.

Bitcoin Core serves requests with "id":null or even an absent "id", and
responds to such requests with "id":null in the response.

Btcd does not respond to any request without and "id" or with "id":null,
regardless the indicated JSON-RPC protocol version.

In order to avoid compatibility issues with software relying on
Core's behavior, this commit implements "quirks mode" as follows:
 - quirks mode can be enabled via configuration (disabled by default)
 - If no JSON-RPC version is indicated in the request, accept and
respond to request with "id":null
 - If no JSON-RPC version is indicated in the request, accept and
respond to requests without an "id" member
 - In both cases above, use "id":null in the response
 - Do not respond to request without an "id" or with "id":null when
JSON-RPC version is indicated in the request (process as notification)
2016-10-24 13:24:18 -05:00
Tibor Bősze
9799f0e547 rpcserver: Improve JSON-RPC compatibility
In order to avoid compatibility issues with software relying on 
Core's behavior, terminate HTTP POST JSON-RPC responses with a newline.
2016-10-24 13:24:18 -05:00
Dave Collins
0e71867dfe mempool: Optimize orphan map limiting.
This optimizes the way in which the mempool oprhan map is limited in the
same way the server block manager maps were previously optimized.

Previously the code would read a cryptographically random value large
enough to construct a hash, find the first entry larger than that value,
and evict it.

That approach is quite inefficient and could easily become a
bottleneck when processing transactions due to the need to read from a
source such as /dev/urandom and all of the subsequent hash comparisons.

Luckily, strong cryptographic randomness is not needed here. The primary
intent of limiting the maps is to control memory usage with a secondary
concern of making it difficult for adversaries to force eviction of
specific entries.

Consequently, this changes the code to make use of the pseudorandom
iteration order of Go's maps along with the preimage resistance of the
hashing function to provide the desired functionality.  It has
previously been discussed that the specific pseudorandom iteration order
is not guaranteed by the Go spec even though in practice that is how it
is implemented.  This is not a concern however because even if the
specific compiler doesn't implement that, the preimage resistance of the
hashing function alone is enough.

The following is a before and after comparison of the function for both
speed and memory allocations:

benchmark                    old ns/op     new ns/op     delta
----------------------------------------------------------------
BenchmarkLimitNumOrphans     3727          243           -93.48%

benchmark                    old allocs    new allocs    delta
-----------------------------------------------------------------
BenchmarkLimitNumOrphans     4             0             -100.00%
2016-10-23 21:14:09 -05:00
David Hill
e6062595db travis: go1.7.3 2016-10-23 21:53:29 -04:00
Dave Collins
25de9ce5d9 mempool: Add docs.go and flesh out README.md. 2016-10-23 20:47:12 -05:00
Dave Collins
e90b0c967f docs: Add mempool entry to developer tools section. 2016-10-23 20:47:05 -05:00
Dave Collins
26e22790cd mempool: Rename RelayNonStd config option.
This renames the mempool.Config.RelayNonStd option to AcceptNonStd which
more accurately describes its behavior since the mempool was refactored
into a separate package.

The reasoning for this change is that the mempool is not responsible for
relaying transactions (nor should it be).  Its job is to maintain a pool
of unmined transactions that are validated according to consensus and
policy configuration options which are then used to provide a source of
transactions that need to be mined.

Instead, it is the server that is responsible for relaying transactions.
While it is true that the current server code currently only relays txns
that were accepted to the mempool, this does not necessarily have to
be the case.  It would be entirely possible (and perhaps even a good
idea as something do in the future), to separate the relay policy from
the mempool acceptance policy (and thus indirectly the mining policy).
2016-10-23 20:41:54 -05:00
David Hill
f161d6b69e chaincfg: Introduce new type DNSSeed
DNSSeed defines a DNS Seed with a hostname and whether it supports
filtering by service flag bits.
2016-10-23 15:59:15 -05:00
David Hill
0d508e6522 peer: BestLocalAddress config option is no longer used. 2016-10-23 16:38:45 -04:00
David Hill
a09d052f96 peer: Stop setting AddrMe in the version message.
Older nodes previously added the IP and port information to the address
manager which proved to be unreliable as an inbound connection from a
peer didn't necessarily mean the peer itself accepted inbound
connections.

This also fixes a bug where the peer package was incorrectly sending
the peer's services as its own.
2016-10-23 16:37:44 -04:00
David Hill
bca9877796 Do not add inbound peers to the address manager. 2016-10-23 16:37:44 -04:00
Dave Collins
b8df516b4b docs: Add fullblocktests entry and make consistent. 2016-10-23 13:34:14 -05:00
Dave Collins
9634a8cb0d fullblocktests: Add missing doc.go file. 2016-10-23 13:31:26 -05:00
Javed Khan
bff2ba70fd connmgr: Refactor connection management into pkg
This commit introduces package connmgr which contains connection
management related functionality.

The following is an overview of the features the package provides:

- Maintain fixed number of outbound connections
- Optional connect-only mode
- Retry persistent connections with increasing back-off
- Source peers from DNS seeds
- Use Tor to resolve DNS
- Dynamic ban scores
- Test coverage

In addition, btcd has been refactored to make use of the new package by
extending the connection manager to work with the server to source and
maintain peer connections. The following is a broad overview of the
changes to integrate the package:

- Simplify peer state by removing pending, retry peers
- Refactor to remove retries which are now handled by connmgr
- Use callback to add addresses sourced from the  DNS seed

Finally the following connection-related things have been improved as a
part of this refactor:

- Fixes 100% cpu usage when network is down (#129)
- Fixes issues with max peers (#577)
- Simplify outbound peer connections management
2016-10-22 01:11:57 -05:00
Marco Peereboom
69fca4d9b1 Reconcile differences between btcd/dcrd.
Fixes #793
2016-10-21 16:37:30 -05:00
David Hill
4494f0f852 txscript: Remove OP_SMALLDATA 2016-10-21 15:18:42 -04:00
Javed Khan
f6cd49ac51 peer: rename peer.Connect to AssociateConnection 2016-10-21 14:13:18 -05:00
Dave Collins
0731f2ddc9 txscript: Cleanup and improve NullDataScript tests.
This modifies the recently-added NullDataScript function in several
ways in an effort to make them more consistent with the tests in the
rest of the code base and improve/correct the logic:

- Use the hexToBytes and mustParseShortForm functions
- Consistently format the test errors
- Replace the valid bool flag with an expected error and test against it
- Ensure the returned script type is the expected type in all cases
2016-10-21 10:26:09 -05:00
DanielKrawisz
b77654f8d4 txscript: Add null data script creator
This adds a new function named NullDataScript to the txscript package that returns a provably-pruneable  OP_RETURN script with the provided data.  The function will return an error if the provided data is larger than the maximum allowed length for a nulldata script to be be considered standard.
2016-10-21 09:37:48 -05:00
Josh Rickmar
d0a9c03844 Concurrently handle websocket client JSON-RPC requests. 2016-10-20 19:55:58 -04:00
Dave Collins
da04285e0d rpctest: Choose flags based on provided params.
This modifies the rpctest framework to start btcd with the appropriate
network flags depending on the provided parameters.

Previously, it always started btcd with --simnet even if other
parameters, such as those for the regression test network, were
provided.
2016-10-20 12:19:58 -05:00
Dave Collins
49cbaf23dd blockchain: Support small coinbase block size
This modifies the ExtractCoinbaseHeight function to recognize small
canonically serialized block heights in coinbase scripts of blocks
higher than version 2.

This allows regression test chains in which blocks encode the serialized
height in the coinbase starting from block 1.
2016-10-20 12:09:09 -05:00
Dave Collins
59a3fc2f66 txscript: Consolidate tests into txscript package.
Putting the test code in the same package makes it easier for forks
since they don't have to change the import paths as much and it also
gets rid of the need for internal_test.go to bridge.

Also, do some light cleanup on a few tests while here.
2016-10-20 09:28:33 -05:00
Dave Collins
b60e3547d2 txscript: Correct nulldata standardness check.
This corrects the isNullData standard transaction type test to work
properly with canonically-encoded data pushes.  In particular, single
byte data pushes that are small integers (0-16) are converted to the
equivalent numeric opcodes when canonically encoded and the code failed
to detect them properly.

It also adds several tests to ensure that both canonical and
non-canonical nulldata scripts are recognized properly and modifies the
test failure print to include the script that failed.

This does not affect consensus since it is just a standardness check.
2016-10-20 01:44:58 -05:00
David Hill
07e1e308f1 rpc: Add localaddr and relaytxes to getpeerinfo 2016-10-19 19:58:50 -04:00
David Hill
3b5bb9fd43 btcjson: Add preciousblock 2016-10-19 14:08:32 -05:00
David Hill
403aaf5cf3 rpcserver: avoid nested decodescript p2sh addrs 2016-10-19 13:59:50 -05:00
Olaoluwa Osuntokun
e7caccc866 mempool: transaction finality checks now use median-time-past
This coincides with the mempool only, policy change which enforces
transaction finality according to the median-time-past rather than
blockheader timestamps. The behavior is pre-cursor to full blown BIP
113 consensus deployment, and subsequent activation.

As a result, the TimeSource field in the mempoolConfig is no longer
needed so it has been removed. Additionally, checkTransactionStandard has been
modified to instead take a time.Time as the mempool is no longer explicitly
dependant on a Chain instance.
2016-10-19 11:13:34 -07:00
Olaoluwa Osuntokun
a82f67b538 mempool: add closure to compute median time past to config
This commit adds an additional closure function to the mempool’s config
which computes the median time past from the point of view of the best
node in the chain. The mempool test harness has also been updated to allow
setting a mock median time past for testing purposes.

In addition to increasing the testability of the mempool, this commit
should also speed up transaction and block validation for BIP 113 as
the MTP no longer needs to be re-calculated each time from scratch.
2016-10-19 11:13:25 -07:00
David Hill
a6bf1d9850 txscript: Implement CheckSequenceVerify (BIP0112) 2016-10-19 12:06:44 -04:00
Dave Collins
fdfa07b0be btcec: Consolidate tests into the btcec package.
Putting the test code in the same package makes it easier for forks
since they don't have to change the import paths as much and it also
gets rid of the need for internal_test.go to bridge.

Also, remove the exception from the lint checks about returning the
unexported type since it is no longer required.
2016-10-19 00:55:23 -05:00
David Hill
b1621332cc Optimize by removing defers
defer's are nice for readability but they do add overhead.  This
gets rid of defer's where it is just as easy as not to use one.
2016-10-18 17:56:51 -04:00
Jimmy Song
294b5d46da btcec: Add regression tests for field.go.
This adds new tests to the TestNormalize, TestMul, TestAdd2 functions
which trigger an issue with modular reduction that was fixed in the
prevous commit to prevent regressions.
2016-10-18 16:21:45 -05:00
Dave Collins
a52eb04aaa btcec: Ensure reduction when > P in all cases.
As noted in issue #706, the existing code had an issue where the
normalized result was > P when both the first and second words of the
field representation being normalized were BOTH greater than or equal to
the first and second words of P.  Although this condition is rare in
practice, it needs to be handled properly.

This resolves the issue by comparing the low words in the final
reduction step against the normalized low order prime bits to ensure the
final subtraction occurs correctly any time they're > P.  This approach
retains the constant time property as well.
2016-10-18 16:21:36 -05:00
David Hill
d009185a56 peer: Implement feefilter p2p message (bip0133) 2016-10-17 15:45:56 -04:00
David Hill
9935fe5dba wire: Bump minor due to feefilter addition 2016-10-17 13:45:12 -04:00
David Hill
ca4e9b82d6 wire: implement feefilter message (bip0133)
feefilter is used to request the receiving peer does not announce any
transactions below the specified minimum fee rate.
2016-10-17 13:33:16 -04:00
Dave Collins
f21410e47c blockchain: Add block validation infrastructure.
This adds a full-blown testing infrastructure in order to test consensus
validation rules.  It is built around the idea of dynamically generating
full blocks that target specific rules linked together to form a block
chain.  In order to properly test the rules, each test instance starts
with a valid block that is then modified in the specific way needed to
test a specific rule.

Blocks which exercise following rules have been added for this initial
version.  These tests were largely ported from the original Java-based
'official' block acceptance tests as well as some additional tests
available in the Core python port.  It is expected that further tests
can be added over time as consensus rules change.

* Enough valid blocks to have a stable base of mature coinbases to spend
  for futher tests
* Basic forking and chain reorganization
* Double spends on forks
* Too much proof-of-work coinbase (extending main chain, in block that
  forces a reorg, and in a valid fork)
* Max and too many signature operations via various combinations of
  OP_CHECKSIG, OP_MULTISIG, OP_CHECKSIGVERIFY, and OP_MULTISIGVERIFY
* Too many and max signature operations with offending sigop after
  invalid data push
* Max and too many signature operations via pay-to-script-hash redeem
  scripts
* Attempt to spend tx created on a different fork
* Attempt to spend immature coinbase (on main chain and fork)
* Max size block and block that exceeds the max size
* Children of rejected blocks are either orphans or rejected
* Coinbase script too small and too large
* Max length coinbase script
* Attempt to spend tx in blocks that failed to connect
* Valid non-coinbase tx in place of coinbase
* Block with no transactions
* Invalid proof-of-work
* Block with a timestamp too far in the future
* Invalid merkle root
* Invalid proof-of-work limit (bits header field)
* Negative proof-of-work limit (bits header field)
* Two coinbase transactions
* Duplicate transactions
* Spend from transaction that does not exist
* Timestamp exactly at and one second after the median time
* Blocks with same hash via merkle root tricks
* Spend from transaction index that is out of range
* Transaction that spends more that its inputs provide
* Transaction with same hash as an existing tx that has not been
  fully spent (BIP0030)
* Non-final coinbase and non-coinbase txns
* Max size block with canonical encoding which exceeds max size with
  non-canonical encoding
* Spend from transaction earlier in same block
* Spend from transaction later in same block
* Double spend transaction from earlier in same block
* Coinbase that pays more than subsidy + fees
* Coinbase that includes subsidy + fees
* Invalid opcode in dead execution path
* Reorganization of txns with OP_RETURN outputs
* Spend of an OP_RETURN output
* Transaction with multiple OP_RETURN outputs
* Large max-sized block reorganization test (disabled by default since
  it takes a long time and a lot of memory to run)

Finally, the README.md files in the main and docs directories have been
updated to reflect the use of the new testing framework.
2016-10-17 12:16:53 -05:00
Dave Collins
1cba5c8fc0 blockchain: Remove exported CalcPastTimeMedian func.
This removes the exported CalcPastTimeMedian function from the
blockchain package as it is no longer needed since the information is
now available via the BestState snapshot.

Also, update the only known caller of this, which is the chain state in
block manager, to use the snapshot instead.  In reality, now that
everything the block manager chain state provides is available via the
blockchain BestState snapshot, the entire thing can be removed, however
that will be done in a separate to commit to keep the changes targeted.
2016-10-17 10:41:25 -05:00
Dave Collins
e88f2d7bf4 mempool: Add test for max orphan entry eviction.
This adds a test to the mempool for ensuring that orphans are evicted
when exceeding the max orphan policy setting as expected.
2016-10-17 10:33:09 -05:00
Dave Collins
8965d88893 peer: Strictly enforce bloom filter service bit.
This makes the enforcement of the bloom filter service bit much more
strict.  In particular, it does the following:

- Moves the enforcement of the bloom filter service bit out of the peer
  package and into the server so the server can ban as necessary
- Disconnect peers that send filter commands when the server is
  configured to disable them regardless of the protocol version
- Bans peers that are a high enough protocol version that they are
  supposed to observe the service bit is disabled, but ignore it and
  send filter commands regardless.

As an added bonus, this fixes the old logic which had a bug in that it
was examining the *remote* peer's supported services in order to choose
whether or not to disconnect instead of the *local* server's supported
services.
2016-10-16 02:19:28 -05:00
Dave Collins
77913ad2e8 blockchain: Expose main chain flag on ProcessBlock.
This modifies the blockchain.ProcessBlock function to return an
additional boolean as the first parameter which indicates whether or not
the block ended up on the main chain.

This is primarily useful for upcoming test code that needs to be able to
tell the difference between a block accepted to a side chain and a block
that either extends the main chain or causes a reorganize that causes it
to become the main chain.  However, it is also useful for the addblock
utility since it allows a better error in the case a file with out of
order blocks is provided.
2016-10-13 16:47:50 -05:00
David Hill
42a4366ba8 addrmgr: Fix AddressCache to not include nils
allAddr was being allocated with counters instead of the actual size
of the address map.  This led to the possibility of including nils
in the returned slice, which resulted in a panic.
2016-09-26 12:16:40 -05:00
Dave Collins
754c4fbe0c rpctest: Gate rpctest-based behind a build tag.
This adds a new build tag named rpctest which must be set in order for
rpctest-based tests to be executed.  The new build tag is also added to
the goclean.sh script which is executed by Travis during continuous
integration builds.

This change is being made because the rpctest framework requires
additional careful user configuration to ensure the version of btcd
under test can be programmatically launched from the system path with
all of the necessary ports open whereas all of the other tests are
self-contained within the test binary itself.

Since said additional configuration is typically not done, it leads to a
lot of false positives.  Putting the tests behind a build tag allows
them to remain to be available and run during continuous integration
without imposing the additional configuration requirements on users.
2016-09-26 01:20:31 -05:00
David Hill
99165eb558 rpctest: Fix typo
Use os.Getpid() to get the process ID, not os.Getppid(), which returns
the parent process ID.  This resulted in multiple calls to
generateListeningAddresses() getting the same listening ports.
2016-09-22 00:14:51 -04:00
David Hill
5ec83d23f3 Update dependencies and API usage. 2016-09-21 20:50:55 -04:00
Dave Collins
7cf9ec8190 rpctest: Use ports based on the process id.
This modifies the ports that are selected for use for the p2p and rpc
ports to start with a port that is based on the process id instead of a
hard-coded value.  The chosen ports are incremented for each running
instance similar to the previous code except the p2p and rpc ports and
now split into ranges instead of being 2 apart.

This is being done because the previous code only worked for a single
process which means it prevented the ability to run tests in parallel.

The new approach will work with multiple processes, however it must be
stated that there is still a very small probability that the stars could
align resulting in the same ports being selected.

Finally, this also reverts the recent change to run tests serially since
this fixes the underlying cause for that change.
2016-09-20 16:59:37 -05:00
Olaoluwa Osuntokun
daac24626e build: execute tests across all packages serially
This modifies the goclean.sh to execute all the tests amongst
the packages serially. The default behavior of the `go test` command is
to execute all tests in parallel amongst the listed packages. This
behavior can at times cause tests which use the `rpctest` package to
fail due to multiple `btcd` nodes attempting to bind to the same port
simultaneously. As only one node can successfully bind to the port, the
btcd processes for the other concurrent harness instances exit silently
causing the RPC clients to fail with connection timeouts as their
target process no longer exists. Executing all tests serially
eliminates such a race condition which can cause non-deterministic test
failures.
2016-09-20 01:16:08 -05:00
Dave Collins
2b780d16b0 Improve HTTP error handling.
This modifies the code which handles failed server responses to attempt
to unmarshal the response bytes as a regular JSON-RPC response
regardless of the HTTP status code and, if that fails, return an error
that includes the status code as well as the raw response bytes.

This is being done because some JSON-RPC servers, such as Bitcoin Core,
don't follow the intended HTTP status code meanings.  For example, if
you request a block that doesn't exist, it returns a status code of 500
which is supposed to mean internal server error instead of a standard
200 status code (since the HTTP request itself was successful) along
with the JSON-RPC error response.

The result is that errors from Core will now show up as an actual
RPCError instead of an error with the raw JSON bytes.

This also has the benefit of returning the HTTP status code in the
error for real HTTP failure cases such as 401 authentication failures,
which previously would just be an empty error when used against Core
since it doesn't return the actual response along with the status code
as it should.
2016-09-16 14:52:50 -05:00
Chris Martin
c20db1cf14 blockchain: make scriptval_test fail more nicely
Return early when failing on len(blocks) > 1.

Check for len(blocks) == 0 so we can fail on that condition with an
error message instead of a panic.
2016-09-16 15:44:27 -04:00
David Hill
6e5c0a7904 Fix GetBlockVerbose API.
This changes the GetBlockVersion API to not send a third parameter.
The third parameter is a boolean that expands the transaction data
structures as well.  However, Bitcore Core does not recognize this
feature.

GetBlockVerbose now only takes a hash value.

Users of the GetBlockVerbose(hash, true) must now use
GetBlockVerboseTx(hash).
2016-09-16 12:35:52 -04:00
Dave Collins
982229439a Return wire.MsgBlock from GetBlock(Async).
This modifies the return value of the GetBlock and GetBlockAsync
functions to a raw *wire.MsgBlock instead of *btcutil.Block.

This is being done because a btcutil.Block assumes there is a height
associated with the block and the block height is not part of a raw
serialized block as returned by the underlying non-verbose RPC API
getblock call and thus is not available to populate a btcutil.Block
correctly.

A raw wire.Block correctly matches what the underlying RPC call is
actually returning and thus avoids the confusion that could occur.
2016-09-15 14:06:36 -05:00
David Hill
96ed4cb5b0 Update installation section in README.md
Have users use -u along with go get to pull the latest
dependencies.
2016-09-15 14:25:23 -04:00
David Hill
8ff9623a8c Add API for getblockheader
GetBlockHeader returns the blockheader of the specified blockhash.
GetBlockHeaderVerbose returns the data structure with information about
the blockheader of the specified blockhash.

Fixes #89
2016-09-15 13:39:27 -04:00
Dave Collins
fb90c334df rpctest: Cleanup resources on failed setup.
This modifies the rpctest harness to call the TearDown function in the
SetUp error path.  This ensures any resources, such as temp directories,
that were created during setup before whatever the failure that caused
the error are properly cleaned up.
2016-09-13 17:19:32 -05:00
Chris Martin
0ddd10add6 Remove extra backtick in README.md 2016-09-13 15:10:48 -04:00
Marco Peereboom
5e93b1664e config: Replace log outputs with fmt.Fprintln.
This corrects the case where any errors that might have happened when
creating the config file were not being displayed due to the logging
system not being initialized yet.

While here, consistently use fmt.Fprint{f,ln} throughout the loadConfig
function even after the logging system is initialized since it will
help prevent copy/paste errors such as the one that induced this change
to begin with.
2016-09-12 02:21:37 -05:00
Dave Collins
2ef82e7db3 mempool: Improve tx input standard checks.
This changes the transaction input standardness checks as follows:

- Allow any script in a pay-to-script-hash transaction to be relayed and
  mined so long as it has no more than 15 signature operations
- Remove the obsolete checks which naively calculated the number of
  expected inputs in favor of the more accurate ScriptVerifyCleanStack
  and ScriptVerifySigPushOnly functionality of the script engine that was
  added after these checks.
2016-08-24 23:16:23 -05:00
Olaoluwa Osuntokun
c6d50b7abf rpctest: ensure the main harness rejects non-standard transactions
This commit modifies the current set of integration tests to ensure
that that the main harness always rejects non-standard transactions.

With this in place, even though we’re using simnet parameters, we
ensure that transaction acceptance/validation is identical to that of
main net.
2016-08-24 15:43:35 -07:00
Olaoluwa Osuntokun
815ded348e config: introduce new flags to accept/reject non-std transactions
This commit adds two new cli flags: one for accepting non-std
transactions, and the other for rejecting non-std transactions.

The two flag are rejected when using concurrently. Config parsing is
set up such that, the desired policy expressed via the config always
overrides the policy set by default for a particular chain.

The doc.go files and the sample-btcd.conf file have been updated to document
the new flags exposing further policy control.
2016-08-24 15:43:26 -07:00
Olaoluwa Osuntokun
dc5486a579 mempool: add non-standard tx relaying to policy config 2016-08-24 15:43:22 -07:00
Dave Collins
15bace88dc mempool: Add basic test harness infrastructure.
This adds a basic test harness infrastructure for the mempool package
which aims to make writing tests for it much easier.

The harness provides functionality for creating and signing transactions
as well as a fake chain that provides utxos for use in generating valid
transactions and allows an arbitrary chain height to be set.  In order
to simplify transaction creation, a single signing key and payment
address is used throughout and a convenience type for spendable outputs
is provided.

The harness is initialized with a spendable coinbase output by default
and the fake chain height set to the maturity height needed to ensure
the provided output is in fact spendable as well as a policy that is
suitable for testing.

Since tests are in the same package and each harness provides a unique
pool and fake chain instance, the tests can safely reach into the pool
policy, or any other state, and change it for a given harness without
affecting the others.

In order to be able to make use of the existing blockchain.Viewpoint
type, a Clone method has been to the UtxoEntry type which allows the
fake chain instance to keep a single view with the actual available
unspent utxos while the mempool ends up fetching a subset of the view
with the specifically requested entries cloned.

To demo the harness, this also contains a couple of tests which make use
of it:

- TestSimpleOrphanChain -- Ensures an entire chain of orphans is
  properly accepted and connects up when the missing parent transaction
  is added
- TestOrphanRejects -- Ensure orphans are actually rejected when the
  flag on ProcessTransactions is set to reject them
2016-08-24 10:04:40 -05:00
David Hill
a109bea3f1 mempool: unexport the mutex
callers should not need to lock/unlock the mempool themselves.
2016-08-23 14:59:48 -04:00
Dave Collins
641182b2ad mempool: Break dependency on chain instance.
This modifies the config for the new mempool package such that it takes
a callback function to obtain the best chain height instead of requiring
a fully initialized blockchain.BlockChain instance.

This will make it much easier to test the mempool since the tests will
be able to provide their own height function to test various
functionality without having create and manipulate full blocks and chain
instances.
2016-08-23 12:29:45 -05:00
Dave Collins
c57c18c8e2 blockchain: Add median time to state snapshot.
This adds a new field to the best chain state snapshot for the
calculated past median time as returned by the calcPastMedianTime
function.  This is useful since it provides fast access to it without
having to acquire the chain lock which is needed to recalculate it.

This will ultimately allow the associated exported function to be
removed since it only exists to be able to calculate this exact value,
however this commit only introduces the new field in order to keep the
changes minimal.
2016-08-23 01:29:11 -05:00
Olaoluwa Osuntokun
bfe2ba4191 rpctest: prevent leaking processes in tests due to panics
This commit adds a `defer` statement at the top of  `TestRpcServer`
which will attempt a `recover` which tears down all active harnesses in
the event that one of the tests causes a panic in the main goroutine.

Before this commit, if a buggy test caused a panic while all integration
tests were being executed, then any active harnesses would fail to be
properly torn down. This would cause the running btcd processes to be
leaked, possibly interfering with future test runs until the process was
manually killed. This commit fixes such behavior.

In order to aide in debugging, when a test panics, the test number is
printed out along with a full stack-trace from the start of the test to
the panic point.
2016-08-22 17:09:18 -07:00
Dave Collins
6cf60b5fae rpctest: Correct several issues in tests and joins.
This corrects several issues with the new rpctest package.

The following is an overview of the issues that have been corrected:

- The JoinNodes code was incorrect for both mempool and blocks.
  - For mempool it was only checking the first node against itself
  - For blocks it was only testing the height instead of hash and height
    which means the chains could be different and it would claim they're
    synced
- The test for mempool joins was inaccurate in a few ways:
  - It was generating separate chains such that the generated
    transaction was invalid (an orphan) on one chain, but not
    the other
  - Mempools are not automatically synced when nodes are connected and
    there is a large random delay before any transaction rebroadcast
    happens, so it can't be relied on for the purposes of this test
- The test for block joins was generating two independent chains of the
  same height with the same difficulty and was only passing due to the
  aforementioned bug in JoinNodes
- All of the ConnectNode calls were connecting the main harness outbound
  to the local test harness instances
  - This is not correct because ConnectNode makes the outbound
    connection persistent, which means once the local test harness is
    gone, it would keep trying to connect for the remainder of the tests
    to a node that is never coming back and instead ends up connecting to
    an independent test harness.
2016-08-21 15:10:23 -05:00
jadeblaquiere
47ced81d44 don't spin forever looking for peers (#724) 2016-08-20 22:11:12 -05:00
Olaoluwa Osuntokun
7c4b169faa build: install btcd binary within Travis container
This commit modifies the existing Travis configuration to also install
the btcd binary within the container’s PATH.

This change is necessary in order for tests written against the new
rpctest package to work properly within the Travis testing environment.
2016-08-19 17:43:42 -05:00
Olaoluwa Osuntokun
c3d5371615 build: update dependancies required by rpctest 2016-08-19 17:42:25 -05:00
Olaoluwa Osuntokun
2d86fbface btcd: add basic RPC tests using the rpctest package
This commit introduces a new file: rpcserver_test.go dedicated for
including integration tests for btcd using the new rpctest package.

The tests are created using a TestMain instance first creates a single
main harness which is intended to be re-used across tests instances.
Afterwards all registered RPC tests are executed, with proper clean up
being executed regardless of the passing state of the tests.

The following RPC calls are excessed by the initial set of tests added:
    * getbestblock
    * getblockcount
    * getblockhash
2016-08-19 17:41:37 -05:00
Olaoluwa Osuntokun
b86df0ba91 rpctest: create new rpctest package
This commit adds a new package (rpctest) which provides functionality
for writing automated black box tests to exercise the RPC interface.

An instance of a rpctest consists of an active btcd process running in
(typically) --simnet mode, a btcrpcclient instance connected to said
node, and finally an embedded in-memory wallet instance (the memWallet)
which manages any created coinbase outputs created by the mining btcd
node.

As part of the SetUp process for an RPC test, a test author can
optionally opt to have a test blockchain created. The second argument
to SetUp dictates the number of mature coinbase outputs desired. The
btcd process will then be directed to generate a test chain of length:
100 + numMatureOutputs.

The embedded memWallet instance acts as a minimal, simple wallet for
each Harness instance. The memWallet itself is a BIP 32 HD wallet
capable of creating new addresses, creating fully signed transactions,
creating+broadcasting a transaction paying to an arbitrary set of
outputs, and querying the currently confirmed balance.

In order to test various scenarios of blocks containing arbitrary
transactions, one can use the Generate rpc call via the exposed
btcrpcclient connected to the active btcd node. Additionally, the
Harness also exposes a secondary block generation API allowing callers
to create blocks with a set of hand-selected transactions, and an
arbitrary BlockVersion or Timestamp.

After execution of test logic TearDown should be called, allowing the
test instance to clean up created temporary directories, and shut down
the running processes.

Running multiple concurrent rpctest.Harness instances is supported in
order to allow for test authors to exercise complex scenarios. As a
result, the primary interface to create, and initialize an
rpctest.Harness instance is concurrent safe, with shared package level
private global variables protected by a sync.Mutex.

Fixes #116.
2016-08-19 17:37:08 -05:00
Dave Collins
763f731c5c wire: Lower MaxUserAgentLen to 256. (#744)
The protocol was silently upgrade in Core some time ago to enforce a
limit of 256 for the user agent in version messages.  This updates wire
to coincide with that change and consequently includes a wire version
bump to 0.4.1.
2016-08-19 12:40:24 -05:00
Dave Collins
7fac099bee mempool: Refactor mempool code to its own package. (#737)
This does the minimum work necessary to refactor the mempool code into
its own package.  The idea is that separating this code into its own
package will greatly improve its testability, allow independent
benchmarking and profiling, and open up some interesting opportunities
for future development related to the memory pool.

There are likely some areas related to policy that could be further
refactored, however it is better to do that in future commits in order
to keep the changeset as small as possible during this refactor.

Overview of the major changes:

- Create the new package
- Move several files into the new package:
  - mempool.go -> mempool/mempool.go
  - mempoolerror.go -> mempool/error.go
  - policy.go -> mempool/policy.go
  - policy_test.go -> mempool/policy_test.go
- Update mempool logging to use the new mempool package logger
- Rename mempoolPolicy to Policy (so it's now mempool.Policy)
- Rename mempoolConfig to Config (so it's now mempool.Config)
- Rename mempoolTxDesc to TxDesc (so it's now mempool.TxDesc)
- Rename txMemPool to TxPool (so it's now mempool.TxPool)
- Move defaultBlockPrioritySize to the new package and export it
- Export DefaultMinRelayTxFee from the mempool package
- Export the CalcPriority function from the mempool package
- Introduce a new RawMempoolVerbose function on the TxPool and update
  the RPC server to use it
- Update all references to the mempool to use the package.
- Add a skeleton README.md
2016-08-19 11:08:37 -05:00
Dave Collins
87b3756c8c server: Remove superfluous check in OnMemPool. (#736)
This reduces the mempool lock contention by removing an unnecessary
check when responding to a "mempool" request.

In particular, the code first gets a list of all transactions from the
mempool and then iterates them in order to construct the inventory
vectors and apply bloom filtering if it is enabled.  Since it is
possible that the transaction was removed from the mempool by another
thread while that list is being iterated, the code was checking if each
transaction was still in the mempool.  This is a pointless check because
the transaction might still be removed at any point after the check
anyways.  For example, it might be removed after the mempool response
has been sent to the remote peer or even while the loop is still
iterating.
2016-08-19 11:04:16 -05:00
David Hill
05ab7141d3 travis: Add go 1.7 and drop go 1.5 support. (#740) 2016-08-18 11:06:26 -05:00
Dave Collins
b89c91b9d6 Refactor ntfnstate mutex. (#85)
This refactors the notification state mutex out of the state itself to
the client.  This is being done since the code makes a copy of the
notification state and accesses that copy immutably, and therefore there
is no need for it to have its own mutex.
2016-08-17 13:12:27 -05:00
David Hill
8c8d4453ad travis: Add golang 1.7 and drop golang 1.5 support. (#83) 2016-08-17 13:09:36 -05:00
Dave Collins
2de61b2d9a Manually copy relevant fields in ntfn state copy. (#84)
This makes vet happy by manually copying the notification state fields
during the deep copy instead of copying the entire struct which contains
an embedded mutex.
2016-08-17 12:39:34 -05:00
Janus Troelsen
4a5223266c docs: Add chainhash to README.md (#739) 2016-08-17 10:41:50 -05:00
Dave Collins
cee207c64c txscript: Expose AddOps on ScriptBuilder. (#734)
This exposes a new function on the ScriptBuilder type named AddOps that
allows multiple opcodes to be added via a single call and adds tests to
exercise the new function.

Finally, it updates a couple of places in the signing code that were
abusing the interface by setting its private script directly to use the
new public function instead.
2016-08-12 19:29:28 -05:00
Waldir Pimenta
f584dbd2e5 add license title (#82)
background: https://github.com/btcsuite/btcd/pull/717
2016-08-11 20:44:30 -05:00
Waldir Pimenta
fb9b640ef2 add license title (#717)
It's not strictly required, but it's useful metadata, and part of the recommended license template text (see http://choosealicense.com/licenses/isc/ and https://opensource.org/licenses/isc-license)
2016-08-11 16:58:15 -05:00
Dave Collins
044a11c9fc btcd: Simplify shutdown signal handling logic. (#733)
This rewrites the shutdown logic to simplify the shutdown signalling.
All cleanup is now run from deferred functions in the main function and
channels are used to signal shutdown either from OS signals or from
other subsystems such as the RPC server and windows service controller.

The RPC server has been modified to use a new channel for signalling
shutdown that is exposed via the RequestedProcessShutdown function
instead of directly calling Stop on the server as it previously did.

Finally, it adds a few checks for early termination during the main
start sequence so the process can be stopped without starting all the
subsystems if desired.

This is a backport of the equivalent logic from Decred with a few slight
modifications.  Credits go to @jrick.
2016-08-11 13:39:23 -05:00
Dave Collins
a7b35d9f9e chaincfg/blockchain: Parameterize more chain consts. (#732)
This moves several of the chain constants to the Params struct in the
chaincfg package which is intended for that purpose.  This is mostly a
backport of the same modifications made in Decred along with a few
additional things cleaned up.

The following is an overview of the changes:

- Comment all fields in the Params struct definition
- Add locals to BlockChain instance for the calculated values based on
  the provided chain params
- Rename the following param fields:
  - SubsidyHalvingInterval -> SubsidyReductionInterval
  - ResetMinDifficulty -> ReduceMinDifficulty
- Add new Param fields:
  - CoinbaseMaturity
  - TargetTimePerBlock
  - TargetTimespan
  - BlocksPerRetarget
  - RetargetAdjustmentFactor
  - MinDiffReductionTime
2016-08-10 16:02:23 -05:00
Dave Collins
c39e35318d Update for recent chainhash-related API changes. (#81)
This updates all code to make use of the new chainhash package since the
old wire.ShaHash type and related functions have been removed in favor
of the abstracted package.

Also, while here, rename all variables that included sha in their name
to include hash instead.
2016-08-08 14:16:04 -05:00
Dave Collins
bd4e64d1d4 chainhash: Abstract hash logic to new package. (#729)
This is mostly a backport of some of the same modifications made in
Decred along with a few additional things cleaned up.  In particular,
this updates the code to make use of the new chainhash package.

Also, since this required API changes anyways and the hash algorithm is
no longer tied specifically to SHA, all other functions throughout the
code base which had "Sha" in their name have been changed to Hash so
they are not incorrectly implying the hash algorithm.

The following is an overview of the changes:

- Remove the wire.ShaHash type
- Update all references to wire.ShaHash to the new chainhash.Hash type
- Rename the following functions and update all references:
  - wire.BlockHeader.BlockSha -> BlockHash
  - wire.MsgBlock.BlockSha -> BlockHash
  - wire.MsgBlock.TxShas -> TxHashes
  - wire.MsgTx.TxSha -> TxHash
  - blockchain.ShaHashToBig -> HashToBig
  - peer.ShaFunc -> peer.HashFunc
- Rename all variables that included sha in their name to include hash
  instead
- Update for function name changes in other dependent packages such as
  btcutil
- Update copyright dates on all modified files
- Update glide.lock file to use the required version of btcutil
2016-08-08 14:04:33 -05:00
Dave Collins
b6b1e55d1e TravisCI: Set vendor experiment variable earlier. (#731)
This sets the GO15VENDOREXPERIMENT environment variable before glide is
installed so vendoring is used when installing it as well.
2016-08-08 13:31:17 -05:00
Dave Collins
711f33450c chainhash: Implement a new chainhash package. (#730)
This is a backport of the chainhash package made in Decred along with a
few additional things cleaned up, finished test coverage, and rewording
of some documentation to make it more generic.

In particular, the new package provides the definition of the hash type
and associated hashing functions which will allow the rest of the code to be
agnostic to the specific hash algorithm.

This only implements the package and does not change any of the code
base over to use it.
2016-08-08 12:05:51 -05:00
Dave Collins
d406d9e52b wire: Consolidate tests into the wire pkg. (#728)
Putting the test code in the same package makes it easier for forks
since they don't have to change the import paths as much and it also
gets rid of the need for internal_test.go to bridge.

This same thing should probably be done for the majority of the code
base.
2016-08-08 11:42:54 -05:00
Calvin McAnarney
5f19113422 Allow RPC identifiers in E notation (#80) 2016-08-07 22:47:07 -05:00
David Hill
6e644855f5 Bump travis to use the latest golang - 1.6.3 (#723) 2016-07-25 13:48:19 -05:00
David Hill
61a15f6f1b blockchain: optimize HaveBlock (#720)
If a block is known to exist in the memory chain or database then
there is no need to check the orphan pool.
2016-07-25 10:16:57 -05:00
Dave Collins
00ebb9d14d blockchain: Associate time src with chain instance.
Rather than making the caller to pass in the median time source on
ProcessBlock and IsCurrent, modify the Config struct to include the
median time source and associate it with the chain instance when it is
created.

This is being done because both the ProcessBlock and IsCurrent functions
require access to the blockchain state already, it is a little bit safer
to ensure the time source matches the chain instance state, it
simplifies the caller logic, and it also allows its use within the logic
of the blockchain package itself which will be required by upcoming
rule change warning logic that is part of BIP9.
2016-07-14 13:10:47 -05:00
Jonathan Gillham
1ffc3dc18d peer: Fix logging of connected peer. 2016-06-24 13:39:50 +01:00
Jonathan Gillham
f3d759d783 peer: Extract protocol negotiation from main read and write code paths.
This allows cleaner separation of the half-duplex version negotiation from the fully duplex message passing between peers.
2016-06-24 13:18:33 +01:00
Jonathan Gillham
777ccdade3 peer: Remove error return from Connect. 2016-06-24 13:12:01 +01:00
Jonathan Gillham
5cbd1f85bf peer: Remove potential race when calling Connect. 2016-06-24 13:12:01 +01:00
David Hill
7de7bddba9 peer: use atomics instead of mutexes (#670) 2016-06-20 14:34:21 -05:00
Hector Jusforgues
ff4ada0b0e Add automatic RPC configuration. 2016-06-03 21:14:15 -05:00
Dave Collins
6229e35835 wire: Further reduce transaction allocs.
This commit drastically reduces the number of allocations needed to
deserialize a transaction and its scripts by using the combination of a
free list for initially deserializing the individual scripts along with
copying them into a single contiguous byte slice after the final size is
known and modifying each script in the transaction to point to its
location within the contiguous blob.

The end result is only a single allocation that holds all of the scripts
for a transaction regardless of the total number of scripts it has.

The script free list allows a maximum of 12,500 items with each buffer
being 512 bytes.  This implies it will have a peak usage of 6.1MB.  The
values were chosen based on profiling data and a desire to allow at
least 100 scripts per transaction to be simultaneously deserialized by
125 peers.

Also, while optimizing, decode directly into the existing previous
outpoint structure of each transaction input in order to avoid the extra
allocation per input that is otherwise caused when the local escapes to
the heap.

The following is a before and after comparison of the allocations
with the benchmarks that did not change removed:

benchmark              old allocs     new allocs     delta
-----------------------------------------------------------
ReadTxOut              1              0              -100.00%
ReadTxIn               2              0              -100.00%
DeserializeTxSmall     7              5              -28.57%
DeserializeTxLarge     11146          6              -99.95%
2016-06-03 17:09:14 -05:00
Dave Collins
2adfb3b56a wire: Reduce allocs with contiguous slices.
The current code involves a ton of small allocations which is harsh on
the garbage collector and in turn causes a lot of addition runtime
overhead both in terms of additional memory and processing time.

In order to improve the situation, this drasticially reduces the number
of allocations by creating contiguous slices of objects and
deserializing into them.  Since the final data structures consist of
slices of pointers to the objects, they are constructed by pointing them
into the appropriate offset of the contiguous slice.

This could be improved upon even further by converting all of the data
structures provided the wire package to be slices of contiguous objects
directly, however that would be a major breaking API change and would
end up requiring updating a lot more code in every caller.  I do think
that ultimately the API should be changed, but the changes in this
commit already makes a massive difference and it doesn't require
touching any of the callers, so it is a good place to begin.

The following is a before and after comparison of the allocations
with the benchmarks that did not change removed:

benchmark              old allocs     new allocs     delta
-----------------------------------------------------------
DeserializeTxLarge     16715          11146          -33.32%
DecodeGetHeaders       501            2              -99.60%
DecodeHeaders          2001           2              -99.90%
DecodeGetBlocks        501            2              -99.60%
DecodeAddr             3001           2002           -33.29%
DecodeInv              50003          3              -99.99%
DecodeNotFound         50002          3              -99.99%
DecodeMerkleBlock      107            3              -97.20%
2016-06-03 17:08:31 -05:00
Dave Collins
5de5b7354c wire: Avoid allocation on timestamp decodes.
Since the protocol encodes timestamps differently depending on the
message, the code currently decodes into a local variable and then
converts it to a time.Time.  However, this causes an allocation due to
the local having to escape to the heap in order for the readElement
function to write to it.

So, in order to avoid that, this introduces two new types for a
timestamp named uint32Time and int64Time that are encoded as the
respective type on the read.  When calling the readElements function,
the time.Time field in the message is cast to a pointer of the
appropriate type which effectively allows the allocations to be avoided.

The following is a before and after comparison of the allocations
with the benchmarks that did not change removed:

benchmark              old allocs     new allocs     delta
----------------------------------------------------------------------
ReadBlockHeader        1              0              -100.00%
DecodeHeaders          4001           2001           -49.99%
DecodeAddr             4001           3001           -24.99%
DecodeMerkleBlock      108            107            -0.93%
2016-06-03 17:08:31 -05:00
Dave Collins
f68cd7422d wire: Reduce allocs with a binary free list.
This introduces a new binary free list which provides a concurrent safe
list of unused buffers for the purpose of serializing and deserializing
primitive integers to their raw binary bytes.

For convenience, the type also provides functions for each of the
primitive unsigned integers that automatically obtain a buffer from the
free list, perform the necessary binary conversion, read from or write
to the given io.Reader or io.Writer, and return the buffer to the free
list.

A global instance of the type has been introduced with a maximum number
of 1024 items. Since each buffer is 8 bytes, it will consume a maximum
of 8KB.  Theoretically, this value would only allow up to 1024 peers
simultaneously reading and writing without having to resort to burdening
the garbage collector with additional allocations.  However, due to the
fact the code is designed in such a way that the buffers are quickly
used and returned to the free list, in practice it can support much more
than 1024 peers without involving the garbage collector since it is
highly unlikely every peer would need a buffer at the exact same time.

The following is a before and after comparison of the allocations
with the benchmarks that did not change removed:

benchmark              old allocs     new allocs     delta
-------------------------------------------------------------
WriteVarInt1           1              0              -100.00%
WriteVarInt3           1              0              -100.00%
WriteVarInt5           1              0              -100.00%
WriteVarInt9           1              0              -100.00%
ReadVarInt1            1              0              -100.00%
ReadVarInt3            1              0              -100.00%
ReadVarInt5            1              0              -100.00%
ReadVarInt9            1              0              -100.00%
ReadVarStr4            3              2              -33.33%
ReadVarStr10           3              2              -33.33%
WriteVarStr4           2              1              -50.00%
WriteVarStr10          2              1              -50.00%
ReadOutPoint           1              0              -100.00%
WriteOutPoint          1              0              -100.00%
ReadTxOut              3              1              -66.67%
WriteTxOut             2              0              -100.00%
ReadTxIn               5              2              -60.00%
WriteTxIn              3              0              -100.00%
DeserializeTxSmall     15             7              -53.33%
DeserializeTxLarge     33428          16715          -50.00%
SerializeTx            8              0              -100.00%
ReadBlockHeader        7              1              -85.71%
WriteBlockHeader       10             4              -60.00%
DecodeGetHeaders       1004           501            -50.10%
DecodeHeaders          18002          4001           -77.77%
DecodeGetBlocks        1004           501            -50.10%
DecodeAddr             9002           4001           -55.55%
DecodeInv              150005         50003          -66.67%
DecodeNotFound         150004         50002          -66.67%
DecodeMerkleBlock      222            108            -51.35%
TxSha                  10             2              -80.00%
2016-06-03 17:08:31 -05:00
Dave Collins
dc83f4ee6a addblock: Add support for generating indexes.
This adds two new flags, --txindex and --addrindex, to the addblock
utility which mirror the flags on btcd.  They serve to to specify that
the transaction index and/or address index, respectively, should be
built while importing from the bootstrap file.

This is technically not 100% required since btcd will build the indexes
on the first load (when enabled) if they aren't already built, however
it is much faster to build the indexes as the blocks are being validated
(particularly for the address index), so this makes the capability
available.
2016-06-03 17:06:16 -05:00
Mawueli Kofi Adzoe
7f07fb1093 txscript: Fix typo. (#700)
* Fix tiny typo. Bump copyright year.
* Clarify documentation.
2016-05-22 23:23:20 -05:00
Mawueli Kofi Adzoe
e8e2167a1a Bump up copyright. Reflect recent update. (#699) 2016-05-22 23:22:42 -05:00
Steven Roose
24e41c843b Update installation instructions using Glide (#698)
The main README.md file had newer installation that uses Glide, while these did not.
2016-05-21 17:30:26 -05:00
Nathan Bass
f893558d78 Minor correction to GenerateSharedSecret documentation. (#696) 2016-05-14 22:56:29 -05:00
Dave Collins
2554caee59 build: Convert project to use glide. (#689)
This converts the project to allow btcd to be used with the glide
package manager in order to provide stable and reproducible builds
without the user having to jump through all of the hoops as they do
today.

It consists of adding a glide.yaml file which identifies the project
dependencies and locations along with a glide.lock file which contains
the complete dependency tree pinned to specific versions.  Glide uses
these files to download the packages (or updates) to a local vendor
directory and checkout the correct pinned versions.  The go tool, in
turn, is used to build/install btcd and will use the pinned versions in
the vendor directory.

This also updates TravisCI to build using glide, removes some of the
exceptions in the lint checks which are no longer required, and updates
the README.md with the new instructions needed to build the project with
glide.
2016-05-06 10:47:53 -05:00
Dave Collins
128366734f main: Limit garbage collection percentage. (#686)
This reduces the target ratio of freshly allocated data to live data to
10% in order to limit excessive overallocations by the garbage collector
during data bursts such as processing complex blocks or rapidly
receiving a lot of large transactions.
2016-05-05 14:16:58 -05:00
David Hill
1a0e7452f3 btcd: handle signal SIGTERM (#688)
When an OS reboots or shuts down, it sends all processes SIGTERM before
sending SIGKILL.  This allows btcd to do a proper shutdown which most
importantly closes the database.
2016-05-05 14:16:42 -05:00
Jonathan Gillham
0d7f526600 mining: Correctly format log messages. (#685)
Type feePerKB is an int64 and therefore is not correctly interpreted by
format verb f.
2016-04-27 14:09:23 -05:00
David Hill
1b23410214 btcd: sendheaders server support (#671)
This adds support for serving headers instead of inventory messages in
accordance with BIP0130.  btcd itself does not yet make use of the
feature when receiving data.
2016-04-26 13:24:03 -05:00
Dave Collins
b14032487f wire: Add several decode benchmarks. (#682)
This adds decode benchmarks for several of the messages that profiling
has identified to cause a lot of allocations in addition to those that
already exist.  By adding these benchmarks, it makes it easier to get
allocation and speed statistics which can in turn be used to compare
future improvements.

The following bencharmarks have been added:

DecodeGetHeaders, DecodeHeaders, DecodeGetBlocks, DecodeAddr, DecodeInv,
DecodeNotFound, and DecodeMerkleBlock

For reference, here is the benchmark data as of this commit.

DecodeGetHeaders     93261 ns/op     24120 B/op     1004 allocs/op
DecodeHeaders      2071263 ns/op    368399 B/op    18002 allocs/op
DecodeGetBlocks      92486 ns/op     24120 B/op     1004 allocs/op
DecodeAddr          850608 ns/op    136202 B/op     9002 allocs/op
DecodeInv         17107172 ns/op   3601447 B/op   150004 allocs/op
DecodeNotFound    17522225 ns/op   3601444 B/op   150004 allocs/op
DecodeMerkleBlock    21062 ns/op      5192 B/op      222 allocs/op
2016-04-25 17:32:29 -05:00
Dave Collins
e7ddaa468e wire: Don't allocate new readers in benchmarks. (#679)
This modifies the benchmarks in the wire package to avoid creating a new
reader for each iteration.  This is useful since it means that showing
the memory allocations will only show the function under test instead of
the allocation for the benchmark setup as well.

The following is a before and after comparison of the allocations
with the benchmarks that did not change removed:

benchmark              old allocs     new allocs     delta
------------------------------------------------------------
ReadVarInt1            2              1              -50.00%
ReadVarInt3            2              1              -50.00%
ReadVarInt5            2              1              -50.00%
ReadVarInt9            2              1              -50.00%
ReadVarStr4            4              3              -25.00%
ReadVarStr10           4              3              -25.00%
ReadOutPoint           2              1              -50.00%
ReadTxOut              4              3              -25.00%
ReadTxIn               6              5              -16.67%
DeserializeTxSmall     16             15             -6.25%
DeserializeTxLarge     33430          33428          -0.01%
ReadBlockHeader        8              7              -12.50%
2016-04-25 16:57:44 -05:00
Dave Collins
27c0f9f8d1 wire: Add large tx deserialize benchmark. (#678)
This adds a benchmark for deserializing a large transaction that is
often referred to as the megatransaction since it is the largest Bitcoin
transaction mined to date.  It consists of 5569 inputs and 1 output and
its hash is:

bb41a757f405890fb0f5856228e23b715702d714d59bf2b1feb70d8b2b4e3e08.

This is being done so there is a benchmark that tests more of a
worst-case scenario which is a better candidate for identifying and
testing improvements.

The following benchmark results shows the how much more intensive this
transaction is over the existing mock transaction:

DeserializeTxSmall  1000000    1751 ns/op      376 B/op     16 allocs/op
DeserializeTxLarge  300     5093980 ns/op  1672829 B/op  33430 allocs/op
2016-04-25 16:51:27 -05:00
Dave Collins
de4fb24389 blockchain: Remove unused root field. (#680)
This removes the root field and all references to it from the BlockChain
since it is no longer required.

It was previously required because the chain state was not initialized
when the instance was created.  However, that is no longer the case, so
there is no reason to keep it around any longer.
2016-04-25 16:17:29 -05:00
Dave Collins
644570487f txscript: Reduce script parse opcode allocs. (#677)
This changes the script template parsing function to use a pointer into
the constant global opcode array for parsed opcodes as opposed to making
a copy of the opcode entries which causes unnecessary allocations.

Profiling showed that after roughly 48 hours of operation, this
copy was the culprit of 207 million unnecessary allocations.
2016-04-25 16:17:07 -05:00
Dave Collins
b87723cd94 btcd: Remove peer-specific logging funcs. (#675)
This removes the logging functions that are now implemented in the peer
package as they are no longer used by btcd itself and should have been
removed when they were copied into the peer package.
2016-04-20 23:58:31 -05:00
David Hill
474547b211 travis: run tests on latest golang (1.5.4 and 1.6.1) (#669) 2016-04-14 13:38:25 -05:00
David Hill
a1bb291b28 mempool: Have ProcessTransaction return accepted transactions. (#547)
It is not the responsibility of mempool to relay transactions, so
return a slice of transactions accepted to the mempool due to the
passed transaction to the caller.
2016-04-14 12:58:09 -05:00
Dave Collins
e15d3008cf mining: Improve tests for prio queue. (#667)
This improves the tests of the priority queue to include the secondary
sort ordering as well as adds some manual entries to ensure the edge
conditions are properly tested.

This also brings the priority queue test coverage up to 100%.
2016-04-14 00:19:23 -05:00
Tadge Dryja
432ad76952 fix memory allignment for 32-bit architectures (#668)
having 3 int32s above the uint64s in the struct
will cause misalignment for some 32-bit architectures.
see https://golang.org/pkg/sync/atomic/#pkg-note-BUG
This aligns bytesReceived and bytesSent.
2016-04-13 22:51:02 -05:00
Olaoluwa Osuntokun
3b39edcaa1 txscript: optimize sigcache lookup (#598)
Profiles discovered that lookups into the signature cache included an
expensive comparison to the stored `sigInfo` struct. This lookup had the
potential to be more expensive than directly verifying the signature
itself!

In addition, evictions were rather expensive because they involved
reading from /dev/urandom, or equivalent, for each eviction once the
signature cache was full as well as potentially iterating over every
item in the cache in the worst-case.

To remedy this poor performance several changes have been made:
* Change the lookup key to the fixed sized 32-byte signature hash
* Perform a full equality check only if there is a cache hit which
    results in a significant  speed up for both insertions and existence
checks
* Override entries in the case of a colliding hash on insert Add an
* .IsEqual() method to the Signature and PublicKey types in the
  btcec package to facilitate easy equivalence testing
* Allocate the signature cache map with the max number of entries in
  order to avoid unnecessary map re-sizes/allocations
* Optimize evictions from the signature cache Delete the first entry
* seen which is safe from manipulation due to
    the pre image resistance of the hash function
* Double the default maximum number of entries within the signature
  cache due to the reduction in the size of a cache entry
  * With this eviction scheme, removals are effectively O(1)

Fixes #575.
2016-04-13 21:56:10 -05:00
Dave Collins
5a1e77bd2d blockchain: Remove unneeded unspentness size check. (#665)
The current code is needlessly checking the number of bytes needed to
serialize the unspentness bitmap in the utxo against a maximum value
that could never be returned because the function takes a uint32 output
index which is treated as a bit offset, and converts it bytes, which
will necessarily be less than a max uint32.

This check also causes a compile error on arm where native integers are
32 bits.

This simply removes the unneeded check.
2016-04-13 21:43:16 -05:00
Dave Collins
5b14e157ea mining: Add tests for prio queue. (#666)
This adds a test to ensure the priority queue works properly both for
sorting by fee per KB and priorities.

Thanks to @ceejep for the original test code and idea which was
subsequently modified and cleaned up a bit to the code here.
2016-04-13 14:36:53 -05:00
Dave Collins
b580cdb7d3 database: Replace with new version.
This commit removes the old database package, moves the new package into
its place, and updates all imports accordingly.
2016-04-12 14:55:15 -05:00
Dave Collins
7c174620f7 indexers: Implement optional tx/address indexes.
This introduces a new indexing infrastructure for supporting optional
indexes using the new database and blockchain infrastructure along with
two concrete indexer implementations which provide both a
transaction-by-hash and a transaction-by-address index.

The new infrastructure is mostly separated into a package named indexers
which is housed under the blockchain package.  In order to support this,
a new interface named IndexManager has been introduced in the blockchain
package which provides methods to be notified when the chain has been
initialized and when blocks are connected and disconnected from the main
chain.  A concrete implementation of an index manager is provided by the
new indexers package.

The new indexers package also provides a new interface named Indexer
which allows the index manager to manage concrete index implementations
which conform to the interface.

The following is high level overview of the main index infrastructure
changes:

- Define a new IndexManager interface in the blockchain package and
  modify the package to make use of the interface when specified
- Create a new indexers package
  - Provides an Index interface which allows concrete indexes to plugin
    to an index manager
  - Provides a concrete IndexManager implementation
    - Handles the lifecycle of all indexes it manages
    - Tracks the index tips
    - Handles catching up disabled indexes that have been reenabled
    - Handles reorgs while the index was disabled
    - Invokes the appropriate methods for all managed indexes to allow
      them to index and deindex the blocks and transactions
  - Implement a transaction-by-hash index
    - Makes use of internal block IDs to save a significant amount of
      space and indexing costs over the old transaction index format
  - Implement a transaction-by-address index
    - Makes use of a leveling scheme in order to provide a good tradeoff
      between space required and indexing costs
- Supports enabling and disabling indexes at will
- Support the ability to drop indexes if they are no longer desired

The following is an overview of the btcd changes:

- Add a new index logging subsystem
- Add new options --txindex and --addrindex in order to enable the
  optional indexes
  - NOTE: The transaction index will automatically be enabled when the
    address index is enabled because it depends on it
- Add new options --droptxindex and --dropaddrindex to allow the indexes
  to be removed
  - NOTE: The address index will also be removed when the transaction
    index is dropped because it depends on it
- Update getrawtransactions RPC to make use of the transaction index
- Reimplement the searchrawtransaction RPC that makes use of the address
  index
- Update sample-btcd.conf to include sample usage for the new optional
  index flags
2016-04-11 17:16:42 -05:00
Dave Collins
491acd4ca6 blockchain: Rework to use new db interface.
This commit is the first stage of several that are planned to convert
the blockchain package into a concurrent safe package that will
ultimately allow support for multi-peer download and concurrent chain
processing.  The goal is to update btcd proper after each step so it can
take advantage of the enhancements as they are developed.

In addition to the aforementioned benefit, this staged approach has been
chosen since it is absolutely critical to maintain consensus.
Separating the changes into several stages makes it easier for reviewers
to logically follow what is happening and therefore helps prevent
consensus bugs.  Naturally there are significant automated tests to help
prevent consensus issues as well.

The main focus of this stage is to convert the blockchain package to use
the new database interface and implement the chain-related functionality
which it no longer handles.  It also aims to improve efficiency in
various areas by making use of the new database and chain capabilities.

The following is an overview of the chain changes:

- Update to use the new database interface
- Add chain-related functionality that the old database used to handle
  - Main chain structure and state
  - Transaction spend tracking
- Implement a new pruned unspent transaction output (utxo) set
  - Provides efficient direct access to the unspent transaction outputs
  - Uses a domain specific compression algorithm that understands the
    standard transaction scripts in order to significantly compress them
  - Removes reliance on the transaction index and paves the way toward
    eventually enabling block pruning
- Modify the New function to accept a Config struct instead of
  inidividual parameters
- Replace the old TxStore type with a new UtxoViewpoint type that makes
  use of the new pruned utxo set
- Convert code to treat the new UtxoViewpoint as a rolling view that is
  used between connects and disconnects to improve efficiency
- Make best chain state always set when the chain instance is created
  - Remove now unnecessary logic for dealing with unset best state
- Make all exported functions concurrent safe
  - Currently using a single chain state lock as it provides a straight
    forward and easy to review path forward however this can be improved
    with more fine grained locking
- Optimize various cases where full blocks were being loaded when only
  the header is needed to help reduce the I/O load
- Add the ability for callers to get a snapshot of the current best
  chain stats in a concurrent safe fashion
  - Does not block callers while new blocks are being processed
- Make error messages that reference transaction outputs consistently
  use <transaction hash>:<output index>
- Introduce a new AssertError type an convert internal consistency
  checks to use it
- Update tests and examples to reflect the changes
- Add a full suite of tests to ensure correct functionality of the new
  code

The following is an overview of the btcd changes:

- Update to use the new database and chain interfaces
- Temporarily remove all code related to the transaction index
- Temporarily remove all code related to the address index
- Convert all code that uses transaction stores to use the new utxo
  view
- Rework several calls that required the block manager for safe
  concurrency to use the chain package directly now that it is
  concurrent safe
- Change all calls to obtain the best hash to use the new best state
  snapshot capability from the chain package
- Remove workaround for limits on fetching height ranges since the new
  database interface no longer imposes them
- Correct the gettxout RPC handler to return the best chain hash as
  opposed the hash the txout was found in
- Optimize various RPC handlers:
  - Change several of the RPC handlers to use the new chain snapshot
    capability to avoid needlessly loading data
  - Update several handlers to use new functionality to avoid accessing
    the block manager so they are able to return the data without
    blocking when the server is busy processing blocks
  - Update non-verbose getblock to avoid deserialization and
    serialization overhead
  - Update getblockheader to request the block height directly from
    chain and only load the header
  - Update getdifficulty to use the new cached data from chain
  - Update getmininginfo to use the new cached data from chain
  - Update non-verbose getrawtransaction to avoid deserialization and
    serialization overhead
  - Update gettxout to use the new utxo store versus loading
    full transactions using the transaction index

The following is an overview of the utility changes:
- Update addblock to use the new database and chain interfaces
- Update findcheckpoint to use the new database and chain interfaces
- Remove the dropafter utility which is no longer supported

NOTE: The transaction index and address index will be reimplemented in
another commit.
2016-04-11 16:47:27 -05:00
David Hill
123ff368f4 mempool: Create and use mempoolPolicy. (#571)
mempoolPolicy contains the values that configure the mempool policy.
This decouples the values from the internals of btcd to move closer
to a mempool package.
2016-04-11 16:37:52 -05:00
Dave Collins
5ff5fc5fa2 txscript: Correct comments on alt stack methods. (#657) 2016-04-11 14:22:25 -05:00
Dave Collins
1d83cd5721 chaincfg: Consolidate tests into the chaincfg pkg. (#662)
Putting the test code in the same package makes it easier for forks
since they don't have to change the import paths as much and it also
gets rid of the need for internal_test.go to bridge.

This same thing should probably be done for the majority of the code
base.
2016-04-11 14:21:40 -05:00
Dave Collins
c7e5d56b58 chaincfg: Register networks instead of hard coding. (#660)
This modifies the chaincfg package to register the default network
params via the init function instead of manually hard coding their data
into the maps.  This is less error prone when adding new default
networks.

A new function named mustRegister has been introduced that panics if
there are any errors when registering the network that the new code
makes use of and appropriate tests have been added.
2016-04-11 12:34:28 -05:00
Dave Collins
23f59144c7 server: Optimize map limiting in block manager. (#658)
This optimizes the way in which the maps are limited by the block
manager.

Previously the code would read a cryptographically random value large
enough to construct a hash, find the first entry larger than that value,
and evict it.

That approach is quite inefficient and could easily become a bottleneck
when processing transactions due to the need to read from a source such
as /dev/urandom and all of the subsequent hash comparisons.

Luckily, strong cryptographic randomness is not needed here. The primary
intent of limiting the maps is to control memory usage with a secondary
concern of making it difficult for adversaries to force eviction of
specific entries.

Consequently, this changes the code to make use of the pseudorandom
iteration order of Go's maps along with the preimage resistance of the
hashing function to provide the desired functionality.  It has
previously been discussed that the specific pseudorandom iteration order
is not guaranteed by the Go spec even though in practice that is how it
is implemented.  This is not a concern however because even if the
specific compiler doesn't implement that, the preimage resistance of the
hashing function alone is enough.

Thanks to @Roasbeef for pointing out the efficiency concerns and the
fact that strong cryptographic randomness is not necessary.
2016-04-11 10:29:07 -05:00
Dave Collins
a3fa066745 mining: Export block template fields. (#659)
This simply exports and adds some comments to the fields of the
BlockTemplate struct.

This is primarily being done as a step toward being able to separate the
mining code into its own package, but also it makes sense on its own
because code that requests new block template necessarily examines the
returned fields which implies they should be exported.
2016-04-11 10:27:29 -05:00
David Hill
cab74feb59 Keep track of recently rejected transactions. (#484)
This prevents the node from repeatedly requesting and rejecting the
same transaction as different peers inv the same transaction.

Idea from Bitcoin Core commit 0847d9cb5fcd2fdd5a21bde699944d966cf5add9

Also, limit the number of both requested blocks and transactions.
2016-04-10 01:12:57 -05:00
David Hill
d1e493f4ee config: New option --blocksonly (#553)
The --blocksonly configuration option disables accepting transactions
from remote peers.  It will still accept, relay, and rebroadcast
valid transactions sent via RPC or websockets.
2016-04-07 18:16:46 -05:00
David Hill
7b31349023 Cleanup and optimize handleBroadcastMsg 2016-04-07 14:50:42 -04:00
Dave Collins
64922553b5 TravisCI: Update to latest configurations. (#76)
This updates the Go versions using when running the TravisCI integration
tests to reflect the latest supported Go versions.

Also, the vet tool moved into the Go source tree as of Go 1.5.  Its previous
location in the x/tools repo was deprecated at that time and has now
been removed.

Finally, old tool path is no longer needed, so it has been removed.
2016-04-07 13:26:15 -05:00
Dave Collins
69839adc1c TravisCI: Remove external go vet reference. (#655)
The vet tool moved into the Go source tree as of Go 1.5.  Its previous
location in the x/tools repo was deprecated at that time and has now
been removed.

This commit updates the .travis.yml configuration to avoid fetching vet
from the old location and to simply use the version now available as
part of the standard Go install.

Also, while here, remove the check for changing the tool path since it
is no longer needed.
2016-04-07 13:21:14 -05:00
David Hill
8a58f8cf3a peer: Implement sendheaders support (BIP0130).
This modifies the peer package to add support for the sendheaders
protocol message introduced by BIP0030.

NOTE: This does not add support to btcd itself. That requires the server
and sync code to make use of the new functionality exposed by these
changes.  As a result, btcd will still be using protocol version 70011.
2016-04-06 16:56:48 -05:00
David Hill
c1861bc8fa peer: declare QueueMessage()'s doneChan as send only.
This ensures the channel passed to QueueMessage is writable and that
QueueMessage will not read from the channel (write-only).

This change is merely a safety change.  If a user of the API passes
a read-only channel to QueueMessage, it will now be caught at compile
time instead of panicking during runtime.

Also update internal functions.
2016-04-06 13:50:27 -05:00
Jonathan Gillham
391d5e4a01 server: Stop main loop from blocking when RPC server is not running.
When the RPC server is not running a buffered transaction notification

channel fills and eventually blocks.  This commit ensures that the

channel continues to be drained irrespective of the RPC server status.
2016-04-06 13:20:01 -05:00
Dave Collins
e08038115b TravisCI: Remove Go 1.4.3 tests.
Since the latest golint no longer works with on Go 1.4 and Go 1.4 is no
longer officially supported by btcsuite, remove it from the
configurations tested by TravisCI.
2016-03-29 15:52:20 -05:00
Dave Collins
37938375dc docs: Update READMEs with current details.
This commit updates the main README.md and docs/README.md files to
replace the references to the now dead btcgui project with the
Windows-only Paymetheus project.

While here, it also updates some information to make it more current and
accurately describe the current status.
2016-03-11 14:53:15 -06:00
Dave Collins
e6e3b66040 Improve error log avoidance on client disconnect.
The existing check to only log errors from websocket connections when
the error is not because the connection is being shutdown only detects
the single case where the remote client was shutdown.

This commit modifies that logic to include when the connection is being
forcibly shutdown via closing the quit channel and when it has been
shutdown from a client side close.
2016-03-10 22:36:52 -06:00
Jonathan Gillham
5c59b685e6 server: Appropriately name inbound peers map in peerState. 2016-02-27 15:52:40 +00:00
Dave Collins
f389742b39 multi: Update with result of gofmt -s.
This commit updates the code to make use of the most recent simplified
output from gofmt.
2016-02-25 13:02:54 -06:00
Dave Collins
d4852101d4 Update TravisCI and README for Go 1.6.
Now that Go 1.6 has been released, update the required Go version in the
README to 1.5 and add Go 1.6 to the configurations tested by TravisCI.

Also, while here, update the Go 1.4 and 1.5 versions tested by TravisCI
to the latest point releases.
2016-02-25 12:43:57 -06:00
Dave Collins
eb882f39f8 multi: Fix several misspellings in the comments.
This commit corrects several typos in the comments found by misspell.
2016-02-25 11:17:12 -06:00
Tibor Bősze
ef9c50be57 Fix typos in comments 2016-02-22 19:57:29 +01:00
Jouke Hofman
c17ff82061 wire: Export (read|write)(VarInt|VarBytes). 2016-02-22 18:11:58 +01:00
Dave Collins
f4d551c08d findcheckpoint: Update to allow first checkpoint.
This updates the findcheckpoint utility to work when there are not
already any checkpoints.  This doesn't really matter for Bitcoin at the
current time, but if a new testnet is created it will not have any
checkpoints to start with and this change also means the utility can
work for alts.

While here, switch a couple of error prints to ensure they contain a
final newline.
2016-02-19 15:53:06 -06:00
Dave Collins
f45db028db database: Remove tx log in favor of new ldb txns.
This removes the intermediate transaction log that was introduced as a
part of the database cache as a workaround for leveldb batches causing
massive memory usage spikes in favor of the recently introduced leveldb
transaction interface which no longer has the memory usage issues.

This approach is preferred because it can avoid the extra memory needed
for the transaction log and therefore all of the intermediate states as
well.  As a result, the default cache size has been doubled since it
equals roughly the same amount of overall memory usage and the flush
interval has been raised as well.
2016-02-16 11:46:43 -06:00
Tibor Bősze
c75fea9c94 Implement banning based on dynamic ban scores
Dynamic ban scores consist of a persistent and a decaying component. The
persistent score can be used to create simple additive banning policies
simlar to those found in other bitcoin node implementations. The
decaying score enables the creation of evasive logic which handles
misbehaving peers (especially application layer DoS attacks) gracefully
by disconnecting and banning peers attempting various kinds of flooding.
Dynamic ban scores allow these two approaches to be used in tandem.

This pull request includes the following:

 - Dynamic ban score type & functions, with tests for core functionality
 - Ban score of connected peers can be queried via rpc (getpeerinfo)
 - Example policy with decaying score increments on mempool and getdata
 - Logging of misbehavior once half of the ban threshold is reached
 - Banning logic can be disabled via configuration (enabled by default)
 - User defined ban threshold can be set via configuration
2016-02-16 10:10:29 +01:00
David Hill
907152cef9 mempool: reduce lock contention 2016-02-14 22:03:37 -05:00
Dave Collins
9abc2c0e19 txscript: Comment improvements and fixes
This commit improves and corrects a few comments in txscript to ensure
they match reality.
2016-02-11 21:43:32 -06:00
Kefkius
d272bfebb7 Fix documentation for opcodeInvalid
Change 'opcodeReserved' to 'opcodeInvalid'
2016-02-11 20:42:41 -06:00
Dave Collins
d127ad4083 server: Make consistent use of svr peer stringer.
This updates a couple of logging statements to use the serverPeer
instance instead of the embedded peer.Peer so they are consistent with
all of the other log statements.
2016-02-10 22:29:30 -06:00
Kefkius
d759d1d3df Remove duplicate stack tests. 2016-02-09 11:17:04 -06:00
John C. Vernaleo
b7f030192e Do not use unkeyed fields. 2016-02-08 17:50:38 -05:00
Jonathan Gillham
73d353247c peer: Consolidate Connect, Disconnect, Start, Shutdown public methods.
This commit does not change functionality. It makes the creation of inbound and outbound peers more homogeneous. As a result the Start method of peer was removed as it was found not to be necessary. This is the first of several pull requests/commits designed to make the peer public API and internals less complex.
2016-02-06 11:11:15 +00:00
David Hill
ae00fff14a wire: Implement sendheaders command (BIP0130)
This implements the wire protocol encoding portion of a new
sendheaders message as described by BIP0130. It purpose is to request
that a peer sends header commands instead of inv commands when
announcing new blocks. This includes a protocol version bump to 70012
and a wire version bump to 0.4.0.

Note that this does not implement logic to handle the command in btcd,
rather it only makes the command available at the wire protocol level.
A future commit which honors the command and therefore provides full
BIP0130 support is still required.
2016-02-05 12:41:39 -05:00
David Hill
383ed041ec Use atomic operations instead of mutexes. 2016-02-04 15:20:04 -05:00
Dave Collins
16582789c3 rpcserver: Optimize filteraddr code.
This optimizes the filter address handling code in the RPC server
handleSearchRawTransasctions path in a few ways:

- Only normalize the filter addresses provided in the command once
  rather than for every transaction being returned
- Reset the passes filter flag just before it's used
- Use a local for the encoded address to avoid the bounds checking
  overhead of accessing it from the slice
- Avoiding subsequent map lookups once the filter has already passed

Also, while here, modify a few of the related comments to match code
style and consistency.
2016-02-03 20:38:45 -06:00
Dave Collins
c7e6c1e88f txscript: Correct JSON float conversions in tests.
This modifies the conversion of the output index from the JSON-based
test data for valid and invalid transactions as well as the signature
hash type for signature hash tests to first convert to a signed int and
then to an unsigned int.  This is necessary because the result of a
direct conversion of a float to an unsigned int is implementation
dependent and doesn't result in the expected value on all platforms.

Also, while here, change the function names in the error prints to match
the actual names.

Fixes #600.
2016-02-03 13:38:35 -06:00
Dave Collins
3c2c858888 btcjson: Specify constant size in tests.
This modifies the test for createrawtransaction to specify the constant
size passed as an int64.  This is necessary because the NewCmd function
accepts the parameters as interfaces in order to support varargs and a
raw numeric constant is treated as an integer.  Since the constant value
is larger than an int32, this causes certain platforms like ARM which
treat a raw integer as a 32-bit integer to fail to compile.

This is work towards #600.
2016-02-03 13:13:11 -06:00
Dave Collins
0b32febe5c database: Implement cache layer.
This commit adds a database cache layer to the ffldb database backend so
that callers can commit multiple transactions without having to incur
the overhead of a disk sync on every new block.
2016-02-03 11:42:14 -06:00
Dave Collins
af3ed803f5 database: Major redesign of database package.
This commit contains a complete redesign and rewrite of the database
package that approaches things in a vastly different manner than the
previous version.  This is the first part of several stages that will be
needed to ultimately make use of this new package.

Some of the reason for this were discussed in #255, however a quick
summary is as follows:

- The previous database could only contain blocks on the main chain and
  reorgs required deleting the blocks from the database.  This made it
  impossible to store orphans and could make external RPC calls for
  information about blocks during the middle of a reorg fail.
- The previous database interface forced a high level of bitcoin-specific
  intelligence such as spend tracking into each backend driver.
- The aforementioned point led to making it difficult to implement new
  backend drivers due to the need to repeat a lot of non-trivial logic
  which is better handled at a higher layer, such as the blockchain
  package.
- The old database stored all blocks in leveldb.  This made it extremely
  inefficient to do things such as lookup headers and individual
  transactions since the entire block had to be loaded from leveldb (which
  entails it doing data copies) to get access.

In order to address all of these concerns, and others not mentioned, the
database interface has been redesigned as follows:

- Two main categories of functionality are provided: block storage and
  metadata storage
- All block storage and metadata storage are done via read-only and
  read-write MVCC transactions with both manual and managed modes
  - Support for multiple concurrent readers and a single writer
  - Readers use a snapshot and therefore are not blocked by the writer
- Some key properties of the block storage and retrieval API:
  - It is generic and does NOT contain additional bitcoin logic such spend
    tracking and block linking
  - Provides access to the raw serialized bytes so deserialization is not
    forced for callers that don't need it
  - Support for fetching headers via independent functions which allows
    implementations to provide significant optimizations
  - Ability to efficiently retrieve arbitrary regions of blocks
    (transactions, scripts, etc)
- A rich metadata storage API is provided:
  - Key/value with arbitrary data
  - Support for buckets and nested buckets
  - Bucket iteration through a couple of different mechanisms
  - Cursors for efficient and direct key seeking
- Supports registration of backend database implementations
- Comprehensive test coverage
- Provides strong documentation with example usage

This commit also contains an implementation of the previously discussed
interface named ffldb (flat file plus leveldb metadata backend).  Here
is a quick overview:

- Highly optimized for read performance with consistent write performance
  regardless of database size
- All blocks are stored in flat files on the file system
- Bulk block region fetching is optimized to perform linear reads which
  improves performance on spindle disks
- Anti-corruption mechanisms:
  - Flat files contain full block checksums to quickly an easily detect
    database corruption without needing to do expensive merkle root
    calculations
  - Metadata checksums
  - Open reconciliation
- Extensive test coverage:
  - Comprehensive blackbox interface testing
  - Whitebox testing which uses intimate knowledge to exercise uncommon
    failure paths such as deleting files out from under the database
  - Corruption tests (replacing random data in the files)

In addition, this commit also contains a new tool under the new database
directory named dbtool which provides a few basic commands for testing the
database.  It is designed around commands, so it could be useful to expand
on in the future.

Finally, this commit addresses the following issues:

- Adds support for and therefore closes #255
- Fixes #199
- Fixes #201
- Implements and closes #256
- Obsoletes and closes #257
- Closes #247 once the required chain and btcd modifications are in place
  to make use of this new code
2016-02-03 11:42:04 -06:00
Chris Shepherd
528ddaf23e txscript: Fix typo in README 2016-01-29 12:39:11 -08:00
Jonathan Gillham
95361a2afc wire: Minor code clean up. 2016-01-26 23:09:18 +00:00
Jonathan Gillham
e03fa30e89 peer: Simplify PushAddrMsg method loop.
This implementation ensures that all addresses have an equal chance of
being included in the addr message. It also moves the pseudorandom number
generator seeding to package level so that it can be overridden for
testing if required.
2016-01-24 10:52:59 +00:00
Jonathan Zeppettini
407fcc2aaf Update LICENSE 2016-01-23 16:06:10 -05:00
Mawuli Adzoe
f5ded65636 Change copyright date for code that was updated this year(2016). 2016-01-07 09:01:51 -07:00
Mawuli Adzoe
1944637333 Bump copyright date to reflect fixes since the beginning of this year. 2016-01-06 15:29:58 -07:00
Javed Khan
7996eb1f9d peer: drain chans before exiting peerHandler
Also disconnect the failed peer to allow the peerDoneHandler goroutine
to exit, instead of hanging around. Fixes #583.
2016-01-06 20:59:43 +05:30
David Hill
e24fe94f58 chaincfg: Remove testnet-seed.alexykot.me, it is defunct. 2016-01-05 16:16:11 -05:00
Daniel Martí
829e87a733 Replace *KoblitzCurve by elliptic.Curve
Found via github.com/mvdan/interfacer.
2016-01-03 13:40:26 +01:00
Mawuli Adzoe
73a9dd628d Bump up copyright date to reflect recent fix time. 2016-01-02 14:08:08 -07:00
Mawuli Adzoe
911a735e1f Clarify documentation. Fix typo. 2016-01-02 14:06:55 -07:00
Mawuli Adzoe
14ccab80e7 Review and fix typos in SigCache code. 2015-12-30 11:57:15 -07:00
David Hill
34a94b7d0b txscript: sync Bitcoin Core script tests 2015-12-30 09:38:16 -05:00
Mawuli Adzoe
6e133b58da txscript: Fix docs to match function.
Changed the order of return values described in the docs to be
consistent with the function’s actual return value signature.
2015-12-29 11:42:03 -07:00
Mawueli Kofi Adzoe
ff0c787237 Fix inconsistent spacing.
guidelinestherin ---> guidelines therein.
2015-12-29 10:59:57 -07:00
Mawuli Adzoe
df20c1074c Fix typos in changelog.
Reviewed changelog and fixed 2 tiny typos.
2015-12-29 09:43:44 -07:00
Dario Nieuwenhuis
d0cdd53720 server: Fix persistent peers not being removed properly
When a persistent peer is disconnected (for example due to a
network timeout), a connection retry is issued. The logic for
doing so failed to remove the peer from the peerState, causing
dead peer connections to fill the peerState. Since connections
in the peerState are counted towards the maxPeers limit, this
would cause btcd to eventually stop retrying connection.

This commit fixes the issue by properly removing the peer from
the peerState.
2015-12-28 01:23:10 +01:00
Dario Nieuwenhuis
89af747603 peer: remove getaddr msg from stall detection.
The getaddr msg is usually replied to with an addr msg, but if
the other peer does not have any addresses to share it will not
reply at all (instead of replying with an addr msg with 0 addresses).
Therefore, the getaddr msg is not guaranteed a reply, so this commit
removes it from stall detection to avoid incorrectly kicking
such peers.
2015-12-27 19:50:36 +01:00
Dario Nieuwenhuis
87182a2ddf blockchain: Correct serialized coinbase height error message. 2015-12-21 20:59:12 +01:00
Jonathan Gillham
4d40a2110a peer: Rename variable for consistency. 2015-12-14 21:52:06 -08:00
Javed Khan
542844832e peer: fix panic due to err in handleVersionMsg
In case of an error during protocol negotiation in handleVersionMsg,
immediately break out and prevent further processing of OnVersion
listener which generally depends upon peer attributes like NA to be set
during the negotiation. Fixes #579.
2015-12-10 01:16:03 +05:30
Dave Collins
2f6aeacfab server: Correct mempool/CPU miner initialize order.
The CPU miner relies on the mempool, so the mempool has to be created
before calling the function to create the CPU miner.  When PR #568
introduced the mempool config struct, it moved the mempool creation
after the miner creation, which leads to the CPU miner crashing due to
trying to access a nil mempool.

This move the CPU miner creation after the mempool creation
appropriately.
2015-12-08 02:01:53 -06:00
David Hill
2a7f41cddb peer: Add DisableRelayTx to config.
DisableRelayTx sets the DisableRelayTx value in the version
message which informs the remote peer on whether to send
inv messages for transactions.
2015-12-03 10:29:06 -05:00
Dave Collins
ce981f45c2 mining: Create skeleton package.
This creates a skeleton mining package that simply contains a few of the
definitions used by the mining and mempool code.

This is a step towards decoupling the mining code from the internals of
btcd and ultimately will house all of the code related to creating block
templates and CPU mining.

The main reason a skeleton package is being created before the full
blown package is ready is to avoid blocking mempool separation which
relies on these type definitions.
2015-11-30 12:23:50 -06:00
David Hill
83bcfea271 mempool: Introduce mempoolConfig.
This is in preparation of moving mempool to its own subpackage.  No
functional change.
2015-11-27 18:34:27 -05:00
Dave Collins
2b6a9a56e5 mempool/mining: Introduce TxSource interface.
This introduces the concept of a new interface named TxSource which aims
to generically provide a concurrent safe source of transactions to be
considered for inclusion in a new block.  This is a step towards
decoupling the mining code from the internals of btcd.  Ultimately the
intent is to create a separate mining package.

The new TxSource interface relies on a new struct named miningTxDesc,
which describes each entry in the transaction source.  Once this code is
refactored into a separate mining package, the mining prefix can simply
be dropped leaving the type exported as mining.TxDesc.

To go along with this, the existing TxDesc type in the mempool has been
renamed to mempoolTxDesc and changed to embed the new miningTxDesc type.
This allows the mempool to efficiently implement the MiningTxDescs
method needed to satisfy the TxSource interface.

This approach effectively separates the direct reliance of the mining
code on the mempool and its data structures.  Even though the memory
pool will still be the default concrete implementation of the interface,
making it an interface offers much more flexibility in terms of testing
and even provides the potential to allow more than one source (perhaps
multiple independent relay networks, for example).

Finally, the memory pool and all of the mining code has been updated to
implement and use the new interface.
2015-11-25 13:30:44 -06:00
Dave Collins
8ab565ce21 mempool/mining: Decouple and optimize priority calcs.
This does three things:

- Splits the priority calculation logic from the TxDesc type
- Modifies the calcPriority function to perform the value age
  calculation instead of accepting it as a parameter
- Changes the starting priority to be calculated when the transaction is
  added to the pool

The first is useful as it is a step towards decoupling the mining code
from the internals of the memory pool.  Also, the concept of priority is
related to mining policy, so it makes more sense to have the
calculations separate than being defined on the memory pool tx
descriptor.

The second change has been made because everywhere that uses the
calcPriority function first has to calculate the value age anyways and
by making it part of the function it can be avoided altogether in
certain circumstances thereby provided a bit of optimization.

The third change ensure the starting priority is safe for reentrancy
which will be important once the mempool is split into a separate
package.
2015-11-25 12:39:47 -06:00
Dave Collins
f41ff545be server: Improve the persistent peer retry logic.
This fixes an issue introduced during the peer refactor where persistent
peers that failed the initial connection are not retried as intended.

It also improves the retry logic as follows:

- Make the retry handler goroutine simply use a for loop instead of
  launching a new goroutine for each backoff attempt.  Even though
  goroutines are fairly cheap to create, it is much more efficient to
  simply loop
- Change the retry handler to accept a flag if it is the initial attempt
- Rather than dividing the const interval by 2 everywhere and passing
  the retry duration in, just half the constant and set the initial
  duration to it in the retry handler

Finally, include the address of the peer in the error message when a new
outbound peer can't be created.
2015-11-24 16:25:32 -06:00
Dave Collins
1217e00d39 peer: Unexport the mru inventory map.
This unexports the mruInventoryMap type since it is only needed within
the peer package.
2015-11-24 09:41:35 -06:00
Dave Collins
a4aa131dd5 mining: Refactor policy into its own struct.
This introduces the concept of a mining policy struct which is used to
control block template generation instead of directly accessing the
config struct.  This is a step toward decoupling the mining code from
the internals of btcd.  Ultimately the intent is to create a separate
mining package.
2015-11-23 22:02:14 -06:00
Dave Collins
2799ddf538 mining: Remove comment and change fee calc to int.
Now that the memory pool minimum fee calculation code is also
calculating a more precise value instead of rounding up to the nearest
kilobyte boundary, the comment in NewBlockTemplate regarding this
behavior is no longer accurate.  Thus, this removes the comment.

Also, while here, change the calculation to use an int64 instead of
float since it matches the precision of the new calculation code used by
the memory pool and can also avoid the need for the slower floating
point math.
2015-11-23 21:52:39 -06:00
Michail Kargakis
09874f1e91 Fix dropaddrindex flag usage message 2015-11-23 22:20:02 +01:00
Dave Collins
d0f0a2ac02 server: Improve handling of disconnected peers.
When the peer code was refactored, the lists of peers were converted to
maps however the code which runs when a peer disconnects still iterates
them like a slice.  This is no longer necessary since they are maps
which means the peer can simply be looked up by its ID.

Also, the old code was comparing the map entry and the peer being
removed by their pointers which could lead to potentially not properly
finding the peer.  This issue is also resolved by this change since it
looks up the peer by its ID.
2015-11-23 11:03:42 -06:00
Dave Collins
6c00d07910 peer: Combine stats struct into peer struct.
This defines the peer stat fields directly in the Peer struct instead of
defining them in a separate struct and embedding them.  It is slightly
more efficient this way and there is really no reason to have them
defined separately anyways since they are not passed around or otherwise
used independently.
2015-11-23 09:25:53 -06:00
Rune T. Aune
b81555beea Fix "Established connection" log message.
Don't log "Established connection" message on new when
DisableConnectOnNew is set and no connection was actually established.
Do log that same message when Connect() successfully connects.
2015-11-21 14:41:06 -06:00
Dave Collins
cea5d3c1cc Prepare for release 0.12.0. 2015-11-20 19:33:17 -06:00
Dave Collins
23df5ed2fc Add 0.12.0 deps to deps.txt. 2015-11-20 17:35:07 -06:00
Dave Collins
ee8601ecac Add checkpoint at block height 382320. 2015-11-20 13:06:42 -06:00
danda
975536f20f add filteraddrs param to searchrawtransactions 2015-11-16 22:17:19 -08:00
Javed Khan
0c3cf63773 Use new connections for peers in TestOutboundPeer
Fixes a rare hang during peer tests due to the same connection being
used for different outbound peers. Also fixed passing a chan to
QueueMessage(..) which was not being waited on, so was not being used.
2015-11-16 23:01:51 +05:30
danda
c7eaee6020 adds filteraddrs param to searchrawtransactions API 2015-11-15 15:30:13 -08:00
Rune T. Aune
b691a222d5 Add signature hash calculation tests from Bitcoin Core.
500 tests with various transactions and scripts, verifying that
calcSignatureHash generates the expected hash in each case.

This requires changing SigHashType to uint32; that won't affect the
standard use-cases, but will make calcSignatureHash behave more like the
Core counterpart for non-standard SigHashType settings, like those in
some of these tests.
2015-11-15 16:39:00 -05:00
David Hill
365b1bd156 mempool: convert orphansByPrev from a list to a map.
This is more efficient and prevents duplicate entries which can
lead to no-longer-orphans being attempted be added to the mempool
multiple times.

Bug found by me, debugged with @davecgh, and patch from @davecgh
2015-11-13 08:38:17 -05:00
Dave Collins
58e2762158 mempool: Move checkTransactionStandard to policy.
This refactors the checkTransactionStandard function, along with related
constants, from the mempool to the policy.go file since it is strictly
related to policy.

In addition, it adds tests for the function which cover all code paths.
2015-11-11 13:34:32 -06:00
David Hill
d765c73a41 mempool: Remove returned error from ProcessOrphans
ProcessTransaction could have accepted a new transaction into mempool
but could have returned a reject message if a no-longer-orphan
transaction failed to be accepted.  This would also skip any
additional no-longer-orphans, keeping them in the orphan pool.

Instead of returning an error incorrectly, log the error and skip
the no-longer-orphan transaction.  This allows the rest of the
no-longer-orphans to be processed as well.
2015-11-10 18:34:51 -05:00
David Hill
5016675d40 Move comment to where it belongs. 2015-11-10 13:20:29 -05:00
Javed Khan
21d11e2809 Fix failing test case because of wrong TimeOffset 2015-11-10 05:15:55 +05:30
David Hill
cb71f278ec chaincfg: Move DNS Seeds to chaincfg.
This allows API users access to the DNS Seeds for use with SPV
clients, seeders, etc.
2015-11-09 17:21:16 -05:00
Dave Collins
aa0efa1f3e server: Allow IPv6 addresses with zone id.
This modifies the IP parsing code to work with IPv6 zone ids.  This is
needed since the net.ParseIP function does not allow zone ids even
though net.Listen does.
2015-11-09 10:39:30 -06:00
Josh Rickmar
23bcb367b2 blockchain: Fix bogus error message.
In the presence of overflow, the actual output sum is still not
negative and should not be reported as so.
2015-11-09 11:33:37 -05:00
Justus Ranvier
756f58b581 add installation instructions for Gentoo 2015-11-08 14:55:08 -06:00
Dario Nieuwenhuis
912a8d8301 Fix mempool transactions disappearing if a parent tx is confirmed. 2015-11-06 11:42:42 +01:00
David Hill
c56072017d Add optional locktime parameter to CreateRawTransaction APIs. 2015-10-30 19:34:34 -04:00
David Hill
4b7206b54f btcjson: Add optional locktime to createrawtransaction
rpcserver:
If the locktime is given, the transaction inputs will be set to a
non-max value, activating the locktime.  The locktime for the
new transaction will be set to the given value.

This mimics Bitcoin Core commit 212bcca92089f406d9313dbe6d0e1d25143d61ff
2015-10-30 17:16:50 -04:00
Ben Echols
489ba8d31d Update minimum fee calculations to match Core.
Calculate rate*fee first before dividing by 1000.
Add more unit tests around the fee calculations.
2015-10-30 13:41:25 -06:00
David Hill
7811770d31 Implement BIP0065 changeover logic for v4 blocks.
This commit implements the changeover logic for version 4 blocks as
described by BIP0065.
2015-10-28 13:28:50 -04:00
David Hill
3d6afcffe7 Move non-mempool specific functions to new file.
No functional change. Add tests.
2015-10-27 10:05:06 -04:00
Dave Collins
5c50db5357 peer: Don't stall on pongs. 2015-10-26 08:59:01 -05:00
Dave Collins
4a397d51d4 peer: Don't log write errs due to disconnect.
This commit modifies the peer logging so that it will not log an error
when it's due to the remote peer disconnecting or the peer being
forcibly disconnected or shutdown.
2015-10-23 14:55:43 -05:00
Dave Collins
3942a116e4 docs: Make various README.md files consistent.
First, it removes the documentation section from all the README.md files
and instead puts a web-based godoc badge and link at the top with the
other badges.  This is being done since the local godoc tool no longer
ships with Go by default, so the instructions no longer work without
first installing godoc. Due to this, pretty much everyone uses the
web-based godoc these days anyways.  Anyone who has manually installed
godoc won't need instructions.

Second, it makes sure the ISC license badge is at the top with the other
badges and removes the textual reference in the overview section.

Finally, it's modifies the Installation section to Installation and
Updating and adds a '-u' to the 'go get' command since it works for both
and thus is simpler.
2015-10-23 14:51:36 -05:00
Dave Collins
aa03d68e1e peer: Correct a few typos in documentation. 2015-10-23 14:47:18 -05:00
Dave Collins
cbbe3a8bbe peer: Implement stall detection.
This commit implements stall detection logic at the peer level to detect
and disconnect peers that are either not following the protocol in
regards to expected response messages or have otherwise stalled.  This
is accomplished by setting deadlines for each message type which expects
a response and periodically checking them while properly taking into
account processing time.

There are an increasing number of nodes on the network which claim to be
full nodes, but don't actually properly implement the entire p2p
protocol even though they implement it enough to cause properly
implemented nodes to make data requests to which they never respond.

Since btcd currently only syncs new blocks via single sync peer and,
prior to this commit only had very basic stall detection, this could
lead to a situation where the block download became stalled indefinitely
due to one of these misbehaving peers.  This commit fixes that issue
since the stalled peer will now be detected and disconnected which leads
to a new sync peer being selected.

This logic will also fit nicely with the future multi-peer sync model
which is on the roadmap and for which infrastructure work is underway.

Fixes #486 and fixes #229.
2015-10-23 10:27:00 -05:00
Dave Collins
f1bd2f8d6e peer: Ping on interval instead of delayed timer.
This commit modifies the ping logic in the peer to ping on an interval
regardless of what other messages are being sent versus the previous
method of delaying the ping each time a message that is expected to
receive data is sent.

This helps improve the ping statistics and simplifies its logic.
2015-10-23 10:22:22 -05:00
Dave Collins
250228c32f peer: Improve documentation.
This fleshes out the doc.go documentation which is shown on godoc, the
README.md shown on github, and improves a couple of comments for the
fields in the Config struct.
2015-10-23 01:17:12 -05:00
Javed Khan
00bddf7540 peer: Refactor peer code into its own package.
This commit introduces package peer which contains peer related features
refactored from peer.go.

The following is an overview of the features the package provides:

- Provides a basic concurrent safe bitcoin peer for handling bitcoin
  communications via the peer-to-peer protocol
- Full duplex reading and writing of bitcoin protocol messages
- Automatic handling of the initial handshake process including protocol
  version negotiation
- Automatic periodic keep-alive pinging and pong responses
- Asynchronous message queueing of outbound messages with optional
  channel for notification when the message is actually sent
- Inventory message batching and send trickling with known inventory
  detection and avoidance
- Ability to wait for shutdown/disconnect
- Flexible peer configuration
  - Caller is responsible for creating outgoing connections and listening
    for incoming connections so they have flexibility to establish
    connections as they see fit (proxies, etc.)
  - User agent name and version
  - Bitcoin network
  - Service support signalling (full nodes, bloom filters, etc.)
  - Maximum supported protocol version
  - Ability to register callbacks for handling bitcoin protocol messages
- Proper handling of bloom filter related commands when the caller does
  not specify the related flag to signal support
  - Disconnects the peer when the protocol version is high enough
  - Does not invoke the related callbacks for older protocol versions
- Snapshottable peer statistics such as the total number of bytes read
  and written, the remote address, user agent, and negotiated protocol
  version
- Helper functions for pushing addresses, getblocks, getheaders, and
  reject messages
  - These could all be sent manually via the standard message output
    function, but the helpers provide additional nice functionality such
    as duplicate filtering and address randomization
- Full documentation with example usage
- Test coverage

In addition to the addition of the new package, btcd has been refactored
to make use of the new package by extending the basic peer it provides to
work with the blockmanager and server to act as a full node.  The
following is a broad overview of the changes to integrate the package:

- The server is responsible for all connection management including
  persistent peers and banning
- Callbacks for all messages that are required to implement a full node
  are registered
- Logic necessary to serve data and behave as a full node is now in the
  callback registered with the peer

Finally, the following peer-related things have been improved as a part
of this refactor:

- Don't log or send reject message due to peer disconnects
- Remove trace logs that aren't particularly helpful
- Finish an old TODO to switch the queue WaitGroup over to a channel
- Improve various comments and fix some code consistency cases
- Improve a few logging bits
- Implement a most-recently-used nonce tracking for detecting self
  connections and generate a unique nonce for each peer
2015-10-23 06:17:29 +05:30
David Hill
2e6e896aa6 txscript: Sync Bitcoin Core tests. 2015-10-22 16:10:29 -04:00
David Hill
3fa416a7ef txscript: fix isMultiSig bug.
isMultiSig was not verifying the number of pubkeys specified matched
the number of pubkeys provided.  This caused certain non-standard
scripts to be considered multisig scripts.

However, the script still would have failed during execution.

NOTE: This only affects whether or not the script is considered
standard and does NOT affect consensus.

Also, add a test for this check.
2015-10-22 15:55:34 -04:00
David Hill
a56db22e9b config: New option --minrelaytxfee
--minrelaytxfee allows the user to specify the minimum transaction
fee in BTC/kB in which the fee is considered a non-zero fee.
2015-10-20 12:41:12 -04:00
Dave Collins
5a9bac9668 Correct a few style related issues found by golint.
Also, update TravisCI goclean script to remove the special casing which
ignored 'Id' from the lint output since that exception is no longer
needed.  It was previously required due to the old version of btcjson,
but that is no longer in the repo.
2015-10-20 10:34:14 -05:00
Josh Rickmar
07406791c9 rpcserver: Copy btcwallet fix for verifymessage. 2015-10-16 14:25:07 -04:00
Dave Collins
80fa803875 wire: Export var length string serialization funcs.
This commit exports the ReadVarString and WriteVarString functions so
they are available for callers to use.

A variable length string is encoded as a variable length integer
containing the length of the string followed by the bytes that represent
the string itself.
2015-10-16 11:24:45 -05:00
David Hill
4c3ad4987b txscript: Implement CheckLockTimeVerify (BIP0065)
See https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki for
more information.

This commit mimics Bitcoin Core commit bc60b2b4b401f0adff5b8b9678903ff8feb5867b
and includes additional tests from Bitcoin Core commit
cb54d17355864fa08826d6511a0d7692b21ef2c9
2015-10-14 13:19:49 -04:00
David Hill
0f57a41ed8 txscript: Add ScriptVerifyLowS to the standard flags
We've already been generating lowS sigs for quite a while.  This removes
the malleability vector.

This mimics Bitcoin Core commit 49dd5c629df0a08cf3b1ea8085c03312d1a81696
2015-10-09 15:30:12 -04:00
David Hill
f862536929 btcjson: Add errors to InfoChainResult
The getinfo RPC will now include the errors attribute in the
result.
2015-10-09 14:32:53 -04:00
Olaoluwa Osuntokun
0029905d43 Integrate a valid ECDSA signature cache into btcd
Introduce an ECDSA signature verification into btcd in order to
mitigate a certain DoS attack and as a performance optimization.

The benefits of SigCache are two fold. Firstly, usage of SigCache
mitigates a DoS attack wherein an attacker causes a victim's client to
hang due to worst-case behavior triggered while processing attacker
crafted invalid transactions. A detailed description of the mitigated
DoS attack can be found here: https://bitslog.wordpress.com/2013/01/23/fixed-bitcoin-vulnerability-explanation-why-the-signature-cache-is-a-dos-protection/
Secondly, usage of the SigCache introduces a signature verification
optimization which speeds up the validation of transactions within a
block, if they've already been seen and verified within the mempool.

The server itself manages the sigCache instance. The blockManager and
txMempool respectively now receive pointers to the created sigCache
instance. All read (sig triplet existence) operations on the sigCache
will not block unless a separate goroutine is adding an entry (writing)
to the sigCache. GetBlockTemplate generation now also utilizes the
sigCache in order to avoid unnecessarily double checking signatures
when generating a template after previously accepting a txn to the
mempool. Consequently, the CPU miner now also employs the same
optimization.

The maximum number of entries for the sigCache has been introduced as a
config parameter in order to allow users to configure the amount of
memory consumed by this new additional caching.
2015-10-08 17:31:42 -07:00
Dario Nieuwenhuis
0190c349aa Add reverse order option to searchrawtransactions rpc 2015-10-08 16:31:39 +02:00
Dave Collins
29a2544887 Update SearchRawTransaction calls for latest API.
This commit modifies the SearchRawTransactions and
SearchRawTransactionsVerbose functions to work properly with the latest
searchrawtransactions API in btcd.

In particular, this involves adding a new parameter for the reverse
option which specifies whether or not to include the results in reverse
order.
2015-10-07 12:12:53 -05:00
David Hill
ce22159fb2 txscript: Change makeScriptNum to take a length argument
While current existing numeric opcodes are limited to 4 bytes, new
opcodes may need different limits.

This mimics Bitcoin Core commit 99088d60d8a7747c6d1a7fd5d8cd388be1b3e138
2015-10-05 19:48:55 -04:00
Dave Collins
e4c053e504 rpcserver: Optimize JSON raw tx input list create.
This commit optimizes the createVinList function which is used to
generate the JSON list of transaction inputs.  It also makes it more
consistent with the createVinListPrevOut function.

In particular, it entails the following changes:
- Only do a single coinbase check and return right away instead of
  checking multiple times inside the loop over the inputs
- Use a pointer for populating the details of each entry to avoid
  multiple unnecessary array lookups and bounds checks
- Group all fields that populate the entry for better readability
2015-09-30 20:52:42 -05:00
David Hill
c9ee3d9c5e wire: Implement SFNodeBloom (BIP0111).
SFNodeBloom is a new service flag that a node is required to use to
indicate that it supports bloom filtering.  This includes a protocol
version bump to 70011 and a wire version bump to 0.3.0.

btcd:
The SFNodeBloom flag is set by default.  A new configuration option
--nopeerbloomfilters has been added to to disable bloom filtering.

Any node advertising a version greater than or equal to 70011 that
attempts to use bloom filtering will be disconnected if bloom
filtering is disabled.

This mimics Bitcoin Core commit afb0ccaf9c9e4e8fac7db3564c4e19c9218c6b03
2015-09-28 16:25:44 -04:00
Dave Collins
064cc8e7c3 txscript: Optimize DisasmString function.
This commit modifies the DisasmString function to use a bytes buffer for
constructing the disassembled string instead of naive string
concatenation.  This makes a huge difference when disassembling scripts
with large numbers of opcodes.
2015-09-28 12:57:53 -05:00
Dave Collins
79aac01b02 wire: Reject non-canonically encoded varints.
The Bitcoin wire protocol includes several fields with their lengths
encoded according to a variable length integer encoding scheme that does
not enforce a unique encoding for all numbers.

This can lead to a situation where deserializing and re-serializing the
same data can result in different bytes.  There are no currently known
issues due to this, but it is safer to reject such subtle differences as
they could potentially lead to exploits.

Consequently, this commit modifies the varint decoding function to error
when the value is not canonically encoded which effectively means that
all messages with varints that are not canonically encoded will now be
rejected.  This will not cause issues with old client versions in
regards to blocks and transactions since the data is deserialized into
memory and then reserialized before being relayed thereby effectively
erasing any non-canonical encodings.

Also, new tests have been added to ensure non-canonical encodings are
properly rejected and exercise the new code, and the default user agent
version for wire has been bumped to version 0.2.1 to differentiate the
new behavior.

The equivalent logic was implemented in Bitcoin Core by PR 2884.
2015-09-26 16:22:31 -05:00
Mawuli Adzoe
03d423cebf Review and fix docs. 2015-09-24 13:12:04 +00:00
Josh Rickmar
5983c9b98e Allow the session RPC for limited (read-only) clients. 2015-09-23 16:53:07 -04:00
Mawueli Kofi Adzoe
d06c232f04 Review and fix. Mostly typos. 2015-09-20 11:42:49 +00:00
Josh Rickmar
3c9d18d641 Add a websocket session RPC. 2015-09-17 12:18:15 -04:00
Josh Rickmar
6ac46f9e5f Remove mixed newlines in documentation. 2015-09-17 12:18:15 -04:00
Dave Collins
e6d5c163d5 Update TravisCI to test against golang 1.5.1.
Also, modify the goclean.sh script to quote the test command arguments
and update the vet test to exclude recent false positives.
2015-09-17 11:01:34 -05:00
Josh Rickmar
6f3bc8e57c Add support for the session extension RPC. 2015-09-17 11:32:17 -04:00
Dario Nieuwenhuis
1806557d14 Fix skip not being applied to mempool txns in searchrawtransactions 2015-09-08 15:17:43 +02:00
Dave Collins
0f9fc42a06 Output error to stderr if the limits can't be set. 2015-09-02 17:13:01 -05:00
David Hill
4e34a462eb Add new seed seed.bitcoin.jonasschnelli.ch.
Drop seed seeds.bitcoin.open-nodes.org.
2015-08-31 09:45:54 -04:00
Dave Collins
83bcfcb2ca Improve mru inventory map and test cov to 100%.
This commit improves the most-recently used inventory map human readable
string to only show the inventory vectors. and adds tests for the entire
structure to bring its coverage to 100%.

In addition, removes the type assertion check in the Add function since
the internal inventory list is only managed by the type itself and the
tests would now catch any mistakes in the type of entries in the list.
2015-08-29 15:43:42 -05:00
Dave Collins
34db203930 Update SearchRawTransaction calls for latest API.
This commit modifies the SearchRawTransactions and
SearchRawTransactionsVerbose functions to work properly with the latest
searchrawtransactions API in btcd.

In particular, this involves changing the return value from
[]*btcjson.TxRawResult to []*btcjson.SearchRawTransactionResult and
adding the additional optional parameter which specifies whether or not
to include information about the previous outputs.
2015-08-26 12:49:14 -05:00
danda
43774fe6bb adds optional prevOut section to vin for searchrawtransactions api. See https://github.com/btcsuite/btcd/issues/485 2015-08-23 09:58:03 -07:00
David Hill
2441120b55 Recognize the BIP0064 service bit.
This does not add BIP0064 (getutxos/utxos) support to btcd.
2015-08-22 09:11:05 -04:00
Dario Nieuwenhuis
9c039f5fe4 Fix longpoll getblocktemplate not getting notified if a block is pushed via submitblock 2015-08-21 16:46:06 +02:00
Dave Collins
0280fa0264 Convert block heights to int32.
This commit converts all block height references to int32 instead of
int64.  The current target block production rate is 10 mins per block
which means it will take roughly 40,800 years to reach the maximum
height an int32 affords.  Even if the target rate were lowered to one
block per minute, it would still take roughly another 4,080 years to
reach the maximum.

In the mean time, there is no reason to use a larger type which results
in higher memory and disk space usage.  However, for now, in order to
avoid having to reserialize a bunch of database information, the heights
are still serialized to the database as 8-byte uint64s.

This is being mainly being done in preparation for further upcoming
infrastructure changes which will use the smaller and more efficient
4-byte serialization in the database as well.
2015-08-11 11:13:17 -05:00
Jonathan Gillham
27f7f82355 txscript: Make error strings idiomatic. 2015-08-09 14:06:36 +01:00
David Hill
3331d6098b txscript: New function IsUnspendable
IsUnspendable takes a public key script and returns whether it is
spendable.

Additionally, hook this into the mempool isDust function, since
unspendable outputs can't be spent.

This mimics Bitcoin Core commit 0aad1f13b2430165062bf9436036c1222a8724da
2015-08-03 10:10:23 -04:00
Jonathan Gillham
b448a2b6bc Make PubKey variable names consistent. 2015-08-02 22:21:27 +01:00
Dave Collins
88b15e74f0 docs: Add info describing model commit messages. 2015-07-30 13:14:42 -05:00
Daniel Krawisz
2dc8687728 Fix incorrect ip connection attempt logic.
The comment says "only allow recent nodes (10mins) after we failed 30 times",
but the server actually did the opposite and allowed only recent nodes before
30 failed connection attempts. This corrects the server's behavior.
2015-07-30 08:30:31 -05:00
Dave Collins
506fc9fb94 rpcserver: Allow tx result creation without block.
This commit modifies the createTxRawResult code path along with callers
to work with block headers as opposed to btcutil.Blocks.  This in turn
allows the code in handleGetRawTransaction and
handleSearchRawTransactions to perform a much cheaper block header load
as opposed to a full block load.

While here, also very slightly optimize the createVinList function to
avoid creating a util.Tx wrapper and to take advantage of the
btcutil.Amount type added after the function was originally written
2015-07-28 18:44:30 -05:00
Dave Collins
f891391f7c peer: Optimize merkle block handling.
This commit updates the merkle block handling to for the latest changes
to the btcutil API and optimizes it along the way.

Previously, the code was inefficiently reloading the transactions for
the matched hashes from the database instead of simply pulling them from
the full block that was used to create the merkle block.
2015-07-28 10:38:08 -05:00
Jonathan Gillham
f8167ab36f txscript: Remove unneeded signature hash copies
These copies were likely left over from when MsgTx had no deep copy
functionality.
2015-07-28 11:31:43 +01:00
Dave Collins
51fca61707 wire: Remove duplicate tx error path tests. 2015-07-26 13:05:09 -05:00
drazisil
b1606447b5 added getwalletinfo cmd 2015-07-24 13:56:24 -04:00
Dave Collins
5ab891177b docs: Remove outdated btcws link from README.md. 2015-07-24 09:24:32 -05:00
David Hill
a6c79c7a91 Implement getblockheader RPC.
This mimics Bitcoin Core commit 076badb60f33f0c32b035de220ca14c52a423a2a
2015-07-23 15:22:17 -04:00
David Hill
eb4ad09598 Implmement BlockHeader BtcEncode/BtcDecode.
At the current time, there is no difference between the wire encoding
at protocol version 0 and the stable long-term storage format.  These
methods are simply for consistency with the other types.
2015-07-22 11:34:18 -04:00
Dave Collins
3d89b56b27 wire: Update tests to force error in tx lock time.
This commit updates the wire tests for transactions which force
serialization and deserialization errors to force an error in the the
transaction lock time path.

This brings the wire test coverage back up to 100%.
2015-07-22 09:26:52 -05:00
Josh Rickmar
6f1272e767 btcjson: Add spendable to listunspent result. 2015-07-21 13:45:42 -04:00
David Hill
8a4a875e9d Ignore getheaders requests if not synced.
This mimics Bitcoin Core commit a1ba0778dd3c784046dea334e5d39f37eca264f7
2015-07-20 14:16:55 -04:00
Dave Collins
1ddf8e8edf Correct reconnect handling for persistent peers.
This commit correctly replaces persistent peers that are being retried in
the list of persistent peers so it will continue to be retried as
intended.

Also, limit the maximum retry interval for persistent peers to 5 minutes.

Fixes #463.
2015-07-20 12:35:44 -05:00
Dave Collins
7246b9b933 docs: Correct JSON-RPC API overview return links.
This commit corrects the JSON-RPC API doc return to overview links for
the Websocket Extension Method Details section.
2015-07-20 12:32:40 -05:00
Bruno
4335ce828c switch maxDataCarrierSize to public const 2015-07-20 14:26:05 +08:00
Jonathan Gillham
8fcea82a56 Fixed erroneous txscript.KeyClosure documentation. 2015-07-19 16:15:29 +01:00
David Hill
e13b4febec Document limitfreerelay and norelaypriority options. 2015-07-13 12:52:17 -04:00
David Hill
9ffd96bf51 Revert "Move IsFinalizedTransaction to txscript."
This reverts commit 17da2ba7fa.

This was done prematurely.  This will be revisited when a code
restructure is more urgent.
2015-06-29 11:12:35 -04:00
David Hill
17da2ba7fa Move IsFinalizedTransaction to txscript.
This change moves IsFinalizedTransaction to txscript and also changes
the first argument to take a wire.MsgTx instead of btcutil.Tx.  This
is needed for an upcoming diff in which txscript will require
IsFinalizedTransaction and we do not want to import the btcd/blockchain.
2015-06-28 09:43:14 -04:00
David Hill
bb8333a739 Use NewMsgInvSizeHint when we know the size.
When trickling inv's to peers, allocate for the number of inv's
in the queue instead of the default 1000.  This should save on
memory.
2015-06-27 11:07:36 -04:00
David Hill
527f585463 txscript: Move lockTimeThreshold to txscript
Move lockTimeThreshold to txscript and export it.  This is a
consensus value which txscript will need in an upcoming diff.
2015-06-26 10:55:22 -04:00
David Hill
7cfa843832 Support getmempoolinfo. 2015-06-24 20:34:56 -04:00
John C. Vernaleo
7cb04ae6db Update example code.
Example code used old api for
OnBlockConnected and OnBlockDisconnected
2015-06-18 16:21:03 -04:00
Josh Rickmar
09ce6f94d3 Add timestamps to block(dis)connected notifications. 2015-06-18 13:02:13 -04:00
Josh Rickmar
1831071905 Parse/notify times from block(dis)connected notifications. 2015-06-18 12:14:42 -04:00
Josh Rickmar
2ceb6418e7 Notify mined transactions before connected block.
This allows clients watching for both to know when all mined
transaction notifications for the block have been received.
Otherwise, clients would be aware that there is a new block, see the
exact same block hash/height in the next tx notifications, but never
know when all txs from the block have been received and processed.

This fixes a synchronization issue in btcwallet where the wallet would
mark itself synced to some block height before any of those blocks'
transactions were processed.  If the RPC connection is lost between
sending the blockconnected notification and receiving the last
transaction notification, the wallet would not know of this and
continue with missing transactions.
2015-06-17 22:37:55 -04:00
Josh Rickmar
77baeb8d79 Fix data race.
This synchronizes access to the (*Client).disconnect channel, which
may change during reconnects.

Fixes #54.
2015-06-15 11:38:25 -04:00
David Hill
a1bd15e7c2 Fix --onion.
The configured onion proxy was not being used due to checking if the
passed string had suffix '.onion', which never matched because the
port number is part of the string.
2015-06-10 09:56:25 -04:00
Josh Rickmar
ee3a86c44b Make listsinceblock block hash optional.
Before, calling ListSinceBlock(Async) would create an empty string
from the nil block hash.
2015-06-01 14:41:23 -04:00
Dave Collins
3aac3bda44 Update README.md install section.
This commit adds an additional step to the README.md install section to
run the go version command and check the version so people that are
installing it for the first time and ensure they are running a high
enough version and have GOROOT and GOPATH set correctly.
2015-05-29 15:34:16 -05:00
Dave Collins
d12b3a144c Prepare for release 0.11.1. 2015-05-27 11:06:52 -05:00
Dave Collins
58fa972954 Add 0.11.1 deps to deps.txt. 2015-05-27 10:50:12 -05:00
David Hill
6d15b04128 Add gettxoutproof and verifytxoutproof JSON-RPC infrastructure.
From Bitcoin Core commit 59ed61b3895b022f61970ea7aac0c20e8ba38886
2015-05-27 10:01:22 -04:00
Dave Collins
9350b939bc mempool: Correct cmd field for rejected txns.
This commit corrects an issue where the reject message sent in response
to rejected transactions had the Cmd field set to "block" instead of
"tx".

Fixes #436.
2015-05-26 13:59:01 -05:00
Ishbir Singh
d9556df292 Mitigate timing attacks while using btcec.Decrypt. 2015-05-25 16:42:43 +05:30
David Hill
9d6d0e4006 Keep track of peers with maps instead of lists. 2015-05-21 11:10:00 -04:00
Ishbir Singh
58f29ad939 Added ECDH and encryption/decryption support 2015-05-19 23:48:33 +05:30
David Hill
007bee5ec8 Add new option --torisolation
Tor stream isolation randomizes proxy user credentials resulting in
Tor creating a new circuit for each connection.  This makes it more
difficult to correlate connections.

Idea from Wladimir J. van der Laan via Bitcoin Core.
2015-05-13 18:30:48 -04:00
David Hill
5f8dbab47a Add new option -maxorphantx
The option -maxorphantx allows the user to specify the number of
orphan transactions to keep in memory.

Also, lower the default max orphan count from 10000 to 1000.
2015-05-12 17:22:13 -04:00
Dave Collins
19eae8d8a1 blockchain: Split block and header validation.
This commit refactors the consensus rule checks for block headers and
blocks in the blockchain package into separate functions.  These changes
contain no modifications to consensus rules and the code still passes all
block consensus tests.  It is only a refactoring.

This is being done to help pave the way toward supporting concurrent
downloads.  While the package already supports headers-first mode up
through the latest checkpoint through the use of the BFFastAdd flag and
hard-coded checkpoints, it currently only works when downloading from a
single peer.  In order to support concurrent downloads from multiple
peers, the ability for the caller to do things such as independently
checking a block header (both context-free and full-context checks) will
be needed.

There are several more changes that will be necessary to support
concurrent downloads as well, such as making the package concurrent safe,
modifying it to make use of the new database API, etc.  Those changes are
planned for future commits.
2015-05-12 16:04:42 -05:00
David Hill
aa34ec0925 Update docs for IRC move to freenode. 2015-05-12 14:01:58 -04:00
David Hill
7e50b843d8 Rename hex to scriptSig in SignRawTransactionError.
Fixes a 'read and type the wrong thing'.
2015-05-07 13:30:21 -04:00
David Hill
31a959d921 Run gofmt on btcjson/ 2015-05-07 12:42:16 -04:00
David Hill
92c241c64b Fix typo in previous: Result->Error 2015-05-07 12:34:50 -04:00
David Hill
1555124c85 Add additional fields to the SignRawTransaction RPC result.
This mimics Bitcoin Core commit 8ac2a4e1788426329b842eea7121b8eac7875c76
2015-05-07 09:27:50 -04:00
Dave Collins
a40058cd0e rpcserver: Omit empty getrawtransaction->confirmations.
This commit omits the confirmations field from the getrawtransactions
RPC result when it's 0.

Closes #420.
2015-05-06 13:33:42 -05:00
Josh Rickmar
788c316879 Fix listtransactions/gettransaction result structs.
The following changes were made to ListTransactionsResult (which
models the long result format used by listtransactions,
listsinceblock, etc.):

  - Fee made optional (float64 -> *float64 + omitempty)
  - BlockIndex made optional (int64 + omitempty -> *int64 + omitempty)
  - InvolvesWatchOnly added (bool + omitempty)
  - Vout added (uint32)

The following changes were made to GetTransactionDetailsResult (which
models the short result format of listtransactions):

  - InvolvesWatchOnly added (bool + omitempty)
  - Fee added (*float64 + omitempty)
  - Vout added (uint32)

The combination of pointer types and the omitempty struct tag allow
excluding the field from the JSON object, or including it with the
zero value.  This is useful in particular for the fee fields, which
should be included whenever the category is "send" even if the fee is
zero.  Other optional fields which are only added to the result object
with non-zero values (such as includeswatchonly) can be reduced to
simply an omitempty tag without the pointer type.
2015-05-06 14:12:10 -04:00
Federico Bond
717a9f25b5 Fix documentation example 2015-05-06 13:46:04 -03:00
Alex Akselrod
9874580e5b Add Generate support in mining.go.
- Create FutureGenerateResult type with Receive() method

- Create GenerateAsync method for Client which returns a
  FutureGenerateResult

- Create Generate method for Client which calls GenerateAsync
  and then calls Receive() on the returned FutureGenerateResult
2015-05-05 16:52:53 -04:00
Dave Collins
b046f36c72 Relicense to the btcsuite developers. 2015-05-01 12:03:51 -05:00
Dave Collins
dd8dc87577 Run goimports -w . 2015-05-01 10:54:58 -05:00
Dave Collins
ce9e8aa264 Update btcjson path import paths to new location. 2015-05-01 10:42:52 -05:00
Albert Puigsech Galicia
b1d5c1b9f6 Add account param on GetNewAddress 2015-04-27 00:44:16 +02:00
Josh Rickmar
13b872259d Remove dependence on btcjson.NewOutPointFromWire. 2015-04-23 14:55:35 -04:00
Alex Akselrod
05f1d6c89a Change parseTxAcceptedNtfnParams to accept a float64 returned by btcd. 2015-04-17 14:20:39 -04:00
Dave Collins
34c87a7340 Add golint to TravisCI builds. 2015-04-05 23:00:36 -05:00
Dave Collins
774eb787a8 Rename HttpPostMode conn param to HTTPPostMode.
This rename is to make golint happy.
2015-04-05 23:00:33 -05:00
Dave Collins
c4bc5220bc Update for recent GetTxOut argument change. 2015-04-05 23:00:29 -05:00
Dave Collins
3daafd5617 Add SearchRawTransactions support. 2015-04-05 23:00:22 -05:00
Javed Khan
04a3ed28f5 Added ImportAddress and ImportPubKey support. 2015-04-05 22:56:54 -05:00
Dave Collins
9ca93b30ad Update to make use of latest version of btcjson.
This commit contains several changes needed to update the client to use
the latest version of btcjson.  In addition, it contains a couple of other
minor changes along the way.

While the underlying changes are quite large, the public API of this
package is still the same, so caller should generally not have to update
their code due to that.  However, the underlying btcjson package API has
changed significantly.  Since this package hides the vast majority of that
from callers, it should not afffect them very much.  However, one area in
particular to watch out for is that the old btcjson.Error is now
btcjson.RPCError, so any callers doing any type assertions there will need
to update.

The following is a summary of the changes:

- The underlying btcjson significantly changed how commands work, so the
  internals of this package have been reworked to be based off of requests
  instead of the now non-existant btcjson.Cmd interface
- Update all call sites of btcjson.New<Foo>Cmd since they can no longer
  error or take varargs
- The ids for each request are part of the request now instead of the
  command to match the new btcjson model and are strict uint64s so type
  assertions are no longer needed (slightly improved efficiency)
- Remove the old temporary workaround for the getbalance command with an
  account of "*" since btcwallet has since been fixed
- Change all instances of JSONToAmount to btcutil.NewAmount since that
  function was removed in favor of btcutil.Amount
- Change all btcws invocations to btcjson since they have been combined
2015-04-05 22:56:39 -05:00
Dave Collins
d45f4c47a2 Update btcws path import paths to new location. 2015-02-19 13:08:29 -06:00
Dave Collins
23ab2bf329 Update btcjson path import paths to new location. 2015-02-19 12:00:51 -06:00
Dave Collins
7d4e1e17f0 Update btcnet path import paths to new location. 2015-02-05 23:39:15 -06:00
Josh Rickmar
87ef953a63 Update Go versions for Travis. 2015-02-05 17:24:42 -05:00
Dave Collins
ed821410cb Update btcwire path import paths to new location. 2015-02-05 15:50:03 -06:00
Javed Khan
20ff0689d8 Added async version for CreateNewAccount, RenameAccount 2015-01-23 17:32:22 +05:30
Dave Collins
bc36ac6d52 Update btcd import paths to new location. 2015-01-17 01:18:22 -06:00
Dave Collins
04f541082a Update btcwallet import paths to new location. 2015-01-17 00:30:49 -06:00
Dave Collins
64231158a0 Update btcjson import paths to new location. 2015-01-16 23:56:08 -06:00
Dave Collins
9e86f74c50 Update btcws import paths to new location. 2015-01-16 23:38:11 -06:00
Dave Collins
fccfbbb347 Update btcnet import paths to new location. 2015-01-16 17:36:51 -06:00
Dave Collins
c1df139e32 Update btcwire import paths to new location. 2015-01-16 15:28:53 -06:00
Javed Khan
132207cb3f Added CreateNewAccount, RenameAccount 2015-01-17 00:45:41 +05:30
Dave Collins
a02ba2b4b1 Update btclog import paths to new location. 2015-01-16 13:08:45 -06:00
Dave Collins
231854f24b Update go-socks import paths to new location. 2015-01-16 01:29:50 -06:00
Dave Collins
62b2ec081a Update btcrpcclient import paths to new location. 2015-01-15 21:21:40 -06:00
Dave Collins
d3d5618ef9 Update TravisCI to goclean script.
Also update to use the new container-based builds

For now, golint has been disabled since it fails due to wanting
HTTPPostMode.  This change will be made as a part of the upgrade to use
btcjson2 since it involves an API change.
2015-01-15 20:58:39 -06:00
Dave Collins
d5cbdf8492 Fix a couple of issues found by golint. 2015-01-15 18:09:18 -06:00
Dave Collins
bbec5f3a91 Update websocket import paths to new location. 2015-01-15 17:25:14 -06:00
Dave Collins
3dfefc978f Update btcutil import paths to new location. 2015-01-15 10:41:20 -06:00
Dave Collins
c74bb4e087 Update license year to 2015. 2015-01-03 02:24:28 -06:00
David Hill
37fbff291c Add missing error check 2014-12-13 10:02:59 -05:00
Jimmy Song
59e825d796 Added GetTxOut as a possible command to btcrpcclient.
Put into chain.go vs other places as per @davecgh
2014-10-26 17:30:10 -05:00
Dave Collins
a8dda1fabf Add ListReceivedByAddress support.
This commit also fixes a couple of comment typos found while adding
support for ListReceivedByAddress.

Closes #20.
2014-10-26 17:16:28 -05:00
Javed Khan
331f25f506 compatiblity hack for blockchain.info getbalance 2014-09-10 21:22:09 +05:30
Josh Rickmar
160a843171 Allow websocket conns to be established after New.
ok @davecgh
2014-07-25 13:17:43 -05:00
Josh Rickmar
6b8ff7f52f Use block shas for rescan begin/end.
Also send block sha and block time for rescanprogress/finished.

ok @davecgh
2014-07-25 13:17:30 -05:00
Dave Collins
a07dadb600 Add Move support.
This commit adds the Move, MoveAsync, MoveMinConf, MoveMinConfAsync,
MoveComment, and MoveCommentAsync functions for issuing the "move" RPC
with various optional parameters.

Closes #16.
2014-07-09 21:42:50 -05:00
Michalis Kargakis
406dd2fcb7 Fix typos 2014-07-08 15:40:53 +03:00
Dave Collins
656fa8699b Improve disconnect and shutdown handling.
Previously the exported Disconnect and Shutdown functions called each other
and therefore needed to release and reacquire the locks which could
potentionally allow a request to sneak in between the lock and unlock.

This commit changes the exported Shutdown function so that everything is
done under the request lock to prevent this possibility.  In order to
support this, the shared code between the Disconnect and Shutdown
functions has been refactored into unexported functions and the locking
has been altered accordingly.

Also, replace the sendMessage function with a select over the send and
disconnect channels to prevent any issues with queued up messages.

joint debugging effort between myself and @jrick
2014-07-07 21:58:13 -05:00
Josh Rickmar
1a23feb53e Fix hang on new request receives after shutdown.
Previously, requests could still be sent to a shutdown client and
added to the client's internal data structures, without ever
responding to the future with an error for a shutdown client (causing
hangs when blocking on the future receive).  This change fixes this by
performing a non-blocking read of the client's shutdown channel before
adding a request, and responding with the shutdown error if the client
has begun or completed its shutdown.

ok @davecgh
2014-07-07 19:11:50 -05:00
Dave Collins
1a866200e3 Improve websocket connection error handling.
This commit modifies the error handling for websocket connections to fall
back to returning the status text returned from the server if the
handshake fails and it's not due to an authentication or invalid endpoint.
2014-07-03 12:31:53 -05:00
Tomás Senart
77fdb1011b Handle non successfull HTTP responses
This change set equips the RPC client with handling of non successful
HTTP responses. An HTTP response is considered non successful when its
status code is not in the range 200..299
2014-07-03 18:54:50 +02:00
Dave Collins
0e463baf95 goimports -w . 2014-07-02 19:34:05 -05:00
Josh Rickmar
130edaec65 Add ListLockUnspent support.
Closes #11.

ok @davecgh
2014-06-30 10:30:11 -05:00
Josh Rickmar
4c045cc1ec Add LockUnspent support.
Closes #10.

ok @davecgh
2014-06-30 10:30:10 -05:00
GeertJohan
f1bfb5dc1c Add ImportPrivKey versions with Label and Rescan arguments. 2014-06-28 23:34:38 -05:00
Dave Collins
894d5e23aa Unmarshal the SubmitBlock error.
The string needs to be unmarshaled rather than returned directly otherwise
it will have an extra set of quotes plus it's safter to properly unmarshal
it.

Pointed out by @jrick.
2014-06-26 21:47:43 -05:00
Dave Collins
0f6726b9c3 Add missing import. 2014-06-26 20:24:40 -05:00
Dave Collins
cb67972512 Detect error strings on SumitBlock.
The submitblock RPC returns a string containing an error if it failed for
any reason.  This comment detects a non-null return from submitblock and
converts that string to an error which is returned.
2014-06-26 18:04:01 -05:00
Dave Collins
a9e1b8fb84 Use system CAs when Certificates are not specified.
This commit modifies the TLS setup to only override the RootCAs for the
TLS connection if certificates are specified.  This allows the
Certificates parameter to be ommitted from the connection config to use
the system CAs.
2014-06-25 21:53:00 -05:00
David Hill
0ae3676a7d Log when the initial connection has been made. 2014-06-25 12:56:47 -04:00
Josh Rickmar
22b6af1400 Fix unmarshaling of HTTP POST responses.
If connecting to a bitcoin RPC server as an HTTP POST client, full
response objects rather than just the raw result bytes were being
passed to the specific result unmarshalers, causing unmarshal errors
for the incorrect JSON types.  To fix this, first unmarshal the
response body into a rawResponse, and pass only the raw result bytes
(or an error) to the specific handlers.

This was reported by nskelsey on IRC.

ok @davecgh
2014-06-18 10:21:18 -05:00
Josh Rickmar
aceaed82c5 Add support for new rescanfinished notification.
ok @davecgh
2014-06-16 14:45:47 -05:00
Dave Collins
7448f9555c Make golint happier. 2014-06-12 23:00:03 -05:00
Dave Collins
67d570e660 Correct a few cases of address encoding.
There are certain RPCs where an address should be sent as the raw string
instead of the encoded bitcoin address since the RPC handler on the other
end expects "keys" instead of Bitcoin addresses.

For example, the multisignature RPCs addmultisigaddress and createmultisig
can work with pubkeys (compressed, uncompressed, or hybrid) as well as
Bitcoin addresses.

The original issue which prompted these changes was report by Paul Snow on
IRC.
2014-06-12 22:55:30 -05:00
Josh Rickmar
28c0a3c8c7 Modify doco and example for btcwallet ws endpoint. 2014-06-12 13:00:42 -05:00
Josh Rickmar
4ac778d72a Do not reissue rescan requests on reconnect. 2014-06-12 11:38:22 -05:00
Josh Rickmar
cfb46e2c43 Add getinfo support. 2014-06-11 16:38:54 -05:00
Josh Rickmar
1ec6dde39c Add custom/raw request/response support. 2014-06-11 16:38:54 -05:00
Josh Rickmar
793e66c785 Add callback for connects and reconnects. 2014-06-11 16:38:54 -05:00
Dave Collins
8e624b20a9 Switch to conformal vendoring of Gorilla websocket. 2014-06-07 01:06:11 -05:00
Dave Collins
1602463681 Don't split up the group for debuglevel functions. 2014-06-05 23:57:05 -05:00
Dave Collins
7eea8252a4 Add the new example to README.md and doc.go. 2014-06-05 20:58:47 -05:00
Dave Collins
cd4018b33e Add btcwallet websocket example.
Reviewed by @jrick who also provided some comment and terminology
improvements.
2014-06-05 20:40:45 -05:00
Michalis Kargakis
e31398a272 Add height parameter in disconn notification 2014-06-05 20:48:56 +03:00
Dave Collins
4e8e63e0d7 Use bytes.NewReader for all deserialization.
Rather than using bytes.NewBuffer, which is a read/write entity
(io.ReadWriter), use bytes.NewReader which is only a read entitiy
(io.Reader).  Benchmarking shows it's slightly faster and it's also
technically more accurate since it ensures the data is read-only.
2014-06-04 22:12:47 -05:00
Dave Collins
879a125a2f Update CONTRIBUTORS. 2014-06-02 09:50:03 -05:00
kargakis
a785ef6424 minor syntax fixes 2014-06-02 17:31:02 +03:00
Josh Rickmar
67e94bcaaa Add createencryptedwallet extension support. 2014-05-28 11:33:28 -05:00
Dave Collins
0c586634bd Update for recent btcutil API changes. 2014-05-27 17:03:09 -05:00
Dave Collins
6af13826ae Convert {Dump,Import}PrivKey to use new WIF type.
The btcutil package recently exposed a WIF type to provide more
functionality and simplify working with WIF strings.  This commit changes
the DumpPrivKey and ImportPrivKey RPCs to use the new WIF type.
2014-05-21 20:00:11 -05:00
Dave Collins
9d9c343247 Make nil pointer handling consistent across RPCs.
There are several RPCs which accept a pointer to a hash, transaction,
block, etc.  Previously not all RPCs handled being passed a nil pointer
consistently.

Closes #4.
2014-05-21 19:53:46 -05:00
Dave Collins
57738b1920 gofmt 2014-05-21 19:24:45 -05:00
Josh Rickmar
85886913b8 Implement Client methods for listunspent requests. 2014-05-12 16:43:30 -05:00
Dave Collins
267cf94edc Correct typo in example log statement. 2014-05-11 13:30:25 -05:00
Dave Collins
e230b54427 Correct FutureGetBestBlockResult.Receive type.
Also correct the error message while here.
2014-05-11 02:35:07 -05:00
Dave Collins
e2ba50eee1 Fix a couple of typos in README.md. 2014-05-10 15:48:14 -05:00
Dave Collins
0fc9504da4 Improve overview in doc.go. 2014-05-10 02:50:19 -05:00
Dave Collins
9bb16e208d Update README.md.
This commit adds a description, status, and major features to the README.
2014-05-10 02:48:11 -05:00
Dave Collins
a35c1e8ede Auto recreate notification state on reconnect.
This commit adds logic to track all registered notifications that have
been registered by the client in a notification state when the default
automatic reconnect is enabled.

The notification state is then used to reregister for all previously
registered notifications on reconnect.  This allows the caller to
continue receiving notifications across reconnect cycles.
2014-05-10 02:15:09 -05:00
Dave Collins
7cc356d4c7 Convert CreateRawTransaction to higher-level types.
Closes #2.
2014-05-09 21:24:25 -05:00
Dave Collins
f666eddf75 Add README.md for bitcoincorehttp example.
Also, change the port in the example to the mainnet RPC port.
2014-05-09 21:09:51 -05:00
Dave Collins
315d85fa4e Add README.md for btcdwebsockets example. 2014-05-09 21:05:01 -05:00
Dave Collins
9b0d311826 Update README.md. 2014-05-09 20:48:05 -05:00
Dave Collins
f54e05c8fe Initial package overview documentation. 2014-05-09 20:12:47 -05:00
Dave Collins
8700eeaeb6 Convert to use gorilla websockets package.
Also, since the new package exposes more connection related error
information, add a new ErrInvalidEndpoint which is returned if the
specified enpoint does not appear to be a valid websocket provider and
only return the ErrInvalidAuth error when HTTP authorization failure
status codes are detected.

Closes #1.
2014-05-09 19:57:58 -05:00
Dave Collins
4921282646 Add example descriptions to README.md. 2014-05-09 01:35:27 -05:00
Dave Collins
2f84189676 Add godoc reference badge to README.md. 2014-05-09 01:26:37 -05:00
Dave Collins
73ecb412e9 Add support for TravisCI.
Also add TravisCI build status badge to README.md.
2014-05-09 01:16:47 -05:00
Dave Collins
eb82f35aac Improve btcdwebsockets example.
Since the example illustrates callbacks for the OnBlockConnected and
OnBlockDisconnected callbacks, also register for the notifications with
NotifyBlocks.

While here, document the fact that most of the callbacks require
registration.
2014-05-09 00:45:09 -05:00
Dave Collins
80f9a8e5e2 Fix FutureWalletPassphraseChange.Receive comment. 2014-05-08 13:12:56 -05:00
Dave Collins
2fc983ece1 Finish the comment on the Disconnect function. 2014-05-08 13:10:09 -05:00
Dave Collins
6825e68c59 Use uint64 for the request ID instead of int64.
There is no reason for the request ID to be signed.  Pointed out by
@jrick.
2014-05-08 13:05:31 -05:00
Dave Collins
01183c4eca Update for recent notifyspent changes.
Closes #2.
2014-05-07 11:23:48 -05:00
Dave Collins
7c552136bc Move Disconnected next to Disconnect func. 2014-05-07 11:23:35 -05:00
Dave Collins
1122a8a9cb Remove leftover testing sleep. 2014-05-07 11:23:27 -05:00
Dave Collins
2f7cb64652 Add CONTRIBUTORS file. 2014-05-07 11:23:16 -05:00
GeertJohan
0cc22e1134 Make btcrpcclient use the new and consistent reply types. 2014-05-07 11:23:00 -05:00
Dave Collins
831d0aeb90 Add support for getwork submit. 2014-05-07 11:22:38 -05:00
Dave Collins
fa7f670160 Add link to examples in README.md. 2014-05-07 11:22:12 -05:00
Dave Collins
43ca7c0f3c Add a couple of examples. 2014-05-07 11:21:36 -05:00
Dave Collins
cf409a8d79 Disconnect client on shutdown. 2014-05-07 11:19:30 -05:00
Dave Collins
e3f130ade5 Initial commit. 2014-05-07 11:14:39 -05:00
803 changed files with 123947 additions and 54871 deletions

2
.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
vendor/
**/docker-compose.yml

15
.gitignore vendored
View File

@@ -17,6 +17,7 @@ btcd.db
# Folders
_obj
_test
vendor
# Architecture specific extensions/prefixes
*.[568vq]
@@ -31,3 +32,17 @@ _cgo_export.*
_testmain.go
*.exe
# IDE
.idea
.vscode
debug
debug.test
__debug_bin
# CI
version.txt
coverage.txt
testdbs/
coverage.tmp

View File

@@ -1,17 +0,0 @@
language: go
go:
- 1.3.3
- 1.4.2
sudo: false
before_install:
- gotools=golang.org/x/tools
- if [ "$TRAVIS_GO_VERSION" = "go1.3.3" ]; then gotools=code.google.com/p/go.tools; fi
install:
- go get -d -t -v ./...
- go get -v $gotools/cmd/cover
- go get -v $gotools/cmd/vet
- go get -v github.com/bradfitz/goimports
- go get -v github.com/golang/lint/golint
script:
- export PATH=$PATH:$HOME/gopath/bin
- ./goclean.sh

166
CHANGES
View File

@@ -3,6 +3,160 @@ User visible changes for btcd
A full-node bitcoin implementation written in Go
============================================================================
Changes in 0.12.0 (Fri Nov 20 2015)
- Protocol and network related changes:
- Add a new checkpoint at block height 382320 (#555)
- Implement BIP0065 which includes support for version 4 blocks, a new
consensus opcode (OP_CHECKLOCKTIMEVERIFY) that enforces transaction
lock times, and a double-threshold switchover mechanism (#535, #459,
#455)
- Implement BIP0111 which provides a new bloom filter service flag and
hence provides support for protocol version 70011 (#499)
- Add a new parameter --nopeerbloomfilters to allow disabling bloom
filter support (#499)
- Reject non-canonically encoded variable length integers (#507)
- Add mainnet peer discovery DNS seed (seed.bitcoin.jonasschnelli.ch)
(#496)
- Correct reconnect handling for persistent peers (#463, #464)
- Ignore requests for block headers if not fully synced (#444)
- Add CLI support for specifying the zone id on IPv6 addresses (#538)
- Fix a couple of issues where the initial block sync could stall (#518,
#229, #486)
- Fix an issue which prevented the --onion option from working as
intended (#446)
- Transaction relay (memory pool) changes:
- Require transactions to only include signatures encoded with the
canonical 'low-s' encoding (#512)
- Add a new parameter --minrelaytxfee to allow the minimum transaction
fee in BTC/kB to be overridden (#520)
- Retain memory pool transactions when they redeem another one that is
removed when a block is accepted (#539)
- Do not send reject messages for a transaction if it is valid but
causes an orphan transaction which depends on it to be determined
as invalid (#546)
- Refrain from attempting to add orphans to the memory pool multiple
times when the transaction they redeem is added (#551)
- Modify minimum transaction fee calculations to scale based on bytes
instead of full kilobyte boundaries (#521, #537)
- Implement signature cache:
- Provides a limited memory cache of validated signatures which is a
huge optimization when verifying blocks for transactions that are
already in the memory pool (#506)
- Add a new parameter '--sigcachemaxsize' which allows the size of the
new cache to be manually changed if desired (#506)
- Mining support changes:
- Notify getblocktemplate long polling clients when a block is pushed
via submitblock (#488)
- Speed up getblocktemplate by making use of the new signature cache
(#506)
- RPC changes:
- Implement getmempoolinfo command (#453)
- Implement getblockheader command (#461)
- Modify createrawtransaction command to accept a new optional parameter
'locktime' (#529)
- Modify listunspent result to include the 'spendable' field (#440)
- Modify getinfo command to include 'errors' field (#511)
- Add timestamps to blockconnected and blockdisconnected notifications
(#450)
- Several modifications to searchrawtranscations command:
- Accept a new optional parameter 'vinextra' which causes the results
to include information about the outputs referenced by a transaction's
inputs (#485, #487)
- Skip entries in the mempool too (#495)
- Accept a new optional parameter 'reverse' to return the results in
reverse order (most recent to oldest) (#497)
- Accept a new optional parameter 'filteraddrs' which causes the
results to only include inputs and outputs which involve the
provided addresses (#516)
- Change the notification order to notify clients about mined
transactions (recvtx, redeemingtx) before the blockconnected
notification (#449)
- Update verifymessage RPC to use the standard algorithm so it is
compatible with other implementations (#515)
- Improve ping statistics by pinging on an interval (#517)
- Websocket changes:
- Implement session command which returns a per-session unique id (#500,
#503)
- btcctl utility changes:
- Add getmempoolinfo command (#453)
- Add getblockheader command (#461)
- Add getwalletinfo command (#471)
- Notable developer-related package changes:
- Introduce a new peer package which acts a common base for creating and
concurrently managing bitcoin network peers (#445)
- Various cleanup of the new peer package (#528, #531, #524, #534,
#549)
- Blocks heights now consistently use int32 everywhere (#481)
- The BlockHeader type in the wire package now provides the BtcDecode
and BtcEncode methods (#467)
- Update wire package to recognize BIP0064 (getutxo) service bit (#489)
- Export LockTimeThreshold constant from txscript package (#454)
- Export MaxDataCarrierSize constant from txscript package (#466)
- Provide new IsUnspendable function from the txscript package (#478)
- Export variable length string functions from the wire package (#514)
- Export DNS Seeds for each network from the chaincfg package (#544)
- Preliminary work towards separating the memory pool into a separate
package (#525, #548)
- Misc changes:
- Various documentation updates (#442, #462, #465, #460, #470, #473,
#505, #530, #545)
- Add installation instructions for gentoo (#542)
- Ensure an error is shown if OS limits can't be set at startup (#498)
- Tighten the standardness checks for multisig scripts (#526)
- Test coverage improvement (#468, #494, #527, #543, #550)
- Several optimizations (#457, #474, #475, #476, #508, #509)
- Minor code cleanup and refactoring (#472, #479, #482, #519, #540)
- Contributors (alphabetical order):
- Ben Echols
- Bruno Clermont
- danda
- Daniel Krawisz
- Dario Nieuwenhuis
- Dave Collins
- David Hill
- Javed Khan
- Jonathan Gillham
- Joseph Becher
- Josh Rickmar
- Justus Ranvier
- Mawuli Adzoe
- Olaoluwa Osuntokun
- Rune T. Aune
Changes in 0.11.1 (Wed May 27 2015)
- Protocol and network related changes:
- Use correct sub-command in reject message for rejected transactions
(#436, #437)
- Add a new parameter --torisolation which forces new circuits for each
connection when using tor (#430)
- Transaction relay (memory pool) changes:
- Reduce the default number max number of allowed orphan transactions
to 1000 (#419)
- Add a new parameter --maxorphantx which allows the maximum number of
orphan transactions stored in the mempool to be specified (#419)
- RPC changes:
- Modify listtransactions result to include the 'involveswatchonly' and
'vout' fields (#427)
- Update getrawtransaction result to omit the 'confirmations' field
when it is 0 (#420, #422)
- Update signrawtransaction result to include errors (#423)
- btcctl utility changes:
- Add gettxoutproof command (#428)
- Add verifytxoutproof command (#428)
- Notable developer-related package changes:
- The btcec package now provides the ability to perform ECDH
encryption and decryption (#375)
- The block and header validation in the blockchain package has been
split to help pave the way toward concurrent downloads (#386)
- Misc changes:
- Minor peer optimization (#433)
- Contributors (alphabetical order):
- Dave Collins
- David Hill
- Federico Bond
- Ishbir Singh
- Josh Rickmar
Changes in 0.11.0 (Wed May 06 2015)
- Protocol and network related changes:
- **IMPORTANT: Update is required due to the following point**
@@ -10,7 +164,7 @@ Changes in 0.11.0 (Wed May 06 2015)
forking from the network on non-standard transactions (#425)
- Add a new checkpoint at block height 352940 (#418)
- Optimized script execution (#395, #400, #404, #409)
- Fix a case that could lead stalled syncs (#138, #296)
- Fix a case that could lead stalled syncs (#138, #296)
- Network address manager changes:
- Implement eclipse attack countermeasures as proposed in
http://cs-people.bu.edu/heilman/eclipse (#370, #373)
@@ -359,7 +513,7 @@ Changes in 0.8.0-beta (Sun May 25 2014)
- Reduce max bytes allowed for a standard nulldata transaction to 40 for
compatibility with the reference client
- Introduce a new btcnet package which houses all of the network params
for each network (mainnet, testnet3, regtest) to ultimately enable
for each network (mainnet, testnet, regtest) to ultimately enable
easier addition and tweaking of networks without needing to change
several packages
- Fix several script discrepancies found by reference client test data
@@ -376,7 +530,7 @@ Changes in 0.8.0-beta (Sun May 25 2014)
- Provide options to control block template creation settings
- Support the getwork RPC
- Allow address identifiers to apply to more than one network since both
testnet3 and the regression test network unfortunately use the same
testnet and the regression test network unfortunately use the same
identifier
- RPC changes:
- Set the content type for HTTP POST RPC connections to application/json
@@ -416,8 +570,8 @@ Changes in 0.8.0-beta (Sun May 25 2014)
- btcctl utility changes:
- Add createencryptedwallet command
- Add getblockchaininfo command
- Add importwallet commmand
- Add addmultisigaddress commmand
- Add importwallet command
- Add addmultisigaddress command
- Add setgenerate command
- Accept --testnet and --wallet flags which automatically select
the appropriate port and TLS certificates needed to communicate
@@ -479,7 +633,7 @@ Changes in 0.7.0 (Thu Feb 20 2014)
- Add getnetworkhashps command
- Add gettransaction command (wallet-specific)
- Add signmessage command (wallet-specific)
- Update getwork command to accept
- Update getwork command to accept
- Continue cleanup and work on implementing the RPC API:
- Implement getnettotals command
(https://github.com/conformal/btcd/issues/84)

10
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,10 @@
node {
stage 'Checkout'
checkout scm
stage 'Version'
sh './deploy.sh version'
stage 'Build'
sh "./deploy.sh build"
}

View File

@@ -1,4 +1,8 @@
Copyright (c) 2013-2015 The btcsuite developers
ISC License
Copyright (c) 2018-2019 DAGLabs
Copyright (c) 2013-2018 The btcsuite developers
Copyright (c) 2015-2016 The Decred developers
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above

View File

@@ -1,28 +1,28 @@
btcd
====
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)]
(https://travis-ci.org/btcsuite/btcd)
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)](https://travis-ci.org/btcsuite/btcd)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/kaspanet/kaspad)
btcd is an alternative full node bitcoin implementation written in Go (golang).
This project is currently under active development and is in a Beta state. It
is extremely stable and has been in production use for over 6 months as of May
2014, however there are still a couple of major features we want to add before
we come out of beta.
is extremely stable and has been in production use since October 2013.
It properly downloads, validates, and serves the block chain using the exact
rules (including bugs) for block acceptance as Bitcoin Core. We have taken
great care to avoid btcd causing a fork to the block chain. It passes all of
the 'official' block acceptance tests
(https://github.com/TheBlueMatt/test-scripts) as well as all of the JSON test
data in the Bitcoin Core code.
rules (including consensus bugs) for block acceptance as Bitcoin Core. We have
taken great care to avoid btcd causing a fork to the block chain. It includes a
full block validation testing framework which contains all of the 'official'
block acceptance tests (and some additional ones) that is run on every pull
request to help ensure it properly follows consensus. Also, it passes all of
the JSON test data in the Bitcoin Core code.
It also relays newly mined blocks, maintains a transaction pool, and relays
individual transactions that have not yet made it into a block. It ensures all
transactions admitted to the pool follow the rules required by the block chain
and also includes the same checks which filter transactions based on
miner requirements ("standard" transactions) as Bitcoin Core.
It also properly relays newly mined blocks, maintains a transaction pool, and
relays individual transactions that have not yet made it into a block. It
ensures all individual transactions admitted to the pool follow the rules
required by the block chain and also includes more strict checks which filter
transactions based on miner requirements ("standard" transactions).
One key difference between btcd and Bitcoin Core is that btcd does *NOT* include
wallet functionality and this was a very intentional design decision. See the
@@ -30,34 +30,49 @@ blog entry [here](https://blog.conformal.com/btcd-not-your-moms-bitcoin-daemon)
for more details. This means you can't actually make or receive payments
directly with btcd. That functionality is provided by the
[btcwallet](https://github.com/btcsuite/btcwallet) and
[btcgui](https://github.com/btcsuite/btcgui) projects which are both under
active development.
[Paymetheus](https://github.com/btcsuite/Paymetheus) (Windows-only) projects
which are both under active development.
## Requirements
[Go](http://golang.org) 1.3 or newer.
[Go](http://golang.org) 1.8 or newer.
## Installation
#### Windows - MSI Available
https://github.com/btcsuite/btcd/releases
https://github.com/kaspanet/kaspad/releases
#### Linux/BSD/MacOSX/POSIX - Build from Source
- Install Go according to the installation instructions here:
http://golang.org/doc/install
- Run the following command to obtain btcd, all dependencies, and install it:
- Ensure Go was installed properly and is a supported version:
```bash
$ go get github.com/btcsuite/btcd/...
$ go version
$ go env GOROOT GOPATH
```
- btcd (and utilities) will now be installed in either ```$GOROOT/bin``` or
```$GOPATH/bin``` depending on your configuration. If you did not already
add the bin directory to your system path during Go installation, we
recommend you do so now.
NOTE: The `GOROOT` and `GOPATH` above must not be the same path. It is
recommended that `GOPATH` is set to a directory in your home directory such as
`~/goprojects` to avoid write permission issues. It is also recommended to add
`$GOPATH/bin` to your `PATH` at this point.
- Run the following commands to obtain btcd, all dependencies, and install it:
```bash
$ # Install dep: https://golang.github.io/dep/docs/installation.html
$ git clone https://github.com/kaspanet/kaspad $GOPATH/src/github.com/kaspanet/kaspad
$ cd $GOPATH/src/github.com/kaspanet/kaspad
$ dep ensure
$ go install . ./cmd/...
```
- btcd (and utilities) will now be installed in ```$GOPATH/bin```. If you did
not already add the bin directory to your system path during Go installation,
we recommend you do so now.
## Updating
@@ -67,15 +82,17 @@ Install a newer MSI
#### Linux/BSD/MacOSX/POSIX - Build from Source
- Run the following command to update btcd, all dependencies, and install it:
- Run the following commands to update btcd, all dependencies, and install it:
```bash
$ go get -u -v github.com/btcsuite/btcd/...
$ cd $GOPATH/src/github.com/kaspanet/kaspad
$ git pull && dep ensure
$ go install . ./cmd/...
```
## Getting Started
btcd has several configuration options avilable to tweak how it runs, but all
btcd has several configuration options available to tweak how it runs, but all
of the basic operations described in the intro section work with zero
configuration.
@@ -87,29 +104,22 @@ Launch btcd from your Start menu.
```bash
$ ./btcd
````
```
## IRC server
## IRC
- irc.conformal.com:6697
- ssl required
- irc.freenode.net
- channel #btcd
## Mailing lists
- btcd: discussion of btcd and its packages.
- btcd-commits: readonly mail-out of source code changes.
To subscribe to a given list, send email to list+subscribe@opensource.conformal.com
- [webchat](https://webchat.freenode.net/?channels=btcd)
## Issue Tracker
The [integrated github issue tracker](https://github.com/btcsuite/btcd/issues)
The [integrated github issue tracker](https://github.com/kaspanet/kaspad/issues)
is used for this project.
## Documentation
The documentation is a work-in-progress. It is located in the [docs](https://github.com/btcsuite/btcd/tree/master/docs) folder.
The documentation is a work-in-progress. It is located in the [docs](https://github.com/kaspanet/kaspad/tree/master/docs) folder.
## GPG Verification Key
@@ -117,8 +127,8 @@ All official release tags are signed by Conformal so users can ensure the code
has not been tampered with and is coming from the btcsuite developers. To
verify the signature perform the following:
- Download the public key from the Conformal website at
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
- Download the Conformal public key:
https://raw.githubusercontent.com/btcsuite/btcd/master/release/GIT-GPG-KEY-conformal.txt
- Import the public key into your GPG keyring:
```bash

File diff suppressed because it is too large Load Diff

View File

@@ -2,21 +2,25 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package addrmgr_test
package addrmgr
import (
"errors"
"bou.ke/monkey"
"fmt"
"github.com/kaspanet/kaspad/config"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/pkg/errors"
"net"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/addrmgr"
"github.com/btcsuite/btcd/wire"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/kaspanet/kaspad/wire"
)
// naTest is used to describe a test to be perfomed against the NetAddressKey
// naTest is used to describe a test to be performed against the NetAddressKey
// method.
type naTest struct {
in wire.NetAddress
@@ -93,12 +97,7 @@ func addNaTests() {
func addNaTest(ip string, port uint16, want string) {
nip := net.ParseIP(ip)
na := wire.NetAddress{
Timestamp: time.Now(),
Services: wire.SFNodeNetwork,
IP: nip,
Port: port,
}
na := *wire.NewNetAddressIPPort(nip, port, wire.SFNodeNetwork)
test := naTest{na, want}
naTests = append(naTests, test)
}
@@ -108,7 +107,7 @@ func lookupFunc(host string) ([]net.IP, error) {
}
func TestStartStop(t *testing.T) {
n := addrmgr.New("teststartstop", lookupFunc)
n := New("teststartstop", lookupFunc, nil)
n.Start()
err := n.Stop()
if err != nil {
@@ -117,7 +116,17 @@ func TestStartStop(t *testing.T) {
}
func TestAddAddressByIP(t *testing.T) {
fmtErr := fmt.Errorf("")
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
fmtErr := errors.Errorf("")
addrErr := &net.AddrError{}
var tests = []struct {
addrIP string
@@ -141,9 +150,9 @@ func TestAddAddressByIP(t *testing.T) {
},
}
amgr := addrmgr.New("testaddressbyip", nil)
amgr := New("testaddressbyip", nil, nil)
for i, test := range tests {
err := amgr.AddAddressByIP(test.addrIP)
err := amgr.AddAddressByIP(test.addrIP, nil)
if test.err != nil && err == nil {
t.Errorf("TestGood test %d failed expected an error and got none", i)
continue
@@ -161,43 +170,53 @@ func TestAddAddressByIP(t *testing.T) {
}
func TestAddLocalAddress(t *testing.T) {
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
var tests = []struct {
address wire.NetAddress
priority addrmgr.AddressPriority
priority AddressPriority
valid bool
}{
{
wire.NetAddress{IP: net.ParseIP("192.168.0.100")},
addrmgr.InterfacePrio,
InterfacePrio,
false,
},
{
wire.NetAddress{IP: net.ParseIP("204.124.1.1")},
addrmgr.InterfacePrio,
InterfacePrio,
true,
},
{
wire.NetAddress{IP: net.ParseIP("204.124.1.1")},
addrmgr.BoundPrio,
BoundPrio,
true,
},
{
wire.NetAddress{IP: net.ParseIP("::1")},
addrmgr.InterfacePrio,
InterfacePrio,
false,
},
{
wire.NetAddress{IP: net.ParseIP("fe80::1")},
addrmgr.InterfacePrio,
InterfacePrio,
false,
},
{
wire.NetAddress{IP: net.ParseIP("2620:100::1")},
addrmgr.InterfacePrio,
InterfacePrio,
true,
},
}
amgr := addrmgr.New("testaddlocaladdress", nil)
amgr := New("testaddlocaladdress", nil, nil)
for x, test := range tests {
result := amgr.AddLocalAddress(&test.address, test.priority)
if result == nil && !test.valid {
@@ -214,14 +233,24 @@ func TestAddLocalAddress(t *testing.T) {
}
func TestAttempt(t *testing.T) {
n := addrmgr.New("testattempt", lookupFunc)
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
n := New("testattempt", lookupFunc, nil)
// Add a new address and get it
err := n.AddAddressByIP(someIP + ":8333")
err := n.AddAddressByIP(someIP+":8333", nil)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
ka := n.GetAddress("any")
ka := n.GetAddress()
if !ka.LastAttempt().IsZero() {
t.Errorf("Address should not have attempts, but does")
@@ -236,16 +265,27 @@ func TestAttempt(t *testing.T) {
}
func TestConnected(t *testing.T) {
n := addrmgr.New("testconnected", lookupFunc)
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
n := New("testconnected", lookupFunc, nil)
// Add a new address and get it
err := n.AddAddressByIP(someIP + ":8333")
err := n.AddAddressByIP(someIP+":8333", nil)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
ka := n.GetAddress("any")
ka := n.GetAddress()
na := ka.NetAddress()
na.Timestamp = time.Now().Add(time.Hour * -1) // make it an hour ago
// make it an hour ago
na.Timestamp = time.Unix(time.Now().Add(time.Hour*-1).Unix(), 0)
n.Connected(na)
@@ -255,14 +295,23 @@ func TestConnected(t *testing.T) {
}
func TestNeedMoreAddresses(t *testing.T) {
n := addrmgr.New("testneedmoreaddresses", lookupFunc)
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
n := New("testneedmoreaddresses", lookupFunc, nil)
addrsToAdd := 1500
b := n.NeedMoreAddresses()
if b == false {
if !b {
t.Errorf("Expected that we need more addresses")
}
addrs := make([]*wire.NetAddress, addrsToAdd)
now := time.Now()
var err error
for i := 0; i < addrsToAdd; i++ {
@@ -273,30 +322,36 @@ func TestNeedMoreAddresses(t *testing.T) {
}
}
srcAddr := &wire.NetAddress{
Timestamp: now,
Services: 0,
IP: net.IPv4(173, 144, 173, 111),
Port: 8333,
}
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
n.AddAddresses(addrs, srcAddr)
numAddrs := n.NumAddresses()
n.AddAddresses(addrs, srcAddr, nil)
numAddrs := n.TotalNumAddresses()
if numAddrs > addrsToAdd {
t.Errorf("Number of addresses is too many %d vs %d", numAddrs, addrsToAdd)
}
b = n.NeedMoreAddresses()
if b == true {
if b {
t.Errorf("Expected that we don't need more addresses")
}
}
func TestGood(t *testing.T) {
n := addrmgr.New("testgood", lookupFunc)
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
n := New("testgood", lookupFunc, nil)
addrsToAdd := 64 * 64
addrs := make([]*wire.NetAddress, addrsToAdd)
now := time.Now()
subnetworkCount := 32
subnetworkIDs := make([]*subnetworkid.SubnetworkID, subnetworkCount)
var err error
for i := 0; i < addrsToAdd; i++ {
@@ -307,67 +362,209 @@ func TestGood(t *testing.T) {
}
}
srcAddr := &wire.NetAddress{
Timestamp: now,
Services: 0,
IP: net.IPv4(173, 144, 173, 111),
Port: 8333,
for i := 0; i < subnetworkCount; i++ {
subnetworkIDs[i] = &subnetworkid.SubnetworkID{0xff - byte(i)}
}
n.AddAddresses(addrs, srcAddr)
for _, addr := range addrs {
n.Good(addr)
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
n.AddAddresses(addrs, srcAddr, nil)
for i, addr := range addrs {
n.Good(addr, subnetworkIDs[i%subnetworkCount])
}
numAddrs := n.NumAddresses()
numAddrs := n.TotalNumAddresses()
if numAddrs >= addrsToAdd {
t.Errorf("Number of addresses is too many: %d vs %d", numAddrs, addrsToAdd)
}
numCache := len(n.AddressCache())
if numCache >= numAddrs/4 {
t.Errorf("Number of addresses in cache: got %d, want %d", numCache, numAddrs/4)
numCache := len(n.AddressCache(true, nil))
if numCache == 0 || numCache >= numAddrs/4 {
t.Errorf("Number of addresses in cache: got %d, want positive and less than %d",
numCache, numAddrs/4)
}
for i := 0; i < subnetworkCount; i++ {
numCache = len(n.AddressCache(false, subnetworkIDs[i]))
if numCache == 0 || numCache >= numAddrs/subnetworkCount {
t.Errorf("Number of addresses in subnetwork cache: got %d, want positive and less than %d",
numCache, numAddrs/4/subnetworkCount)
}
}
}
func TestGoodChangeSubnetworkID(t *testing.T) {
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
n := New("test_good_change_subnetwork_id", lookupFunc, nil)
addr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
addrKey := NetAddressKey(addr)
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
oldSubnetwork := subnetworkid.SubnetworkIDNative
n.AddAddress(addr, srcAddr, oldSubnetwork)
n.Good(addr, oldSubnetwork)
// make sure address was saved to addrIndex under oldSubnetwork
ka := n.find(addr)
if ka == nil {
t.Fatalf("Address was not found after first time .Good called")
}
if !ka.SubnetworkID().IsEqual(oldSubnetwork) {
t.Fatalf("Address index did not point to oldSubnetwork")
}
// make sure address was added to correct bucket under oldSubnetwork
bucket := n.addrTried[*oldSubnetwork][n.getTriedBucket(addr)]
wasFound := false
for e := bucket.Front(); e != nil; e = e.Next() {
if NetAddressKey(e.Value.(*KnownAddress).NetAddress()) == addrKey {
wasFound = true
}
}
if !wasFound {
t.Fatalf("Address was not found in the correct bucket in oldSubnetwork")
}
// now call .Good again with a different subnetwork
newSubnetwork := subnetworkid.SubnetworkIDRegistry
n.Good(addr, newSubnetwork)
// make sure address was updated in addrIndex under newSubnetwork
ka = n.find(addr)
if ka == nil {
t.Fatalf("Address was not found after second time .Good called")
}
if !ka.SubnetworkID().IsEqual(newSubnetwork) {
t.Fatalf("Address index did not point to newSubnetwork")
}
// make sure address was removed from bucket under oldSubnetwork
bucket = n.addrTried[*oldSubnetwork][n.getTriedBucket(addr)]
wasFound = false
for e := bucket.Front(); e != nil; e = e.Next() {
if NetAddressKey(e.Value.(*KnownAddress).NetAddress()) == addrKey {
wasFound = true
}
}
if wasFound {
t.Fatalf("Address was not removed from bucket in oldSubnetwork")
}
// make sure address was added to correct bucket under newSubnetwork
bucket = n.addrTried[*newSubnetwork][n.getTriedBucket(addr)]
wasFound = false
for e := bucket.Front(); e != nil; e = e.Next() {
if NetAddressKey(e.Value.(*KnownAddress).NetAddress()) == addrKey {
wasFound = true
}
}
if !wasFound {
t.Fatalf("Address was not found in the correct bucket in newSubnetwork")
}
}
func TestGetAddress(t *testing.T) {
n := addrmgr.New("testgetaddress", lookupFunc)
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
localSubnetworkID := &subnetworkid.SubnetworkID{0xff}
n := New("testgetaddress", lookupFunc, localSubnetworkID)
// Get an address from an empty set (should error)
if rv := n.GetAddress("any"); rv != nil {
if rv := n.GetAddress(); rv != nil {
t.Errorf("GetAddress failed: got: %v want: %v\n", rv, nil)
}
// Add a new address and get it
err := n.AddAddressByIP(someIP + ":8333")
err := n.AddAddressByIP(someIP+":8332", localSubnetworkID)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
ka := n.GetAddress("any")
ka := n.GetAddress()
if ka == nil {
t.Fatalf("Did not get an address where there is one in the pool")
}
n.Attempt(ka.NetAddress())
// Checks that we don't get it if we find that it has other subnetwork ID than expected.
actualSubnetworkID := &subnetworkid.SubnetworkID{0xfe}
n.Good(ka.NetAddress(), actualSubnetworkID)
ka = n.GetAddress()
if ka != nil {
t.Errorf("Didn't expect to get an address because there shouldn't be any address from subnetwork ID %s or nil", localSubnetworkID)
}
// Checks that the total number of addresses incremented although the new address is not full node or a partial node of the same subnetwork as the local node.
numAddrs := n.TotalNumAddresses()
if numAddrs != 1 {
t.Errorf("Wrong number of addresses: got %d, want %d", numAddrs, 1)
}
// Now we repeat the same process, but now the address has the expected subnetwork ID.
// Add a new address and get it
err = n.AddAddressByIP(someIP+":8333", localSubnetworkID)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
ka = n.GetAddress()
if ka == nil {
t.Fatalf("Did not get an address where there is one in the pool")
}
if ka.NetAddress().IP.String() != someIP {
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
}
if !ka.SubnetworkID().IsEqual(localSubnetworkID) {
t.Errorf("Wrong Subnetwork ID: got %v, want %v", *ka.SubnetworkID(), localSubnetworkID)
}
n.Attempt(ka.NetAddress())
// Mark this as a good address and get it
n.Good(ka.NetAddress())
ka = n.GetAddress("any")
n.Good(ka.NetAddress(), localSubnetworkID)
ka = n.GetAddress()
if ka == nil {
t.Fatalf("Did not get an address where there is one in the pool")
}
if ka.NetAddress().IP.String() != someIP {
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
}
if *ka.SubnetworkID() != *localSubnetworkID {
t.Errorf("Wrong Subnetwork ID: got %v, want %v", ka.SubnetworkID(), localSubnetworkID)
}
numAddrs := n.NumAddresses()
if numAddrs != 1 {
numAddrs = n.TotalNumAddresses()
if numAddrs != 2 {
t.Errorf("Wrong number of addresses: got %d, want %d", numAddrs, 1)
}
}
func TestGetBestLocalAddress(t *testing.T) {
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
localAddrs := []wire.NetAddress{
{IP: net.ParseIP("192.168.0.100")},
{IP: net.ParseIP("::1")},
@@ -417,7 +614,7 @@ func TestGetBestLocalAddress(t *testing.T) {
*/
}
amgr := addrmgr.New("testgetbestlocaladdress", nil)
amgr := New("testgetbestlocaladdress", nil, nil)
// Test against default when there's no address
for x, test := range tests {
@@ -430,7 +627,7 @@ func TestGetBestLocalAddress(t *testing.T) {
}
for _, localAddr := range localAddrs {
amgr.AddLocalAddress(&localAddr, addrmgr.InterfacePrio)
amgr.AddLocalAddress(&localAddr, InterfacePrio)
}
// Test against want1
@@ -445,7 +642,7 @@ func TestGetBestLocalAddress(t *testing.T) {
// Add a public IP to the list of local addresses.
localAddr := wire.NetAddress{IP: net.ParseIP("204.124.8.100")}
amgr.AddLocalAddress(&localAddr, addrmgr.InterfacePrio)
amgr.AddLocalAddress(&localAddr, InterfacePrio)
// Test against want2
for x, test := range tests {
@@ -457,9 +654,9 @@ func TestGetBestLocalAddress(t *testing.T) {
}
}
/*
// Add a tor generated IP address
// Add a Tor generated IP address
localAddr = wire.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")}
amgr.AddLocalAddress(&localAddr, addrmgr.ManualPrio)
amgr.AddLocalAddress(&localAddr, ManualPrio)
// Test against want3
for x, test := range tests {
@@ -478,7 +675,7 @@ func TestNetAddressKey(t *testing.T) {
t.Logf("Running %d tests", len(naTests))
for i, test := range naTests {
key := addrmgr.NetAddressKey(&test.in)
key := NetAddressKey(&test.in)
if key != test.want {
t.Errorf("NetAddressKey #%d\n got: %s want: %s", i, key, test.want)
continue

View File

@@ -9,14 +9,14 @@ Address Manager Overview
In order maintain the peer-to-peer Bitcoin network, there needs to be a source
of addresses to connect to as nodes come and go. The Bitcoin protocol provides
a the getaddr and addr messages to allow peers to communicate known addresses
with each other. However, there needs to a mechanism to store those results and
the getaddr and addr messages to allow peers to communicate known addresses with
each other. However, there needs to a mechanism to store those results and
select peers from them. It is also important to note that remote peers can't
be trusted to send valid peers nor attempt to provide you with only peers they
control with malicious intent.
With that in mind, this package provides a concurrency safe address manager for
caching and selecting peers in a non-determinstic manner. The general idea is
caching and selecting peers in a non-deterministic manner. The general idea is
the caller adds addresses to the address manager and notifies it when addresses
are connected, known good, and attempted. The caller also requests addresses as
it needs them.
@@ -28,11 +28,11 @@ generally helps provide greater peer diversity, and perhaps more importantly,
drastically reduces the chances an attacker is able to coerce your peer into
only connecting to nodes they control.
The address manager also understands routability and tor addresses and tries
The address manager also understands routability and Tor addresses and tries
hard to only return routable addresses. In addition, it uses the information
provided by the caller about connected, known good, and attempted addresses to
periodically purge peers which no longer appear to be good peers as well as
bias the selection toward known good peers. The general idea is to make a best
effort at only providing usuable addresses.
effort at only providing usable addresses.
*/
package addrmgr

View File

@@ -7,7 +7,7 @@ package addrmgr
import (
"time"
"github.com/btcsuite/btcd/wire"
"github.com/kaspanet/kaspad/wire"
)
func TstKnownAddressIsBad(ka *KnownAddress) bool {

View File

@@ -7,19 +7,22 @@ package addrmgr
import (
"time"
"github.com/btcsuite/btcd/wire"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/kaspanet/kaspad/wire"
)
// KnownAddress tracks information about a known network address that is used
// to determine how viable an address is.
type KnownAddress struct {
na *wire.NetAddress
srcAddr *wire.NetAddress
attempts int
lastattempt time.Time
lastsuccess time.Time
tried bool
refs int // reference count of new buckets
na *wire.NetAddress
srcAddr *wire.NetAddress
attempts int
lastattempt time.Time
lastsuccess time.Time
tried bool
refs int // reference count of new buckets
subnetworkID *subnetworkid.SubnetworkID
}
// NetAddress returns the underlying wire.NetAddress associated with the
@@ -28,6 +31,11 @@ func (ka *KnownAddress) NetAddress() *wire.NetAddress {
return ka.na
}
// SubnetworkID returns the subnetwork ID of the known address.
func (ka *KnownAddress) SubnetworkID() *subnetworkid.SubnetworkID {
return ka.subnetworkID
}
// LastAttempt returns the last time the known address was attempted.
func (ka *KnownAddress) LastAttempt() time.Time {
return ka.lastattempt
@@ -38,12 +46,8 @@ func (ka *KnownAddress) LastAttempt() time.Time {
// attempted and how often attempts to connect to it have failed.
func (ka *KnownAddress) chance() float64 {
now := time.Now()
lastSeen := now.Sub(ka.na.Timestamp)
lastAttempt := now.Sub(ka.lastattempt)
if lastSeen < 0 {
lastSeen = 0
}
if lastAttempt < 0 {
lastAttempt = 0
}

View File

@@ -9,38 +9,39 @@ import (
"testing"
"time"
"github.com/btcsuite/btcd/addrmgr"
"github.com/btcsuite/btcd/wire"
"github.com/kaspanet/kaspad/addrmgr"
"github.com/kaspanet/kaspad/wire"
)
func TestChance(t *testing.T) {
now := time.Unix(time.Now().Unix(), 0)
var tests = []struct {
addr *addrmgr.KnownAddress
expected float64
}{
{
//Test normal case
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(-35 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
1.0,
}, {
//Test case in which lastseen < 0
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(20 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(20 * time.Second)},
0, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
1.0,
}, {
//Test case in which lastattempt < 0
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(-35 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, time.Now().Add(30*time.Minute), time.Now(), false, 0),
1.0 * .01,
}, {
//Test case in which lastattempt < ten minutes
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(-35 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, time.Now().Add(-5*time.Minute), time.Now(), false, 0),
1.0 * .01,
}, {
//Test case with several failed attempts.
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(-35 * time.Second)},
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
2, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
1 / 1.5 / 1.5,
},
@@ -56,12 +57,13 @@ func TestChance(t *testing.T) {
}
func TestIsBad(t *testing.T) {
future := time.Now().Add(35 * time.Minute)
monthOld := time.Now().Add(-43 * time.Hour * 24)
secondsOld := time.Now().Add(-2 * time.Second)
minutesOld := time.Now().Add(-27 * time.Minute)
hoursOld := time.Now().Add(-5 * time.Hour)
zeroTime, _ := time.Parse("Jan 1, 1970 at 0:00am (GMT)", "Jan 1, 1970 at 0:00am (GMT)")
now := time.Unix(time.Now().Unix(), 0)
future := now.Add(35 * time.Minute)
monthOld := now.Add(-43 * time.Hour * 24)
secondsOld := now.Add(-2 * time.Second)
minutesOld := now.Add(-27 * time.Minute)
hoursOld := now.Add(-5 * time.Hour)
zeroTime := time.Time{}
futureNa := &wire.NetAddress{Timestamp: future}
minutesOldNa := &wire.NetAddress{Timestamp: minutesOld}

View File

@@ -5,28 +5,9 @@
package addrmgr
import (
"github.com/btcsuite/btclog"
"github.com/kaspanet/kaspad/logger"
"github.com/kaspanet/kaspad/util/panics"
)
// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
// requests it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
DisableLog()
}
// DisableLog disables all library log output. Logging output is disabled
// by default until either UseLogger or SetLogWriter are called.
func DisableLog() {
log = btclog.Disabled
}
// UseLogger uses a specified Logger to output package logging info.
// This should be used in preference to SetLogWriter if the caller is also
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}
var log, _ = logger.Get(logger.SubsystemTags.ADXR)
var spawn = panics.GoroutineWrapperFuncWithPanicHandler(log)

View File

@@ -8,7 +8,9 @@ import (
"fmt"
"net"
"github.com/btcsuite/btcd/wire"
"github.com/kaspanet/kaspad/config"
"github.com/kaspanet/kaspad/wire"
)
var (
@@ -223,6 +225,10 @@ func IsValid(na *wire.NetAddress) bool {
// the public internet. This is true as long as the address is valid and is not
// in any reserved ranges.
func IsRoutable(na *wire.NetAddress) bool {
if config.ActiveConfig().NetParams().AcceptUnroutable {
return !IsLocal(na)
}
return IsValid(na) && !(IsRFC1918(na) || IsRFC2544(na) ||
IsRFC3927(na) || IsRFC4862(na) || IsRFC3849(na) ||
IsRFC4843(na) || IsRFC5737(na) || IsRFC6598(na) ||
@@ -232,7 +238,7 @@ func IsRoutable(na *wire.NetAddress) bool {
// GroupKey returns a string representing the network group an address is part
// of. This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string
// "local" for a local address, the string "tor:key" where key is the /4 of the
// onion address for tor address, and the string "unroutable" for an unroutable
// onion address for Tor address, and the string "unroutable" for an unroutable
// address.
func GroupKey(na *wire.NetAddress) string {
if IsLocal(na) {
@@ -246,12 +252,12 @@ func GroupKey(na *wire.NetAddress) string {
}
if IsRFC6145(na) || IsRFC6052(na) {
// last four bytes are the ip address
ip := net.IP(na.IP[12:16])
ip := na.IP[12:16]
return ip.Mask(net.CIDRMask(16, 32)).String()
}
if IsRFC3964(na) {
ip := net.IP(na.IP[2:6])
ip := na.IP[2:6]
return ip.Mask(net.CIDRMask(16, 32)).String()
}

View File

@@ -5,17 +5,29 @@
package addrmgr_test
import (
"bou.ke/monkey"
"github.com/kaspanet/kaspad/config"
"github.com/kaspanet/kaspad/dagconfig"
"net"
"testing"
"time"
"github.com/btcsuite/btcd/addrmgr"
"github.com/btcsuite/btcd/wire"
"github.com/kaspanet/kaspad/addrmgr"
"github.com/kaspanet/kaspad/wire"
)
// TestIPTypes ensures the various functions which determine the type of an IP
// address based on RFCs work as intended.
func TestIPTypes(t *testing.T) {
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
type ipTest struct {
in wire.NetAddress
rfc1918 bool
@@ -40,12 +52,7 @@ func TestIPTypes(t *testing.T) {
rfc4193, rfc4380, rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598,
local, valid, routable bool) ipTest {
nip := net.ParseIP(ip)
na := wire.NetAddress{
Timestamp: time.Now(),
Services: wire.SFNodeNetwork,
IP: nip,
Port: 8333,
}
na := *wire.NewNetAddressIPPort(nip, 8333, wire.SFNodeNetwork)
test := ipTest{na, rfc1918, rfc2544, rfc3849, rfc3927, rfc3964, rfc4193, rfc4380,
rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598, local, valid, routable}
return test
@@ -151,6 +158,16 @@ func TestIPTypes(t *testing.T) {
// TestGroupKey tests the GroupKey function to ensure it properly groups various
// IP addresses.
func TestGroupKey(t *testing.T) {
activeConfigPatch := monkey.Patch(config.ActiveConfig, func() *config.Config {
return &config.Config{
Flags: &config.Flags{
NetworkFlags: config.NetworkFlags{
ActiveNetParams: &dagconfig.SimNetParams},
},
}
})
defer activeConfigPatch.Unpatch()
tests := []struct {
name string
ip string
@@ -198,12 +215,7 @@ func TestGroupKey(t *testing.T) {
for i, test := range tests {
nip := net.ParseIP(test.ip)
na := wire.NetAddress{
Timestamp: time.Now(),
Services: wire.SFNodeNetwork,
IP: nip,
Port: 8333,
}
na := *wire.NewNetAddressIPPort(nip, 8333, wire.SFNodeNetwork)
if key := addrmgr.GroupKey(&na); key != test.expected {
t.Errorf("TestGroupKey #%d (%s): unexpected group key "+
"- got '%s', want '%s'", i, test.name,

View File

@@ -1,183 +0,0 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"fmt"
"github.com/btcsuite/btcutil"
)
// maybeAcceptBlock potentially accepts a block into the memory block chain.
// It performs several validation checks which depend on its position within
// the block chain before adding it. The block is expected to have already gone
// through ProcessBlock before calling this function with it.
//
// The flags modify the behavior of this function as follows:
// - BFFastAdd: The somewhat expensive BIP0034 validation is not performed.
// - BFDryRun: The memory chain index will not be pruned and no accept
// notification will be sent since the block is not being accepted.
func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error {
fastAdd := flags&BFFastAdd == BFFastAdd
dryRun := flags&BFDryRun == BFDryRun
// Get a block node for the block previous to this one. Will be nil
// if this is the genesis block.
prevNode, err := b.getPrevNodeFromBlock(block)
if err != nil {
log.Errorf("getPrevNodeFromBlock: %v", err)
return err
}
// The height of this block is one more than the referenced previous
// block.
blockHeight := int64(0)
if prevNode != nil {
blockHeight = prevNode.height + 1
}
block.SetHeight(blockHeight)
blockHeader := &block.MsgBlock().Header
if !fastAdd {
// Ensure the difficulty specified in the block header matches
// the calculated difficulty based on the previous block and
// difficulty retarget rules.
expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode,
block.MsgBlock().Header.Timestamp)
if err != nil {
return err
}
blockDifficulty := blockHeader.Bits
if blockDifficulty != expectedDifficulty {
str := "block difficulty of %d is not the expected value of %d"
str = fmt.Sprintf(str, blockDifficulty, expectedDifficulty)
return ruleError(ErrUnexpectedDifficulty, str)
}
// Ensure the timestamp for the block header is after the
// median time of the last several blocks (medianTimeBlocks).
medianTime, err := b.calcPastMedianTime(prevNode)
if err != nil {
log.Errorf("calcPastMedianTime: %v", err)
return err
}
if !blockHeader.Timestamp.After(medianTime) {
str := "block timestamp of %v is not after expected %v"
str = fmt.Sprintf(str, blockHeader.Timestamp,
medianTime)
return ruleError(ErrTimeTooOld, str)
}
// Ensure all transactions in the block are finalized.
for _, tx := range block.Transactions() {
if !IsFinalizedTransaction(tx, blockHeight,
blockHeader.Timestamp) {
str := fmt.Sprintf("block contains "+
"unfinalized transaction %v", tx.Sha())
return ruleError(ErrUnfinalizedTx, str)
}
}
}
// Ensure chain matches up to predetermined checkpoints.
blockHash := block.Sha()
if !b.verifyCheckpoint(blockHeight, blockHash) {
str := fmt.Sprintf("block at height %d does not match "+
"checkpoint hash", blockHeight)
return ruleError(ErrBadCheckpoint, str)
}
// Find the previous checkpoint and prevent blocks which fork the main
// chain before it. This prevents storage of new, otherwise valid,
// blocks which build off of old blocks that are likely at a much easier
// difficulty and therefore could be used to waste cache and disk space.
checkpointBlock, err := b.findPreviousCheckpoint()
if err != nil {
return err
}
if checkpointBlock != nil && blockHeight < checkpointBlock.Height() {
str := fmt.Sprintf("block at height %d forks the main chain "+
"before the previous checkpoint at height %d",
blockHeight, checkpointBlock.Height())
return ruleError(ErrForkTooOld, str)
}
if !fastAdd {
// Reject version 2 blocks once a majority of the network has
// upgraded. This is part of BIP0066.
if blockHeader.Version < 3 && b.isMajorityVersion(3, prevNode,
b.chainParams.BlockRejectNumRequired) {
str := "new blocks with version %d are no longer valid"
str = fmt.Sprintf(str, blockHeader.Version)
return ruleError(ErrBlockVersionTooOld, str)
}
// Reject version 1 blocks once a majority of the network has
// upgraded. This is part of BIP0034.
if blockHeader.Version < 2 && b.isMajorityVersion(2, prevNode,
b.chainParams.BlockRejectNumRequired) {
str := "new blocks with version %d are no longer valid"
str = fmt.Sprintf(str, blockHeader.Version)
return ruleError(ErrBlockVersionTooOld, str)
}
// Ensure coinbase starts with serialized block heights for
// blocks whose version is the serializedHeightVersion or
// newer once a majority of the network has upgraded. This is
// part of BIP0034.
if ShouldHaveSerializedBlockHeight(blockHeader) &&
b.isMajorityVersion(serializedHeightVersion, prevNode,
b.chainParams.BlockEnforceNumRequired) {
expectedHeight := int64(0)
if prevNode != nil {
expectedHeight = prevNode.height + 1
}
coinbaseTx := block.Transactions()[0]
err := checkSerializedHeight(coinbaseTx, expectedHeight)
if err != nil {
return err
}
}
}
// Prune block nodes which are no longer needed before creating
// a new node.
if !dryRun {
err = b.pruneBlockNodes()
if err != nil {
return err
}
}
// Create a new block node for the block and add it to the in-memory
// block chain (could be either a side chain or the main chain).
newNode := newBlockNode(blockHeader, blockHash, blockHeight)
if prevNode != nil {
newNode.parent = prevNode
newNode.height = blockHeight
newNode.workSum.Add(prevNode.workSum, newNode.workSum)
}
// Connect the passed block to the chain while respecting proper chain
// selection according to the chain with the most proof of work. This
// also handles validation of the transaction scripts.
err = b.connectBestChain(newNode, block, flags)
if err != nil {
return err
}
// Notify the caller that the new block was accepted into the block
// chain. The caller would typically want to react by relaying the
// inventory to other peers.
if !dryRun {
b.sendNotification(NTBlockAccepted, block)
}
return nil
}

View File

@@ -1,32 +0,0 @@
// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"testing"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcutil"
)
// BenchmarkIsCoinBase performs a simple benchmark against the IsCoinBase
// function.
func BenchmarkIsCoinBase(b *testing.B) {
tx, _ := btcutil.NewBlock(&Block100000).Tx(1)
b.ResetTimer()
for i := 0; i < b.N; i++ {
blockchain.IsCoinBase(tx)
}
}
// BenchmarkIsCoinBaseTx performs a simple benchmark against the IsCoinBaseTx
// function.
func BenchmarkIsCoinBaseTx(b *testing.B) {
tx := Block100000.Transactions[1]
b.ResetTimer()
for i := 0; i < b.N; i++ {
blockchain.IsCoinBaseTx(tx)
}
}

View File

@@ -1,149 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"github.com/btcsuite/btcd/wire"
)
// BlockLocator is used to help locate a specific block. The algorithm for
// building the block locator is to add the hashes in reverse order until
// the genesis block is reached. In order to keep the list of locator hashes
// to a reasonable number of entries, first the most recent previous 10 block
// hashes are added, then the step is doubled each loop iteration to
// exponentially decrease the number of hashes as a function of the distance
// from the block being located.
//
// For example, assume you have a block chain with a side chain as depicted
// below:
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
// \-> 16a -> 17a
//
// The block locator for block 17a would be the hashes of blocks:
// [17a 16a 15 14 13 12 11 10 9 8 6 2 genesis]
type BlockLocator []*wire.ShaHash
// BlockLocatorFromHash returns a block locator for the passed block hash.
// See BlockLocator for details on the algotirhm used to create a block locator.
//
// In addition to the general algorithm referenced above, there are a couple of
// special cases which are handled:
//
// - If the genesis hash is passed, there are no previous hashes to add and
// therefore the block locator will only consist of the genesis hash
// - If the passed hash is not currently known, the block locator will only
// consist of the passed hash
func (b *BlockChain) BlockLocatorFromHash(hash *wire.ShaHash) BlockLocator {
// The locator contains the requested hash at the very least.
locator := make(BlockLocator, 0, wire.MaxBlockLocatorsPerMsg)
locator = append(locator, hash)
// Nothing more to do if a locator for the genesis hash was requested.
if hash.IsEqual(b.chainParams.GenesisHash) {
return locator
}
// Attempt to find the height of the block that corresponds to the
// passed hash, and if it's on a side chain, also find the height at
// which it forks from the main chain.
blockHeight := int64(-1)
forkHeight := int64(-1)
node, exists := b.index[*hash]
if !exists {
// Try to look up the height for passed block hash. Assume an
// error means it doesn't exist and just return the locator for
// the block itself.
height, err := b.db.FetchBlockHeightBySha(hash)
if err != nil {
return locator
}
blockHeight = height
} else {
blockHeight = node.height
// Find the height at which this node forks from the main chain
// if the node is on a side chain.
if !node.inMainChain {
for n := node; n.parent != nil; n = n.parent {
if n.inMainChain {
forkHeight = n.height
break
}
}
}
}
// Generate the block locators according to the algorithm described in
// in the BlockLocator comment and make sure to leave room for the
// final genesis hash.
iterNode := node
increment := int64(1)
for len(locator) < wire.MaxBlockLocatorsPerMsg-1 {
// Once there are 10 locators, exponentially increase the
// distance between each block locator.
if len(locator) > 10 {
increment *= 2
}
blockHeight -= increment
if blockHeight < 1 {
break
}
// As long as this is still on the side chain, walk backwards
// along the side chain nodes to each block height.
if forkHeight != -1 && blockHeight > forkHeight {
// Intentionally use parent field instead of the
// getPrevNodeFromNode function since we don't want to
// dynamically load nodes when building block locators.
// Side chain blocks should always be in memory already,
// and if they aren't for some reason it's ok to skip
// them.
for iterNode != nil && blockHeight > iterNode.height {
iterNode = iterNode.parent
}
if iterNode != nil && iterNode.height == blockHeight {
locator = append(locator, iterNode.hash)
}
continue
}
// The desired block height is in the main chain, so look it up
// from the main chain database.
h, err := b.db.FetchBlockShaByHeight(blockHeight)
if err != nil {
// This shouldn't happen and it's ok to ignore block
// locators, so just continue to the next one.
log.Warnf("Lookup of known valid height failed %v",
blockHeight)
continue
}
locator = append(locator, h)
}
// Append the appropriate genesis block.
locator = append(locator, b.chainParams.GenesisHash)
return locator
}
// LatestBlockLocator returns a block locator for the latest known tip of the
// main (best) chain.
func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
// Lookup the latest main chain hash if the best chain hasn't been set
// yet.
if b.bestChain == nil {
// Get the latest block hash for the main chain from the
// database.
hash, _, err := b.db.NewestSha()
if err != nil {
return nil, err
}
return b.BlockLocatorFromHash(hash), nil
}
// The best chain is set, so use its hash.
return b.BlockLocatorFromHash(b.bestChain.hash), nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"testing"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// TestHaveBlock tests the HaveBlock API to ensure proper functionality.
func TestHaveBlock(t *testing.T) {
// Load up blocks such that there is a side chain.
// (genesis block) -> 1 -> 2 -> 3 -> 4
// \-> 3a
testFiles := []string{
"blk_0_to_4.dat.bz2",
"blk_3A.dat.bz2",
}
var blocks []*btcutil.Block
for _, file := range testFiles {
blockTmp, err := loadBlocks(file)
if err != nil {
t.Errorf("Error loading file: %v\n", err)
return
}
for _, block := range blockTmp {
blocks = append(blocks, block)
}
}
// Create a new database and chain instance to run tests against.
chain, teardownFunc, err := chainSetup("haveblock")
if err != nil {
t.Errorf("Failed to setup chain instance: %v", err)
return
}
defer teardownFunc()
// Since we're not dealing with the real block chain, disable
// checkpoints and set the coinbase maturity to 1.
chain.DisableCheckpoints(true)
blockchain.TstSetCoinbaseMaturity(1)
timeSource := blockchain.NewMedianTime()
for i := 1; i < len(blocks); i++ {
isOrphan, err := chain.ProcessBlock(blocks[i], timeSource,
blockchain.BFNone)
if err != nil {
t.Errorf("ProcessBlock fail on block %v: %v\n", i, err)
return
}
if isOrphan {
t.Errorf("ProcessBlock incorrectly returned block %v "+
"is an orphan\n", i)
return
}
}
// Insert an orphan block.
isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000),
timeSource, blockchain.BFNone)
if err != nil {
t.Errorf("Unable to process block: %v", err)
return
}
if !isOrphan {
t.Errorf("ProcessBlock indicated block is an not orphan when " +
"it should be\n")
return
}
tests := []struct {
hash string
want bool
}{
// Genesis block should be present (in the main chain).
{hash: chaincfg.MainNetParams.GenesisHash.String(), want: true},
// Block 3a should be present (on a side chain).
{hash: "00000000474284d20067a4d33f6a02284e6ef70764a3a26d6a5b9df52ef663dd", want: true},
// Block 100000 should be present (as an orphan).
{hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506", want: true},
// Random hashes should not be availble.
{hash: "123", want: false},
}
for i, test := range tests {
hash, err := wire.NewShaHashFromStr(test.hash)
if err != nil {
t.Errorf("NewShaHashFromStr: %v", err)
continue
}
result, err := chain.HaveBlock(hash)
if err != nil {
t.Errorf("HaveBlock #%d unexpected error: %v", i, err)
return
}
if result != test.want {
t.Errorf("HaveBlock #%d got %v want %v", i, result,
test.want)
continue
}
}
}

View File

@@ -1,280 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// CheckpointConfirmations is the number of blocks before the end of the current
// best block chain that a good checkpoint candidate must be.
const CheckpointConfirmations = 2016
// newShaHashFromStr converts the passed big-endian hex string into a
// wire.ShaHash. It only differs from the one available in wire in that
// it ignores the error since it will only (and must only) be called with
// hard-coded, and therefore known good, hashes.
func newShaHashFromStr(hexStr string) *wire.ShaHash {
sha, _ := wire.NewShaHashFromStr(hexStr)
return sha
}
// DisableCheckpoints provides a mechanism to disable validation against
// checkpoints which you DO NOT want to do in production. It is provided only
// for debug purposes.
func (b *BlockChain) DisableCheckpoints(disable bool) {
b.noCheckpoints = disable
}
// Checkpoints returns a slice of checkpoints (regardless of whether they are
// already known). When checkpoints are disabled or there are no checkpoints
// for the active network, it will return nil.
func (b *BlockChain) Checkpoints() []chaincfg.Checkpoint {
if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 {
return nil
}
return b.chainParams.Checkpoints
}
// LatestCheckpoint returns the most recent checkpoint (regardless of whether it
// is already known). When checkpoints are disabled or there are no checkpoints
// for the active network, it will return nil.
func (b *BlockChain) LatestCheckpoint() *chaincfg.Checkpoint {
if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 {
return nil
}
checkpoints := b.chainParams.Checkpoints
return &checkpoints[len(checkpoints)-1]
}
// verifyCheckpoint returns whether the passed block height and hash combination
// match the hard-coded checkpoint data. It also returns true if there is no
// checkpoint data for the passed block height.
func (b *BlockChain) verifyCheckpoint(height int64, hash *wire.ShaHash) bool {
if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 {
return true
}
// Nothing to check if there is no checkpoint data for the block height.
checkpoint, exists := b.checkpointsByHeight[height]
if !exists {
return true
}
if !checkpoint.Hash.IsEqual(hash) {
return false
}
log.Infof("Verified checkpoint at height %d/block %s", checkpoint.Height,
checkpoint.Hash)
return true
}
// findPreviousCheckpoint finds the most recent checkpoint that is already
// available in the downloaded portion of the block chain and returns the
// associated block. It returns nil if a checkpoint can't be found (this should
// really only happen for blocks before the first checkpoint).
func (b *BlockChain) findPreviousCheckpoint() (*btcutil.Block, error) {
if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 {
return nil, nil
}
// No checkpoints.
checkpoints := b.chainParams.Checkpoints
numCheckpoints := len(checkpoints)
if numCheckpoints == 0 {
return nil, nil
}
// Perform the initial search to find and cache the latest known
// checkpoint if the best chain is not known yet or we haven't already
// previously searched.
if b.bestChain == nil || (b.checkpointBlock == nil && b.nextCheckpoint == nil) {
// Loop backwards through the available checkpoints to find one
// that we already have.
checkpointIndex := -1
for i := numCheckpoints - 1; i >= 0; i-- {
exists, err := b.db.ExistsSha(checkpoints[i].Hash)
if err != nil {
return nil, err
}
if exists {
checkpointIndex = i
break
}
}
// No known latest checkpoint. This will only happen on blocks
// before the first known checkpoint. So, set the next expected
// checkpoint to the first checkpoint and return the fact there
// is no latest known checkpoint block.
if checkpointIndex == -1 {
b.nextCheckpoint = &checkpoints[0]
return nil, nil
}
// Cache the latest known checkpoint block for future lookups.
checkpoint := checkpoints[checkpointIndex]
block, err := b.db.FetchBlockBySha(checkpoint.Hash)
if err != nil {
return nil, err
}
b.checkpointBlock = block
// Set the next expected checkpoint block accordingly.
b.nextCheckpoint = nil
if checkpointIndex < numCheckpoints-1 {
b.nextCheckpoint = &checkpoints[checkpointIndex+1]
}
return block, nil
}
// At this point we've already searched for the latest known checkpoint,
// so when there is no next checkpoint, the current checkpoint lockin
// will always be the latest known checkpoint.
if b.nextCheckpoint == nil {
return b.checkpointBlock, nil
}
// When there is a next checkpoint and the height of the current best
// chain does not exceed it, the current checkpoint lockin is still
// the latest known checkpoint.
if b.bestChain.height < b.nextCheckpoint.Height {
return b.checkpointBlock, nil
}
// We've reached or exceeded the next checkpoint height. Note that
// once a checkpoint lockin has been reached, forks are prevented from
// any blocks before the checkpoint, so we don't have to worry about the
// checkpoint going away out from under us due to a chain reorganize.
// Cache the latest known checkpoint block for future lookups. Note
// that if this lookup fails something is very wrong since the chain
// has already passed the checkpoint which was verified as accurate
// before inserting it.
block, err := b.db.FetchBlockBySha(b.nextCheckpoint.Hash)
if err != nil {
return nil, err
}
b.checkpointBlock = block
// Set the next expected checkpoint.
checkpointIndex := -1
for i := numCheckpoints - 1; i >= 0; i-- {
if checkpoints[i].Hash.IsEqual(b.nextCheckpoint.Hash) {
checkpointIndex = i
break
}
}
b.nextCheckpoint = nil
if checkpointIndex != -1 && checkpointIndex < numCheckpoints-1 {
b.nextCheckpoint = &checkpoints[checkpointIndex+1]
}
return b.checkpointBlock, nil
}
// isNonstandardTransaction determines whether a transaction contains any
// scripts which are not one of the standard types.
func isNonstandardTransaction(tx *btcutil.Tx) bool {
// TODO(davec): Should there be checks for the input signature scripts?
// Check all of the output public key scripts for non-standard scripts.
for _, txOut := range tx.MsgTx().TxOut {
scriptClass := txscript.GetScriptClass(txOut.PkScript)
if scriptClass == txscript.NonStandardTy {
return true
}
}
return false
}
// IsCheckpointCandidate returns whether or not the passed block is a good
// checkpoint candidate.
//
// The factors used to determine a good checkpoint are:
// - The block must be in the main chain
// - The block must be at least 'CheckpointConfirmations' blocks prior to the
// current end of the main chain
// - The timestamps for the blocks before and after the checkpoint must have
// timestamps which are also before and after the checkpoint, respectively
// (due to the median time allowance this is not always the case)
// - The block must not contain any strange transaction such as those with
// nonstandard scripts
//
// The intent is that candidates are reviewed by a developer to make the final
// decision and then manually added to the list of checkpoints for a network.
func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) {
// Checkpoints must be enabled.
if b.noCheckpoints {
return false, fmt.Errorf("checkpoints are disabled")
}
// A checkpoint must be in the main chain.
exists, err := b.db.ExistsSha(block.Sha())
if err != nil {
return false, err
}
if !exists {
return false, nil
}
// A checkpoint must be at least CheckpointConfirmations blocks before
// the end of the main chain.
blockHeight := block.Height()
_, mainChainHeight, err := b.db.NewestSha()
if err != nil {
return false, err
}
if blockHeight > (mainChainHeight - CheckpointConfirmations) {
return false, nil
}
// Get the previous block.
prevHash := &block.MsgBlock().Header.PrevBlock
prevBlock, err := b.db.FetchBlockBySha(prevHash)
if err != nil {
return false, err
}
// Get the next block.
nextHash, err := b.db.FetchBlockShaByHeight(blockHeight + 1)
if err != nil {
return false, err
}
nextBlock, err := b.db.FetchBlockBySha(nextHash)
if err != nil {
return false, err
}
// A checkpoint must have timestamps for the block and the blocks on
// either side of it in order (due to the median time allowance this is
// not always the case).
prevTime := prevBlock.MsgBlock().Header.Timestamp
curTime := block.MsgBlock().Header.Timestamp
nextTime := nextBlock.MsgBlock().Header.Timestamp
if prevTime.After(curTime) || nextTime.Before(curTime) {
return false, nil
}
// A checkpoint must have transactions that only contain standard
// scripts.
for _, tx := range block.Transactions() {
if isNonstandardTransaction(tx) {
return false, nil
}
}
return true, nil
}

View File

@@ -1,223 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"compress/bzip2"
"encoding/binary"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/database"
_ "github.com/btcsuite/btcd/database/ldb"
_ "github.com/btcsuite/btcd/database/memdb"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// testDbType is the database backend type to use for the tests.
const testDbType = "memdb"
// testDbRoot is the root directory used to create all test databases.
const testDbRoot = "testdbs"
// filesExists returns whether or not the named file or directory exists.
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// isSupportedDbType returns whether or not the passed database type is
// currently supported.
func isSupportedDbType(dbType string) bool {
supportedDBs := database.SupportedDBs()
for _, sDbType := range supportedDBs {
if dbType == sDbType {
return true
}
}
return false
}
// chainSetup is used to create a new db and chain instance with the genesis
// block already inserted. In addition to the new chain instnce, it returns
// a teardown function the caller should invoke when done testing to clean up.
func chainSetup(dbName string) (*blockchain.BlockChain, func(), error) {
if !isSupportedDbType(testDbType) {
return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
}
// Handle memory database specially since it doesn't need the disk
// specific handling.
var db database.Db
var teardown func()
if testDbType == "memdb" {
ndb, err := database.CreateDB(testDbType)
if err != nil {
return nil, nil, fmt.Errorf("error creating db: %v", err)
}
db = ndb
// Setup a teardown function for cleaning up. This function is
// returned to the caller to be invoked when it is done testing.
teardown = func() {
db.Close()
}
} else {
// Create the root directory for test databases.
if !fileExists(testDbRoot) {
if err := os.MkdirAll(testDbRoot, 0700); err != nil {
err := fmt.Errorf("unable to create test db "+
"root: %v", err)
return nil, nil, err
}
}
// Create a new database to store the accepted blocks into.
dbPath := filepath.Join(testDbRoot, dbName)
_ = os.RemoveAll(dbPath)
ndb, err := database.CreateDB(testDbType, dbPath)
if err != nil {
return nil, nil, fmt.Errorf("error creating db: %v", err)
}
db = ndb
// Setup a teardown function for cleaning up. This function is
// returned to the caller to be invoked when it is done testing.
teardown = func() {
dbVersionPath := filepath.Join(testDbRoot, dbName+".ver")
db.Sync()
db.Close()
os.RemoveAll(dbPath)
os.Remove(dbVersionPath)
os.RemoveAll(testDbRoot)
}
}
// Insert the main network genesis block. This is part of the initial
// database setup.
genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock)
_, err := db.InsertBlock(genesisBlock)
if err != nil {
teardown()
err := fmt.Errorf("failed to insert genesis block: %v", err)
return nil, nil, err
}
chain := blockchain.New(db, &chaincfg.MainNetParams, nil)
return chain, teardown, nil
}
// loadTxStore returns a transaction store loaded from a file.
func loadTxStore(filename string) (blockchain.TxStore, error) {
// The txstore file format is:
// <num tx data entries> <tx length> <serialized tx> <blk height>
// <num spent bits> <spent bits>
//
// All num and length fields are little-endian uint32s. The spent bits
// field is padded to a byte boundary.
filename = filepath.Join("testdata/", filename)
fi, err := os.Open(filename)
if err != nil {
return nil, err
}
// Choose read based on whether the file is compressed or not.
var r io.Reader
if strings.HasSuffix(filename, ".bz2") {
r = bzip2.NewReader(fi)
} else {
r = fi
}
defer fi.Close()
// Num of transaction store objects.
var numItems uint32
if err := binary.Read(r, binary.LittleEndian, &numItems); err != nil {
return nil, err
}
txStore := make(blockchain.TxStore)
var uintBuf uint32
for height := uint32(0); height < numItems; height++ {
txD := blockchain.TxData{}
// Serialized transaction length.
err = binary.Read(r, binary.LittleEndian, &uintBuf)
if err != nil {
return nil, err
}
serializedTxLen := uintBuf
if serializedTxLen > wire.MaxBlockPayload {
return nil, fmt.Errorf("Read serialized transaction "+
"length of %d is larger max allowed %d",
serializedTxLen, wire.MaxBlockPayload)
}
// Transaction.
var msgTx wire.MsgTx
err = msgTx.Deserialize(r)
if err != nil {
return nil, err
}
txD.Tx = btcutil.NewTx(&msgTx)
// Transaction hash.
txHash := msgTx.TxSha()
txD.Hash = &txHash
// Block height the transaction came from.
err = binary.Read(r, binary.LittleEndian, &uintBuf)
if err != nil {
return nil, err
}
txD.BlockHeight = int64(uintBuf)
// Num spent bits.
err = binary.Read(r, binary.LittleEndian, &uintBuf)
if err != nil {
return nil, err
}
numSpentBits := uintBuf
numSpentBytes := numSpentBits / 8
if numSpentBits%8 != 0 {
numSpentBytes++
}
// Packed spent bytes.
spentBytes := make([]byte, numSpentBytes)
_, err = io.ReadFull(r, spentBytes)
if err != nil {
return nil, err
}
// Populate spent data based on spent bits.
txD.Spent = make([]bool, numSpentBits)
for byteNum, spentByte := range spentBytes {
for bit := 0; bit < 8; bit++ {
if uint32((byteNum*8)+bit) < numSpentBits {
if spentByte&(1<<uint(bit)) != 0 {
txD.Spent[(byteNum*8)+bit] = true
}
}
}
}
txStore[*txD.Hash] = &txD
}
return txStore, nil
}

View File

@@ -1,361 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"fmt"
"math/big"
"time"
"github.com/btcsuite/btcd/wire"
)
const (
// targetTimespan is the desired amount of time that should elapse
// before block difficulty requirement is examined to determine how
// it should be changed in order to maintain the desired block
// generation rate.
targetTimespan = time.Hour * 24 * 14
// targetSpacing is the desired amount of time to generate each block.
targetSpacing = time.Minute * 10
// BlocksPerRetarget is the number of blocks between each difficulty
// retarget. It is calculated based on the desired block generation
// rate.
BlocksPerRetarget = int64(targetTimespan / targetSpacing)
// retargetAdjustmentFactor is the adjustment factor used to limit
// the minimum and maximum amount of adjustment that can occur between
// difficulty retargets.
retargetAdjustmentFactor = 4
// minRetargetTimespan is the minimum amount of adjustment that can
// occur between difficulty retargets. It equates to 25% of the
// previous difficulty.
minRetargetTimespan = int64(targetTimespan / retargetAdjustmentFactor)
// maxRetargetTimespan is the maximum amount of adjustment that can
// occur between difficulty retargets. It equates to 400% of the
// previous difficulty.
maxRetargetTimespan = int64(targetTimespan * retargetAdjustmentFactor)
)
var (
// bigOne is 1 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigOne = big.NewInt(1)
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
// the overhead of creating it multiple times.
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
)
// ShaHashToBig converts a wire.ShaHash into a big.Int that can be used to
// perform math comparisons.
func ShaHashToBig(hash *wire.ShaHash) *big.Int {
// A ShaHash is in little-endian, but the big package wants the bytes
// in big-endian, so reverse them.
buf := *hash
blen := len(buf)
for i := 0; i < blen/2; i++ {
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
}
return new(big.Int).SetBytes(buf[:])
}
// CompactToBig converts a compact representation of a whole number N to an
// unsigned 32-bit number. The representation is similar to IEEE754 floating
// point numbers.
//
// Like IEEE754 floating point, there are three basic components: the sign,
// the exponent, and the mantissa. They are broken out as follows:
//
// * the most significant 8 bits represent the unsigned base 256 exponent
// * bit 23 (the 24th bit) represents the sign bit
// * the least significant 23 bits represent the mantissa
//
// -------------------------------------------------
// | Exponent | Sign | Mantissa |
// -------------------------------------------------
// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] |
// -------------------------------------------------
//
// The formula to calculate N is:
// N = (-1^sign) * mantissa * 256^(exponent-3)
//
// This compact form is only used in bitcoin to encode unsigned 256-bit numbers
// which represent difficulty targets, thus there really is not a need for a
// sign bit, but it is implemented here to stay consistent with bitcoind.
func CompactToBig(compact uint32) *big.Int {
// Extract the mantissa, sign bit, and exponent.
mantissa := compact & 0x007fffff
isNegative := compact&0x00800000 != 0
exponent := uint(compact >> 24)
// Since the base for the exponent is 256, the exponent can be treated
// as the number of bytes to represent the full 256-bit number. So,
// treat the exponent as the number of bytes and shift the mantissa
// right or left accordingly. This is equivalent to:
// N = mantissa * 256^(exponent-3)
var bn *big.Int
if exponent <= 3 {
mantissa >>= 8 * (3 - exponent)
bn = big.NewInt(int64(mantissa))
} else {
bn = big.NewInt(int64(mantissa))
bn.Lsh(bn, 8*(exponent-3))
}
// Make it negative if the sign bit is set.
if isNegative {
bn = bn.Neg(bn)
}
return bn
}
// BigToCompact converts a whole number N to a compact representation using
// an unsigned 32-bit number. The compact representation only provides 23 bits
// of precision, so values larger than (2^23 - 1) only encode the most
// significant digits of the number. See CompactToBig for details.
func BigToCompact(n *big.Int) uint32 {
// No need to do any work if it's zero.
if n.Sign() == 0 {
return 0
}
// Since the base for the exponent is 256, the exponent can be treated
// as the number of bytes. So, shift the number right or left
// accordingly. This is equivalent to:
// mantissa = mantissa / 256^(exponent-3)
var mantissa uint32
exponent := uint(len(n.Bytes()))
if exponent <= 3 {
mantissa = uint32(n.Bits()[0])
mantissa <<= 8 * (3 - exponent)
} else {
// Use a copy to avoid modifying the caller's original number.
tn := new(big.Int).Set(n)
mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0])
}
// When the mantissa already has the sign bit set, the number is too
// large to fit into the available 23-bits, so divide the number by 256
// and increment the exponent accordingly.
if mantissa&0x00800000 != 0 {
mantissa >>= 8
exponent++
}
// Pack the exponent, sign bit, and mantissa into an unsigned 32-bit
// int and return it.
compact := uint32(exponent<<24) | mantissa
if n.Sign() < 0 {
compact |= 0x00800000
}
return compact
}
// CalcWork calculates a work value from difficulty bits. Bitcoin increases
// the difficulty for generating a block by decreasing the value which the
// generated hash must be less than. This difficulty target is stored in each
// block header using a compact representation as described in the documenation
// for CompactToBig. The main chain is selected by choosing the chain that has
// the most proof of work (highest difficulty). Since a lower target difficulty
// value equates to higher actual difficulty, the work value which will be
// accumulated must be the inverse of the difficulty. Also, in order to avoid
// potential division by zero and really small floating point numbers, the
// result adds 1 to the denominator and multiplies the numerator by 2^256.
func CalcWork(bits uint32) *big.Int {
// Return a work value of zero if the passed difficulty bits represent
// a negative number. Note this should not happen in practice with valid
// blocks, but an invalid block could trigger it.
difficultyNum := CompactToBig(bits)
if difficultyNum.Sign() <= 0 {
return big.NewInt(0)
}
// (1 << 256) / (difficultyNum + 1)
denominator := new(big.Int).Add(difficultyNum, bigOne)
return new(big.Int).Div(oneLsh256, denominator)
}
// calcEasiestDifficulty calculates the easiest possible difficulty that a block
// can have given starting difficulty bits and a duration. It is mainly used to
// verify that claimed proof of work by a block is sane as compared to a
// known good checkpoint.
func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 {
// Convert types used in the calculations below.
durationVal := int64(duration)
adjustmentFactor := big.NewInt(retargetAdjustmentFactor)
// The test network rules allow minimum difficulty blocks after more
// than twice the desired amount of time needed to generate a block has
// elapsed.
if b.chainParams.ResetMinDifficulty {
if durationVal > int64(targetSpacing)*2 {
return b.chainParams.PowLimitBits
}
}
// Since easier difficulty equates to higher numbers, the easiest
// difficulty for a given duration is the largest value possible given
// the number of retargets for the duration and starting difficulty
// multiplied by the max adjustment factor.
newTarget := CompactToBig(bits)
for durationVal > 0 && newTarget.Cmp(b.chainParams.PowLimit) < 0 {
newTarget.Mul(newTarget, adjustmentFactor)
durationVal -= maxRetargetTimespan
}
// Limit new value to the proof of work limit.
if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
newTarget.Set(b.chainParams.PowLimit)
}
return BigToCompact(newTarget)
}
// findPrevTestNetDifficulty returns the difficulty of the previous block which
// did not have the special testnet minimum difficulty rule applied.
func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, error) {
// Search backwards through the chain for the last block without
// the special rule applied.
iterNode := startNode
for iterNode != nil && iterNode.height%BlocksPerRetarget != 0 &&
iterNode.bits == b.chainParams.PowLimitBits {
// Get the previous block node. This function is used over
// simply accessing iterNode.parent directly as it will
// dynamically create previous block nodes as needed. This
// helps allow only the pieces of the chain that are needed
// to remain in memory.
var err error
iterNode, err = b.getPrevNodeFromNode(iterNode)
if err != nil {
log.Errorf("getPrevNodeFromNode: %v", err)
return 0, err
}
}
// Return the found difficulty or the minimum difficulty if no
// appropriate block was found.
lastBits := b.chainParams.PowLimitBits
if iterNode != nil {
lastBits = iterNode.bits
}
return lastBits, nil
}
// calcNextRequiredDifficulty calculates the required difficulty for the block
// after the passed previous block node based on the difficulty retarget rules.
// This function differs from the exported CalcNextRequiredDifficulty in that
// the exported version uses the current best chain as the previous block node
// while this function accepts any block node.
func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTime time.Time) (uint32, error) {
// Genesis block.
if lastNode == nil {
return b.chainParams.PowLimitBits, nil
}
// Return the previous block's difficulty requirements if this block
// is not at a difficulty retarget interval.
if (lastNode.height+1)%BlocksPerRetarget != 0 {
// The test network rules allow minimum difficulty blocks after
// more than twice the desired amount of time needed to generate
// a block has elapsed.
if b.chainParams.ResetMinDifficulty {
// Return minimum difficulty when more than twice the
// desired amount of time needed to generate a block has
// elapsed.
allowMinTime := lastNode.timestamp.Add(targetSpacing * 2)
if newBlockTime.After(allowMinTime) {
return b.chainParams.PowLimitBits, nil
}
// The block was mined within the desired timeframe, so
// return the difficulty for the last block which did
// not have the special minimum difficulty rule applied.
prevBits, err := b.findPrevTestNetDifficulty(lastNode)
if err != nil {
return 0, err
}
return prevBits, nil
}
// For the main network (or any unrecognized networks), simply
// return the previous block's difficulty requirements.
return lastNode.bits, nil
}
// Get the block node at the previous retarget (targetTimespan days
// worth of blocks).
firstNode := lastNode
for i := int64(0); i < BlocksPerRetarget-1 && firstNode != nil; i++ {
// Get the previous block node. This function is used over
// simply accessing firstNode.parent directly as it will
// dynamically create previous block nodes as needed. This
// helps allow only the pieces of the chain that are needed
// to remain in memory.
var err error
firstNode, err = b.getPrevNodeFromNode(firstNode)
if err != nil {
return 0, err
}
}
if firstNode == nil {
return 0, fmt.Errorf("unable to obtain previous retarget block")
}
// Limit the amount of adjustment that can occur to the previous
// difficulty.
actualTimespan := lastNode.timestamp.UnixNano() - firstNode.timestamp.UnixNano()
adjustedTimespan := actualTimespan
if actualTimespan < minRetargetTimespan {
adjustedTimespan = minRetargetTimespan
} else if actualTimespan > maxRetargetTimespan {
adjustedTimespan = maxRetargetTimespan
}
// Calculate new target difficulty as:
// currentDifficulty * (adjustedTimespan / targetTimespan)
// The result uses integer division which means it will be slightly
// rounded down. Bitcoind also uses integer division to calculate this
// result.
oldTarget := CompactToBig(lastNode.bits)
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
newTarget.Div(newTarget, big.NewInt(int64(targetTimespan)))
// Limit new value to the proof of work limit.
if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
newTarget.Set(b.chainParams.PowLimit)
}
// Log new target difficulty and return it. The new target logging is
// intentionally converting the bits back to a number instead of using
// newTarget since conversion to the compact representation loses
// precision.
newTargetBits := BigToCompact(newTarget)
log.Debugf("Difficulty retarget at block height %d", lastNode.height+1)
log.Debugf("Old target %08x (%064x)", lastNode.bits, oldTarget)
log.Debugf("New target %08x (%064x)", newTargetBits, CompactToBig(newTargetBits))
log.Debugf("Actual timespan %v, adjusted timespan %v, target timespan %v",
time.Duration(actualTimespan), time.Duration(adjustedTimespan),
targetTimespan)
return newTargetBits, nil
}
// CalcNextRequiredDifficulty calculates the required difficulty for the block
// after the end of the current best chain based on the difficulty retarget
// rules.
//
// This function is NOT safe for concurrent access.
func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time) (uint32, error) {
return b.calcNextRequiredDifficulty(b.bestChain, timestamp)
}

View File

@@ -1,71 +0,0 @@
// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"math/big"
"testing"
"github.com/btcsuite/btcd/blockchain"
)
func TestBigToCompact(t *testing.T) {
tests := []struct {
in int64
out uint32
}{
{0, 0},
{-1, 25231360},
}
for x, test := range tests {
n := big.NewInt(test.in)
r := blockchain.BigToCompact(n)
if r != test.out {
t.Errorf("TestBigToCompact test #%d failed: got %d want %d\n",
x, r, test.out)
return
}
}
}
func TestCompactToBig(t *testing.T) {
tests := []struct {
in uint32
out int64
}{
{10000000, 0},
}
for x, test := range tests {
n := blockchain.CompactToBig(test.in)
want := big.NewInt(test.out)
if n.Cmp(want) != 0 {
t.Errorf("TestCompactToBig test #%d failed: got %d want %d\n",
x, n.Int64(), want.Int64())
return
}
}
}
func TestCalcWork(t *testing.T) {
tests := []struct {
in uint32
out int64
}{
{10000000, 0},
}
for x, test := range tests {
bits := uint32(test.in)
r := blockchain.CalcWork(bits)
if r.Int64() != test.out {
t.Errorf("TestCalcWork test #%d failed: got %v want %d\n",
x, r.Int64(), test.out)
return
}
}
}

View File

@@ -1,97 +0,0 @@
// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"testing"
"github.com/btcsuite/btcd/blockchain"
)
// TestErrorCodeStringer tests the stringized output for the ErrorCode type.
func TestErrorCodeStringer(t *testing.T) {
tests := []struct {
in blockchain.ErrorCode
want string
}{
{blockchain.ErrDuplicateBlock, "ErrDuplicateBlock"},
{blockchain.ErrBlockTooBig, "ErrBlockTooBig"},
{blockchain.ErrBlockVersionTooOld, "ErrBlockVersionTooOld"},
{blockchain.ErrInvalidTime, "ErrInvalidTime"},
{blockchain.ErrTimeTooOld, "ErrTimeTooOld"},
{blockchain.ErrTimeTooNew, "ErrTimeTooNew"},
{blockchain.ErrDifficultyTooLow, "ErrDifficultyTooLow"},
{blockchain.ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"},
{blockchain.ErrHighHash, "ErrHighHash"},
{blockchain.ErrBadMerkleRoot, "ErrBadMerkleRoot"},
{blockchain.ErrBadCheckpoint, "ErrBadCheckpoint"},
{blockchain.ErrForkTooOld, "ErrForkTooOld"},
{blockchain.ErrCheckpointTimeTooOld, "ErrCheckpointTimeTooOld"},
{blockchain.ErrNoTransactions, "ErrNoTransactions"},
{blockchain.ErrTooManyTransactions, "ErrTooManyTransactions"},
{blockchain.ErrNoTxInputs, "ErrNoTxInputs"},
{blockchain.ErrNoTxOutputs, "ErrNoTxOutputs"},
{blockchain.ErrTxTooBig, "ErrTxTooBig"},
{blockchain.ErrBadTxOutValue, "ErrBadTxOutValue"},
{blockchain.ErrDuplicateTxInputs, "ErrDuplicateTxInputs"},
{blockchain.ErrBadTxInput, "ErrBadTxInput"},
{blockchain.ErrBadCheckpoint, "ErrBadCheckpoint"},
{blockchain.ErrMissingTx, "ErrMissingTx"},
{blockchain.ErrUnfinalizedTx, "ErrUnfinalizedTx"},
{blockchain.ErrDuplicateTx, "ErrDuplicateTx"},
{blockchain.ErrOverwriteTx, "ErrOverwriteTx"},
{blockchain.ErrImmatureSpend, "ErrImmatureSpend"},
{blockchain.ErrDoubleSpend, "ErrDoubleSpend"},
{blockchain.ErrSpendTooHigh, "ErrSpendTooHigh"},
{blockchain.ErrBadFees, "ErrBadFees"},
{blockchain.ErrTooManySigOps, "ErrTooManySigOps"},
{blockchain.ErrFirstTxNotCoinbase, "ErrFirstTxNotCoinbase"},
{blockchain.ErrMultipleCoinbases, "ErrMultipleCoinbases"},
{blockchain.ErrBadCoinbaseScriptLen, "ErrBadCoinbaseScriptLen"},
{blockchain.ErrBadCoinbaseValue, "ErrBadCoinbaseValue"},
{blockchain.ErrMissingCoinbaseHeight, "ErrMissingCoinbaseHeight"},
{blockchain.ErrBadCoinbaseHeight, "ErrBadCoinbaseHeight"},
{blockchain.ErrScriptMalformed, "ErrScriptMalformed"},
{blockchain.ErrScriptValidation, "ErrScriptValidation"},
{0xffff, "Unknown ErrorCode (65535)"},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.String()
if result != test.want {
t.Errorf("String #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}
// TestRuleError tests the error output for the RuleError type.
func TestRuleError(t *testing.T) {
tests := []struct {
in blockchain.RuleError
want string
}{
{
blockchain.RuleError{Description: "duplicate block"},
"duplicate block",
},
{
blockchain.RuleError{Description: "human-readable error"},
"human-readable error",
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.Error()
if result != test.want {
t.Errorf("Error #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}

View File

@@ -1,101 +0,0 @@
// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"fmt"
"math/big"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/database"
_ "github.com/btcsuite/btcd/database/memdb"
"github.com/btcsuite/btcutil"
)
// This example demonstrates how to create a new chain instance and use
// ProcessBlock to attempt to attempt add a block to the chain. As the package
// overview documentation describes, this includes all of the Bitcoin consensus
// rules. This example intentionally attempts to insert a duplicate genesis
// block to illustrate how an invalid block is handled.
func ExampleBlockChain_ProcessBlock() {
// Create a new database to store the accepted blocks into. Typically
// this would be opening an existing database and would not use memdb
// which is a memory-only database backend, but we create a new db
// here so this is a complete working example.
db, err := database.CreateDB("memdb")
if err != nil {
fmt.Printf("Failed to create database: %v\n", err)
return
}
defer db.Close()
// Insert the main network genesis block. This is part of the initial
// database setup. Like above, this typically would not be needed when
// opening an existing database.
genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock)
_, err = db.InsertBlock(genesisBlock)
if err != nil {
fmt.Printf("Failed to insert genesis block: %v\n", err)
return
}
// Create a new BlockChain instance using the underlying database for
// the main bitcoin network and ignore notifications.
chain := blockchain.New(db, &chaincfg.MainNetParams, nil)
// Create a new median time source that is required by the upcoming
// call to ProcessBlock. Ordinarily this would also add time values
// obtained from other peers on the network so the local time is
// adjusted to be in agreement with other peers.
timeSource := blockchain.NewMedianTime()
// Process a block. For this example, we are going to intentionally
// cause an error by trying to process the genesis block which already
// exists.
isOrphan, err := chain.ProcessBlock(genesisBlock, timeSource, blockchain.BFNone)
if err != nil {
fmt.Printf("Failed to process block: %v\n", err)
return
}
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
// Output:
// Failed to process block: already have block 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
}
// This example demonstrates how to convert the compact "bits" in a block header
// which represent the target difficulty to a big integer and display it using
// the typical hex notation.
func ExampleCompactToBig() {
// Convert the bits from block 300000 in the main block chain.
bits := uint32(419465580)
targetDifficulty := blockchain.CompactToBig(bits)
// Display it in hex.
fmt.Printf("%064x\n", targetDifficulty.Bytes())
// Output:
// 0000000000000000896c00000000000000000000000000000000000000000000
}
// This example demonstrates how to convert a target difficulty into the compact
// "bits" in a block header which represent that target difficulty .
func ExampleBigToCompact() {
// Convert the target difficulty from block 300000 in the main block
// chain to compact form.
t := "0000000000000000896c00000000000000000000000000000000000000000000"
targetDifficulty, success := new(big.Int).SetString(t, 16)
if !success {
fmt.Println("invalid target difficulty")
return
}
bits := blockchain.BigToCompact(targetDifficulty)
fmt.Println(bits)
// Output:
// 419465580
}

View File

@@ -1,44 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
/*
This test file is part of the blockchain package rather than than the
blockchain_test package so it can bridge access to the internals to properly
test cases which are either not possible or can't reliably be tested via the
public interface. The functions are only exported while the tests are being
run.
*/
package blockchain
import (
"sort"
"time"
)
// TstSetCoinbaseMaturity makes the ability to set the coinbase maturity
// available to the test package.
func TstSetCoinbaseMaturity(maturity int64) {
coinbaseMaturity = maturity
}
// TstTimeSorter makes the internal timeSorter type available to the test
// package.
func TstTimeSorter(times []time.Time) sort.Interface {
return timeSorter(times)
}
// TstCheckSerializedHeight makes the internal checkSerializedHeight function
// available to the test package.
var TstCheckSerializedHeight = checkSerializedHeight
// TstSetMaxMedianTimeEntries makes the ability to set the maximum number of
// median tiem entries available to the test package.
func TstSetMaxMedianTimeEntries(val int) {
maxMedianTimeEntries = val
}
// TstCheckBlockScripts makes the internal checkBlockScripts function available
// to the test package.
var TstCheckBlockScripts = checkBlockScripts

View File

@@ -1,71 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"errors"
"io"
"github.com/btcsuite/btclog"
)
// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
// requests it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
DisableLog()
}
// DisableLog disables all library log output. Logging output is disabled
// by default until either UseLogger or SetLogWriter are called.
func DisableLog() {
log = btclog.Disabled
}
// UseLogger uses a specified Logger to output package logging info.
// This should be used in preference to SetLogWriter if the caller is also
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}
// SetLogWriter uses a specified io.Writer to output package logging info.
// This allows a caller to direct package logging output without needing a
// dependency on seelog. If the caller is also using btclog, UseLogger should
// be used instead.
func SetLogWriter(w io.Writer, level string) error {
if w == nil {
return errors.New("nil writer")
}
lvl, ok := btclog.LogLevelFromString(level)
if !ok {
return errors.New("invalid log level")
}
l, err := btclog.NewLoggerFromWriter(w, lvl)
if err != nil {
return err
}
UseLogger(l)
return nil
}
// LogClosure is a closure that can be printed with %v to be used to
// generate expensive-to-create data for a detailed log level and avoid doing
// the work if the data isn't printed.
type logClosure func() string
func (c logClosure) String() string {
return c()
}
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

View File

@@ -1,24 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"testing"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcutil"
)
// TestMerkle tests the BuildMerkleTreeStore API.
func TestMerkle(t *testing.T) {
block := btcutil.NewBlock(&Block100000)
merkles := blockchain.BuildMerkleTreeStore(block.Transactions())
calculatedMerkleRoot := merkles[len(merkles)-1]
wantMerkle := &Block100000.Header.MerkleRoot
if !wantMerkle.IsEqual(calculatedMerkleRoot) {
t.Errorf("BuildMerkleTreeStore: merkle root mismatch - "+
"got %v, want %v", calculatedMerkleRoot, wantMerkle)
}
}

View File

@@ -1,73 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"fmt"
)
// NotificationType represents the type of a notification message.
type NotificationType int
// NotificationCallback is used for a caller to provide a callback for
// notifications about various chain events.
type NotificationCallback func(*Notification)
// Constants for the type of a notification message.
const (
// NTBlockAccepted indicates the associated block was accepted into
// the block chain. Note that this does not necessarily mean it was
// added to the main chain. For that, use NTBlockConnected.
NTBlockAccepted NotificationType = iota
// NTBlockConnected indicates the associated block was connected to the
// main chain.
NTBlockConnected
// NTBlockDisconnected indicates the associated block was disconnected
// from the main chain.
NTBlockDisconnected
)
// notificationTypeStrings is a map of notification types back to their constant
// names for pretty printing.
var notificationTypeStrings = map[NotificationType]string{
NTBlockAccepted: "NTBlockAccepted",
NTBlockConnected: "NTBlockConnected",
NTBlockDisconnected: "NTBlockDisconnected",
}
// String returns the NotificationType in human-readable form.
func (n NotificationType) String() string {
if s, ok := notificationTypeStrings[n]; ok {
return s
}
return fmt.Sprintf("Unknown Notification Type (%d)", int(n))
}
// Notification defines notification that is sent to the caller via the callback
// function provided during the call to New and consists of a notification type
// as well as associated data that depends on the type as follows:
// - NTBlockAccepted: *btcutil.Block
// - NTBlockConnected: *btcutil.Block
// - NTBlockDisconnected: *btcutil.Block
type Notification struct {
Type NotificationType
Data interface{}
}
// sendNotification sends a notification with the passed type and data if the
// caller requested notifications by providing a callback function in the call
// to New.
func (b *BlockChain) sendNotification(typ NotificationType, data interface{}) {
// Ignore it if the caller didn't request notifications.
if b.notifications == nil {
return
}
// Generate and send the notification.
n := Notification{Type: typ, Data: data}
b.notifications(&n)
}

View File

@@ -1,224 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"fmt"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// BehaviorFlags is a bitmask defining tweaks to the normal behavior when
// performing chain processing and consensus rules checks.
type BehaviorFlags uint32
const (
// BFFastAdd may be set to indicate that several checks can be avoided
// for the block since it is already known to fit into the chain due to
// already proving it correct links into the chain up to a known
// checkpoint. This is primarily used for headers-first mode.
BFFastAdd BehaviorFlags = 1 << iota
// BFNoPoWCheck may be set to indicate the proof of work check which
// ensures a block hashes to a value less than the required target will
// not be performed.
BFNoPoWCheck
// BFDryRun may be set to indicate the block should not modify the chain
// or memory chain index. This is useful to test that a block is valid
// without modifying the current state.
BFDryRun
// BFNone is a convenience value to specifically indicate no flags.
BFNone BehaviorFlags = 0
)
// blockExists determines whether a block with the given hash exists either in
// the main chain or any side chains.
func (b *BlockChain) blockExists(hash *wire.ShaHash) (bool, error) {
// Check memory chain first (could be main chain or side chain blocks).
if _, ok := b.index[*hash]; ok {
return true, nil
}
// Check in database (rest of main chain not in memory).
return b.db.ExistsSha(hash)
}
// processOrphans determines if there are any orphans which depend on the passed
// block hash (they are no longer orphans if true) and potentially accepts them.
// It repeats the process for the newly accepted blocks (to detect further
// orphans which may no longer be orphans) until there are no more.
//
// The flags do not modify the behavior of this function directly, however they
// are needed to pass along to maybeAcceptBlock.
func (b *BlockChain) processOrphans(hash *wire.ShaHash, flags BehaviorFlags) error {
// Start with processing at least the passed hash. Leave a little room
// for additional orphan blocks that need to be processed without
// needing to grow the array in the common case.
processHashes := make([]*wire.ShaHash, 0, 10)
processHashes = append(processHashes, hash)
for len(processHashes) > 0 {
// Pop the first hash to process from the slice.
processHash := processHashes[0]
processHashes[0] = nil // Prevent GC leak.
processHashes = processHashes[1:]
// Look up all orphans that are parented by the block we just
// accepted. This will typically only be one, but it could
// be multiple if multiple blocks are mined and broadcast
// around the same time. The one with the most proof of work
// will eventually win out. An indexing for loop is
// intentionally used over a range here as range does not
// reevaluate the slice on each iteration nor does it adjust the
// index for the modified slice.
for i := 0; i < len(b.prevOrphans[*processHash]); i++ {
orphan := b.prevOrphans[*processHash][i]
if orphan == nil {
log.Warnf("Found a nil entry at index %d in the "+
"orphan dependency list for block %v", i,
processHash)
continue
}
// Remove the orphan from the orphan pool.
orphanHash := orphan.block.Sha()
b.removeOrphanBlock(orphan)
i--
// Potentially accept the block into the block chain.
err := b.maybeAcceptBlock(orphan.block, flags)
if err != nil {
return err
}
// Add this block to the list of blocks to process so
// any orphan blocks that depend on this block are
// handled too.
processHashes = append(processHashes, orphanHash)
}
}
return nil
}
// ProcessBlock is the main workhorse for handling insertion of new blocks into
// the block chain. It includes functionality such as rejecting duplicate
// blocks, ensuring blocks follow all rules, orphan handling, and insertion into
// the block chain along with best chain selection and reorganization.
//
// It returns a bool which indicates whether or not the block is an orphan and
// any errors that occurred during processing. The returned bool is only valid
// when the error is nil.
func (b *BlockChain) ProcessBlock(block *btcutil.Block, timeSource MedianTimeSource, flags BehaviorFlags) (bool, error) {
fastAdd := flags&BFFastAdd == BFFastAdd
dryRun := flags&BFDryRun == BFDryRun
blockHash := block.Sha()
log.Tracef("Processing block %v", blockHash)
// The block must not already exist in the main chain or side chains.
exists, err := b.blockExists(blockHash)
if err != nil {
return false, err
}
if exists {
str := fmt.Sprintf("already have block %v", blockHash)
return false, ruleError(ErrDuplicateBlock, str)
}
// The block must not already exist as an orphan.
if _, exists := b.orphans[*blockHash]; exists {
str := fmt.Sprintf("already have block (orphan) %v", blockHash)
return false, ruleError(ErrDuplicateBlock, str)
}
// Perform preliminary sanity checks on the block and its transactions.
err = checkBlockSanity(block, b.chainParams.PowLimit, timeSource, flags)
if err != nil {
return false, err
}
// Find the previous checkpoint and perform some additional checks based
// on the checkpoint. This provides a few nice properties such as
// preventing old side chain blocks before the last checkpoint,
// rejecting easy to mine, but otherwise bogus, blocks that could be
// used to eat memory, and ensuring expected (versus claimed) proof of
// work requirements since the previous checkpoint are met.
blockHeader := &block.MsgBlock().Header
checkpointBlock, err := b.findPreviousCheckpoint()
if err != nil {
return false, err
}
if checkpointBlock != nil {
// Ensure the block timestamp is after the checkpoint timestamp.
checkpointHeader := &checkpointBlock.MsgBlock().Header
checkpointTime := checkpointHeader.Timestamp
if blockHeader.Timestamp.Before(checkpointTime) {
str := fmt.Sprintf("block %v has timestamp %v before "+
"last checkpoint timestamp %v", blockHash,
blockHeader.Timestamp, checkpointTime)
return false, ruleError(ErrCheckpointTimeTooOld, str)
}
if !fastAdd {
// Even though the checks prior to now have already ensured the
// proof of work exceeds the claimed amount, the claimed amount
// is a field in the block header which could be forged. This
// check ensures the proof of work is at least the minimum
// expected based on elapsed time since the last checkpoint and
// maximum adjustment allowed by the retarget rules.
duration := blockHeader.Timestamp.Sub(checkpointTime)
requiredTarget := CompactToBig(b.calcEasiestDifficulty(
checkpointHeader.Bits, duration))
currentTarget := CompactToBig(blockHeader.Bits)
if currentTarget.Cmp(requiredTarget) > 0 {
str := fmt.Sprintf("block target difficulty of %064x "+
"is too low when compared to the previous "+
"checkpoint", currentTarget)
return false, ruleError(ErrDifficultyTooLow, str)
}
}
}
// Handle orphan blocks.
prevHash := &blockHeader.PrevBlock
if !prevHash.IsEqual(zeroHash) {
prevHashExists, err := b.blockExists(prevHash)
if err != nil {
return false, err
}
if !prevHashExists {
if !dryRun {
log.Infof("Adding orphan block %v with parent %v",
blockHash, prevHash)
b.addOrphanBlock(block)
}
return true, nil
}
}
// The block has passed all context independent checks and appears sane
// enough to potentially accept it into the block chain.
err = b.maybeAcceptBlock(block, flags)
if err != nil {
return false, err
}
// Don't process any orphans or log when the dry run flag is set.
if !dryRun {
// Accept any orphan blocks that depend on this block (they are
// no longer orphans) and repeat for those accepted blocks until
// there are no more.
err := b.processOrphans(blockHash, flags)
if err != nil {
return false, err
}
log.Debugf("Accepted block %v", blockHash)
}
return false, nil
}

View File

@@ -1,134 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"compress/bzip2"
"encoding/binary"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// TestReorganization loads a set of test blocks which force a chain
// reorganization to test the block chain handling code.
// The test blocks were originally from a post on the bitcoin talk forums:
// https://bitcointalk.org/index.php?topic=46370.msg577556#msg577556
func TestReorganization(t *testing.T) {
// Intentionally load the side chain blocks out of order to ensure
// orphans are handled properly along with chain reorganization.
testFiles := []string{
"blk_0_to_4.dat.bz2",
"blk_4A.dat.bz2",
"blk_5A.dat.bz2",
"blk_3A.dat.bz2",
}
var blocks []*btcutil.Block
for _, file := range testFiles {
blockTmp, err := loadBlocks(file)
if err != nil {
t.Errorf("Error loading file: %v\n", err)
}
for _, block := range blockTmp {
blocks = append(blocks, block)
}
}
t.Logf("Number of blocks: %v\n", len(blocks))
// Create a new database and chain instance to run tests against.
chain, teardownFunc, err := chainSetup("reorg")
if err != nil {
t.Errorf("Failed to setup chain instance: %v", err)
return
}
defer teardownFunc()
// Since we're not dealing with the real block chain, disable
// checkpoints and set the coinbase maturity to 1.
chain.DisableCheckpoints(true)
blockchain.TstSetCoinbaseMaturity(1)
timeSource := blockchain.NewMedianTime()
expectedOrphans := map[int]struct{}{5: struct{}{}, 6: struct{}{}}
for i := 1; i < len(blocks); i++ {
isOrphan, err := chain.ProcessBlock(blocks[i], timeSource, blockchain.BFNone)
if err != nil {
t.Errorf("ProcessBlock fail on block %v: %v\n", i, err)
return
}
if _, ok := expectedOrphans[i]; !ok && isOrphan {
t.Errorf("ProcessBlock incorrectly returned block %v "+
"is an orphan\n", i)
}
}
return
}
// loadBlocks reads files containing bitcoin block data (gzipped but otherwise
// in the format bitcoind writes) from disk and returns them as an array of
// btcutil.Block. This is largely borrowed from the test code in btcdb.
func loadBlocks(filename string) (blocks []*btcutil.Block, err error) {
filename = filepath.Join("testdata/", filename)
var network = wire.MainNet
var dr io.Reader
var fi io.ReadCloser
fi, err = os.Open(filename)
if err != nil {
return
}
if strings.HasSuffix(filename, ".bz2") {
dr = bzip2.NewReader(fi)
} else {
dr = fi
}
defer fi.Close()
var block *btcutil.Block
err = nil
for height := int64(1); err == nil; height++ {
var rintbuf uint32
err = binary.Read(dr, binary.LittleEndian, &rintbuf)
if err == io.EOF {
// hit end of file at expected offset: no warning
height--
err = nil
break
}
if err != nil {
break
}
if rintbuf != uint32(network) {
break
}
err = binary.Read(dr, binary.LittleEndian, &rintbuf)
blocklen := rintbuf
rbytes := make([]byte, blocklen)
// read block
dr.Read(rbytes)
block, err = btcutil.NewBlockFromBytes(rbytes)
if err != nil {
return
}
blocks = append(blocks, block)
}
return
}

View File

@@ -1,46 +0,0 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"fmt"
"runtime"
"testing"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/txscript"
)
// TestCheckBlockScripts ensures that validating the all of the scripts in a
// known-good block doesn't return an error.
func TestCheckBlockScripts(t *testing.T) {
runtime.GOMAXPROCS(runtime.NumCPU())
testBlockNum := 277647
blockDataFile := fmt.Sprintf("%d.dat.bz2", testBlockNum)
blocks, err := loadBlocks(blockDataFile)
if err != nil {
t.Errorf("Error loading file: %v\n", err)
return
}
if len(blocks) > 1 {
t.Errorf("The test block file must only have one block in it")
}
txStoreDataFile := fmt.Sprintf("%d.txstore.bz2", testBlockNum)
txStore, err := loadTxStore(txStoreDataFile)
if err != nil {
t.Errorf("Error loading txstore: %v\n", err)
return
}
scriptFlags := txscript.ScriptBip16
err = blockchain.TstCheckBlockScripts(blocks[0], txStore, scriptFlags)
if err != nil {
t.Errorf("Transaction script validation failed: %v\n",
err)
return
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,52 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"reflect"
"sort"
"testing"
"time"
"github.com/btcsuite/btcd/blockchain"
)
// TestTimeSorter tests the timeSorter implementation.
func TestTimeSorter(t *testing.T) {
tests := []struct {
in []time.Time
want []time.Time
}{
{
in: []time.Time{
time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000)
time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond)
time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000)
time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000)
time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000)
time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
},
want: []time.Time{
time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000)
time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000)
time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000)
time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000)
time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000)
time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond)
},
},
}
for i, test := range tests {
result := make([]time.Time, len(test.in))
copy(result, test.in)
sort.Sort(blockchain.TstTimeSorter(result))
if !reflect.DeepEqual(result, test.want) {
t.Errorf("timeSorter #%d got %v want %v", i, result,
test.want)
continue
}
}
}

View File

@@ -1,319 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"fmt"
"github.com/btcsuite/btcd/database"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// TxData contains contextual information about transactions such as which block
// they were found in and whether or not the outputs are spent.
type TxData struct {
Tx *btcutil.Tx
Hash *wire.ShaHash
BlockHeight int64
Spent []bool
Err error
}
// TxStore is used to store transactions needed by other transactions for things
// such as script validation and double spend prevention. This also allows the
// transaction data to be treated as a view since it can contain the information
// from the point-of-view of different points in the chain.
type TxStore map[wire.ShaHash]*TxData
// connectTransactions updates the passed map by applying transaction and
// spend information for all the transactions in the passed block. Only
// transactions in the passed map are updated.
func connectTransactions(txStore TxStore, block *btcutil.Block) error {
// Loop through all of the transactions in the block to see if any of
// them are ones we need to update and spend based on the results map.
for _, tx := range block.Transactions() {
// Update the transaction store with the transaction information
// if it's one of the requested transactions.
msgTx := tx.MsgTx()
if txD, exists := txStore[*tx.Sha()]; exists {
txD.Tx = tx
txD.BlockHeight = block.Height()
txD.Spent = make([]bool, len(msgTx.TxOut))
txD.Err = nil
}
// Spend the origin transaction output.
for _, txIn := range msgTx.TxIn {
originHash := &txIn.PreviousOutPoint.Hash
originIndex := txIn.PreviousOutPoint.Index
if originTx, exists := txStore[*originHash]; exists {
if originIndex > uint32(len(originTx.Spent)) {
continue
}
originTx.Spent[originIndex] = true
}
}
}
return nil
}
// disconnectTransactions updates the passed map by undoing transaction and
// spend information for all transactions in the passed block. Only
// transactions in the passed map are updated.
func disconnectTransactions(txStore TxStore, block *btcutil.Block) error {
// Loop through all of the transactions in the block to see if any of
// them are ones that need to be undone based on the transaction store.
for _, tx := range block.Transactions() {
// Clear this transaction from the transaction store if needed.
// Only clear it rather than deleting it because the transaction
// connect code relies on its presence to decide whether or not
// to update the store and any transactions which exist on both
// sides of a fork would otherwise not be updated.
if txD, exists := txStore[*tx.Sha()]; exists {
txD.Tx = nil
txD.BlockHeight = 0
txD.Spent = nil
txD.Err = database.ErrTxShaMissing
}
// Unspend the origin transaction output.
for _, txIn := range tx.MsgTx().TxIn {
originHash := &txIn.PreviousOutPoint.Hash
originIndex := txIn.PreviousOutPoint.Index
originTx, exists := txStore[*originHash]
if exists && originTx.Tx != nil && originTx.Err == nil {
if originIndex > uint32(len(originTx.Spent)) {
continue
}
originTx.Spent[originIndex] = false
}
}
}
return nil
}
// fetchTxStoreMain fetches transaction data about the provided set of
// transactions from the point of view of the end of the main chain. It takes
// a flag which specifies whether or not fully spent transaction should be
// included in the results.
func fetchTxStoreMain(db database.Db, txSet map[wire.ShaHash]struct{}, includeSpent bool) TxStore {
// Just return an empty store now if there are no requested hashes.
txStore := make(TxStore)
if len(txSet) == 0 {
return txStore
}
// The transaction store map needs to have an entry for every requested
// transaction. By default, all the transactions are marked as missing.
// Each entry will be filled in with the appropriate data below.
txList := make([]*wire.ShaHash, 0, len(txSet))
for hash := range txSet {
hashCopy := hash
txStore[hash] = &TxData{Hash: &hashCopy, Err: database.ErrTxShaMissing}
txList = append(txList, &hashCopy)
}
// Ask the database (main chain) for the list of transactions. This
// will return the information from the point of view of the end of the
// main chain. Choose whether or not to include fully spent
// transactions depending on the passed flag.
var txReplyList []*database.TxListReply
if includeSpent {
txReplyList = db.FetchTxByShaList(txList)
} else {
txReplyList = db.FetchUnSpentTxByShaList(txList)
}
for _, txReply := range txReplyList {
// Lookup the existing results entry to modify. Skip
// this reply if there is no corresponding entry in
// the transaction store map which really should not happen, but
// be safe.
txD, ok := txStore[*txReply.Sha]
if !ok {
continue
}
// Fill in the transaction details. A copy is used here since
// there is no guarantee the returned data isn't cached and
// this code modifies the data. A bug caused by modifying the
// cached data would likely be difficult to track down and could
// cause subtle errors, so avoid the potential altogether.
txD.Err = txReply.Err
if txReply.Err == nil {
txD.Tx = btcutil.NewTx(txReply.Tx)
txD.BlockHeight = txReply.Height
txD.Spent = make([]bool, len(txReply.TxSpent))
copy(txD.Spent, txReply.TxSpent)
}
}
return txStore
}
// fetchTxStore fetches transaction data about the provided set of transactions
// from the point of view of the given node. For example, a given node might
// be down a side chain where a transaction hasn't been spent from its point of
// view even though it might have been spent in the main chain (or another side
// chain). Another scenario is where a transaction exists from the point of
// view of the main chain, but doesn't exist in a side chain that branches
// before the block that contains the transaction on the main chain.
func (b *BlockChain) fetchTxStore(node *blockNode, txSet map[wire.ShaHash]struct{}) (TxStore, error) {
// Get the previous block node. This function is used over simply
// accessing node.parent directly as it will dynamically create previous
// block nodes as needed. This helps allow only the pieces of the chain
// that are needed to remain in memory.
prevNode, err := b.getPrevNodeFromNode(node)
if err != nil {
return nil, err
}
// If we haven't selected a best chain yet or we are extending the main
// (best) chain with a new block, fetch the requested set from the point
// of view of the end of the main (best) chain without including fully
// spent transactions in the results. This is a little more efficient
// since it means less transaction lookups are needed.
if b.bestChain == nil || (prevNode != nil && prevNode.hash.IsEqual(b.bestChain.hash)) {
txStore := fetchTxStoreMain(b.db, txSet, false)
return txStore, nil
}
// Fetch the requested set from the point of view of the end of the
// main (best) chain including fully spent transactions. The fully
// spent transactions are needed because the following code unspends
// them to get the correct point of view.
txStore := fetchTxStoreMain(b.db, txSet, true)
// The requested node is either on a side chain or is a node on the main
// chain before the end of it. In either case, we need to undo the
// transactions and spend information for the blocks which would be
// disconnected during a reorganize to the point of view of the
// node just before the requested node.
detachNodes, attachNodes := b.getReorganizeNodes(prevNode)
for e := detachNodes.Front(); e != nil; e = e.Next() {
n := e.Value.(*blockNode)
block, err := b.db.FetchBlockBySha(n.hash)
if err != nil {
return nil, err
}
disconnectTransactions(txStore, block)
}
// The transaction store is now accurate to either the node where the
// requested node forks off the main chain (in the case where the
// requested node is on a side chain), or the requested node itself if
// the requested node is an old node on the main chain. Entries in the
// attachNodes list indicate the requested node is on a side chain, so
// if there are no nodes to attach, we're done.
if attachNodes.Len() == 0 {
return txStore, nil
}
// The requested node is on a side chain, so we need to apply the
// transactions and spend information from each of the nodes to attach.
for e := attachNodes.Front(); e != nil; e = e.Next() {
n := e.Value.(*blockNode)
block, exists := b.blockCache[*n.hash]
if !exists {
return nil, fmt.Errorf("unable to find block %v in "+
"side chain cache for transaction search",
n.hash)
}
connectTransactions(txStore, block)
}
return txStore, nil
}
// fetchInputTransactions fetches the input transactions referenced by the
// transactions in the given block from its point of view. See fetchTxList
// for more details on what the point of view entails.
func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Block) (TxStore, error) {
// Build a map of in-flight transactions because some of the inputs in
// this block could be referencing other transactions earlier in this
// block which are not yet in the chain.
txInFlight := map[wire.ShaHash]int{}
transactions := block.Transactions()
for i, tx := range transactions {
txInFlight[*tx.Sha()] = i
}
// Loop through all of the transaction inputs (except for the coinbase
// which has no inputs) collecting them into sets of what is needed and
// what is already known (in-flight).
txNeededSet := make(map[wire.ShaHash]struct{})
txStore := make(TxStore)
for i, tx := range transactions[1:] {
for _, txIn := range tx.MsgTx().TxIn {
// Add an entry to the transaction store for the needed
// transaction with it set to missing by default.
originHash := &txIn.PreviousOutPoint.Hash
txD := &TxData{Hash: originHash, Err: database.ErrTxShaMissing}
txStore[*originHash] = txD
// It is acceptable for a transaction input to reference
// the output of another transaction in this block only
// if the referenced transaction comes before the
// current one in this block. Update the transaction
// store acccordingly when this is the case. Otherwise,
// we still need the transaction.
//
// NOTE: The >= is correct here because i is one less
// than the actual position of the transaction within
// the block due to skipping the coinbase.
if inFlightIndex, ok := txInFlight[*originHash]; ok &&
i >= inFlightIndex {
originTx := transactions[inFlightIndex]
txD.Tx = originTx
txD.BlockHeight = node.height
txD.Spent = make([]bool, len(originTx.MsgTx().TxOut))
txD.Err = nil
} else {
txNeededSet[*originHash] = struct{}{}
}
}
}
// Request the input transactions from the point of view of the node.
txNeededStore, err := b.fetchTxStore(node, txNeededSet)
if err != nil {
return nil, err
}
// Merge the results of the requested transactions and the in-flight
// transactions.
for _, txD := range txNeededStore {
txStore[*txD.Hash] = txD
}
return txStore, nil
}
// FetchTransactionStore fetches the input transactions referenced by the
// passed transaction from the point of view of the end of the main chain. It
// also attempts to fetch the transaction itself so the returned TxStore can be
// examined for duplicate transactions.
func (b *BlockChain) FetchTransactionStore(tx *btcutil.Tx) (TxStore, error) {
// Create a set of needed transactions from the transactions referenced
// by the inputs of the passed transaction. Also, add the passed
// transaction itself as a way for the caller to detect duplicates.
txNeededSet := make(map[wire.ShaHash]struct{})
txNeededSet[*tx.Sha()] = struct{}{}
for _, txIn := range tx.MsgTx().TxIn {
txNeededSet[txIn.PreviousOutPoint.Hash] = struct{}{}
}
// Request the input transactions from the point of view of the end of
// the main chain without including fully spent trasactions in the
// results. Fully spent transactions are only needed for chain
// reorganization which does not apply here.
txStore := fetchTxStoreMain(b.db, txNeededSet, false)
return txStore, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,381 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"math"
"reflect"
"testing"
"time"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// TestCheckConnectBlock tests the CheckConnectBlock function to ensure it
// fails
func TestCheckConnectBlock(t *testing.T) {
// Create a new database and chain instance to run tests against.
chain, teardownFunc, err := chainSetup("checkconnectblock")
if err != nil {
t.Errorf("Failed to setup chain instance: %v", err)
return
}
defer teardownFunc()
err = chain.GenerateInitialIndex()
if err != nil {
t.Errorf("GenerateInitialIndex: %v", err)
}
// The genesis block should fail to connect since it's already
// inserted.
genesisBlock := chaincfg.MainNetParams.GenesisBlock
err = chain.CheckConnectBlock(btcutil.NewBlock(genesisBlock))
if err == nil {
t.Errorf("CheckConnectBlock: Did not received expected error")
}
}
// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works
// as expected.
func TestCheckBlockSanity(t *testing.T) {
powLimit := chaincfg.MainNetParams.PowLimit
block := btcutil.NewBlock(&Block100000)
timeSource := blockchain.NewMedianTime()
err := blockchain.CheckBlockSanity(block, powLimit, timeSource)
if err != nil {
t.Errorf("CheckBlockSanity: %v", err)
}
// Ensure a block that has a timestamp with a precision higher than one
// second fails.
timestamp := block.MsgBlock().Header.Timestamp
block.MsgBlock().Header.Timestamp = timestamp.Add(time.Nanosecond)
err = blockchain.CheckBlockSanity(block, powLimit, timeSource)
if err == nil {
t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
}
}
// TestCheckSerializedHeight tests the checkSerializedHeight function with
// various serialized heights and also does negative tests to ensure errors
// and handled properly.
func TestCheckSerializedHeight(t *testing.T) {
// Create an empty coinbase template to be used in the tests below.
coinbaseOutpoint := wire.NewOutPoint(&wire.ShaHash{}, math.MaxUint32)
coinbaseTx := wire.NewMsgTx()
coinbaseTx.Version = 2
coinbaseTx.AddTxIn(wire.NewTxIn(coinbaseOutpoint, nil))
// Expected rule errors.
missingHeightError := blockchain.RuleError{
ErrorCode: blockchain.ErrMissingCoinbaseHeight,
}
badHeightError := blockchain.RuleError{
ErrorCode: blockchain.ErrBadCoinbaseHeight,
}
tests := []struct {
sigScript []byte // Serialized data
wantHeight int64 // Expected height
err error // Expected error type
}{
// No serialized height length.
{[]byte{}, 0, missingHeightError},
// Serialized height length with no height bytes.
{[]byte{0x02}, 0, missingHeightError},
// Serialized height length with too few height bytes.
{[]byte{0x02, 0x4a}, 0, missingHeightError},
// Serialized height that needs 2 bytes to encode.
{[]byte{0x02, 0x4a, 0x52}, 21066, nil},
// Serialized height that needs 2 bytes to encode, but backwards
// endianness.
{[]byte{0x02, 0x4a, 0x52}, 19026, badHeightError},
// Serialized height that needs 3 bytes to encode.
{[]byte{0x03, 0x40, 0x0d, 0x03}, 200000, nil},
// Serialized height that needs 3 bytes to encode, but backwards
// endianness.
{[]byte{0x03, 0x40, 0x0d, 0x03}, 1074594560, badHeightError},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
msgTx := coinbaseTx.Copy()
msgTx.TxIn[0].SignatureScript = test.sigScript
tx := btcutil.NewTx(msgTx)
err := blockchain.TstCheckSerializedHeight(tx, test.wantHeight)
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("checkSerializedHeight #%d wrong error type "+
"got: %v <%T>, want: %T", i, err, err, test.err)
continue
}
if rerr, ok := err.(blockchain.RuleError); ok {
trerr := test.err.(blockchain.RuleError)
if rerr.ErrorCode != trerr.ErrorCode {
t.Errorf("checkSerializedHeight #%d wrong "+
"error code got: %v, want: %v", i,
rerr.ErrorCode, trerr.ErrorCode)
continue
}
}
}
}
// Block100000 defines block 100,000 of the block chain. It is used to
// test Block operations.
var Block100000 = wire.MsgBlock{
Header: wire.BlockHeader{
Version: 1,
PrevBlock: wire.ShaHash([32]byte{ // Make go vet happy.
0x50, 0x12, 0x01, 0x19, 0x17, 0x2a, 0x61, 0x04,
0x21, 0xa6, 0xc3, 0x01, 0x1d, 0xd3, 0x30, 0xd9,
0xdf, 0x07, 0xb6, 0x36, 0x16, 0xc2, 0xcc, 0x1f,
0x1c, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
}), // 000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250
MerkleRoot: wire.ShaHash([32]byte{ // Make go vet happy.
0x66, 0x57, 0xa9, 0x25, 0x2a, 0xac, 0xd5, 0xc0,
0xb2, 0x94, 0x09, 0x96, 0xec, 0xff, 0x95, 0x22,
0x28, 0xc3, 0x06, 0x7c, 0xc3, 0x8d, 0x48, 0x85,
0xef, 0xb5, 0xa4, 0xac, 0x42, 0x47, 0xe9, 0xf3,
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
Timestamp: time.Unix(1293623863, 0), // 2010-12-29 11:57:43 +0000 UTC
Bits: 0x1b04864c, // 453281356
Nonce: 0x10572b0f, // 274148111
},
Transactions: []*wire.MsgTx{
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: wire.ShaHash{},
Index: 0xffffffff,
},
SignatureScript: []byte{
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
},
Sequence: 0xffffffff,
},
},
TxOut: []*wire.TxOut{
{
Value: 0x12a05f200, // 5000000000
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
0x84, // 65-byte signature
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: wire.ShaHash([32]byte{ // Make go vet happy.
0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60,
0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac,
0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07,
0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87,
}), // 87a157f3fd88ac7907c05fc55e271dc4acdc5605d187d646604ca8c0e9382e03
Index: 0,
},
SignatureScript: []byte{
0x49, // OP_DATA_73
0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3,
0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6,
0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94,
0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58,
0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00,
0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62,
0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c,
0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60,
0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48,
0x01, // 73-byte signature
0x41, // OP_DATA_65
0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d,
0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38,
0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25,
0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e,
0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8,
0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd,
0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b,
0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3,
0xd3, // 65-byte pubkey
},
Sequence: 0xffffffff,
},
},
TxOut: []*wire.TxOut{
{
Value: 0x2123e300, // 556000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60,
0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e,
0xf7, 0xf5, 0x8b, 0x32,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
{
Value: 0x108e20f00, // 4444000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f,
0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b,
0x52, 0xde, 0x3d, 0x7c,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: wire.ShaHash([32]byte{ // Make go vet happy.
0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d,
0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27,
0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65,
0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf,
}), // cf4e2978d0611ce46592e02d7e7daf8627a316ab69759a9f3df109a7f2bf3ec3
Index: 1,
},
SignatureScript: []byte{
0x47, // OP_DATA_71
0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf,
0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5,
0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34,
0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31,
0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee,
0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f,
0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c,
0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e,
0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01,
0x41, // OP_DATA_65
0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78,
0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5,
0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39,
0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21,
0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee,
0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3,
0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95,
0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85,
0x0f, // 65-byte pubkey
},
Sequence: 0xffffffff,
},
},
TxOut: []*wire.TxOut{
{
Value: 0xf4240, // 1000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04,
0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d,
0xad, 0xbe, 0x7e, 0x10,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
{
Value: 0x11d260c0, // 299000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1,
0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab,
0xb3, 0x40, 0x9c, 0xd9,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: wire.ShaHash([32]byte{ // Make go vet happy.
0x0b, 0x60, 0x72, 0xb3, 0x86, 0xd4, 0xa7, 0x73,
0x23, 0x52, 0x37, 0xf6, 0x4c, 0x11, 0x26, 0xac,
0x3b, 0x24, 0x0c, 0x84, 0xb9, 0x17, 0xa3, 0x90,
0x9b, 0xa1, 0xc4, 0x3d, 0xed, 0x5f, 0x51, 0xf4,
}), // f4515fed3dc4a19b90a317b9840c243bac26114cf637522373a7d486b372600b
Index: 0,
},
SignatureScript: []byte{
0x49, // OP_DATA_73
0x30, 0x46, 0x02, 0x21, 0x00, 0xbb, 0x1a, 0xd2,
0x6d, 0xf9, 0x30, 0xa5, 0x1c, 0xce, 0x11, 0x0c,
0xf4, 0x4f, 0x7a, 0x48, 0xc3, 0xc5, 0x61, 0xfd,
0x97, 0x75, 0x00, 0xb1, 0xae, 0x5d, 0x6b, 0x6f,
0xd1, 0x3d, 0x0b, 0x3f, 0x4a, 0x02, 0x21, 0x00,
0xc5, 0xb4, 0x29, 0x51, 0xac, 0xed, 0xff, 0x14,
0xab, 0xba, 0x27, 0x36, 0xfd, 0x57, 0x4b, 0xdb,
0x46, 0x5f, 0x3e, 0x6f, 0x8d, 0xa1, 0x2e, 0x2c,
0x53, 0x03, 0x95, 0x4a, 0xca, 0x7f, 0x78, 0xf3,
0x01, // 73-byte signature
0x41, // OP_DATA_65
0x04, 0xa7, 0x13, 0x5b, 0xfe, 0x82, 0x4c, 0x97,
0xec, 0xc0, 0x1e, 0xc7, 0xd7, 0xe3, 0x36, 0x18,
0x5c, 0x81, 0xe2, 0xaa, 0x2c, 0x41, 0xab, 0x17,
0x54, 0x07, 0xc0, 0x94, 0x84, 0xce, 0x96, 0x94,
0xb4, 0x49, 0x53, 0xfc, 0xb7, 0x51, 0x20, 0x65,
0x64, 0xa9, 0xc2, 0x4d, 0xd0, 0x94, 0xd4, 0x2f,
0xdb, 0xfd, 0xd5, 0xaa, 0xd3, 0xe0, 0x63, 0xce,
0x6a, 0xf4, 0xcf, 0xaa, 0xea, 0x4e, 0xa1, 0x4f,
0xbb, // 65-byte pubkey
},
Sequence: 0xffffffff,
},
},
TxOut: []*wire.TxOut{
{
Value: 0xf4240, // 1000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0x39, 0xaa, 0x3d, 0x56, 0x9e, 0x06, 0xa1, 0xd7,
0x92, 0x6d, 0xc4, 0xbe, 0x11, 0x93, 0xc9, 0x9b,
0xf2, 0xeb, 0x9e, 0xe0,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
},
}

View File

@@ -1,9 +1,9 @@
blockchain
==========
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)]
(https://travis-ci.org/btcsuite/btcd) [![ISC License]
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/kaspanet/kaspad/blockchain)
Package blockchain implements bitcoin block handling and chain selection rules.
The test coverage is currently only around 60%, but will be increasing over
@@ -18,23 +18,10 @@ This package has intentionally been designed so it can be used as a standalone
package for any projects needing to handle processing of blocks into the bitcoin
block chain.
## Documentation
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
(http://godoc.org/github.com/btcsuite/btcd/blockchain)
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/btcsuite/btcd/blockchain
You can also view the documentation locally once the package is installed with
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
http://localhost:6060/pkg/github.com/btcsuite/btcd/blockchain
## Installation
## Installation and Updating
```bash
$ go get github.com/btcsuite/btcd/blockchain
$ go get -u github.com/kaspanet/kaspad/blockchain
```
## Bitcoin Chain Processing Overview
@@ -74,22 +61,19 @@ is by no means exhaustive:
## Examples
* [ProcessBlock Example]
(http://godoc.org/github.com/btcsuite/btcd/blockchain#example-BlockChain-ProcessBlock)
* [ProcessBlock Example](http://godoc.org/github.com/kaspanet/kaspad/blockchain#example-BlockChain-ProcessBlock)
Demonstrates how to create a new chain instance and use ProcessBlock to
attempt to attempt add a block to the chain. This example intentionally
attempt to add a block to the chain. This example intentionally
attempts to insert a duplicate genesis block to illustrate how an invalid
block is handled.
* [CompactToBig Example]
(http://godoc.org/github.com/btcsuite/btcd/blockchain#example-CompactToBig)
* [CompactToBig Example](http://godoc.org/github.com/kaspanet/kaspad/blockchain#example-CompactToBig)
Demonstrates how to convert the compact "bits" in a block header which
represent the target difficulty to a big integer and display it using the
typical hex notation.
* [BigToCompact Example]
(http://godoc.org/github.com/btcsuite/btcd/blockchain#example-BigToCompact)
Demonstrates how to convert how to convert a target difficulty into the
* [BigToCompact Example](http://godoc.org/github.com/kaspanet/kaspad/blockchain#example-BigToCompact)
Demonstrates how to convert a target difficulty into the
compact "bits" in a block header which represent that target difficulty.
## GPG Verification Key

130
blockdag/accept.go Normal file
View File

@@ -0,0 +1,130 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"fmt"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
)
func (dag *BlockDAG) addNodeToIndexWithInvalidAncestor(block *util.Block) error {
blockHeader := &block.MsgBlock().Header
newNode := newBlockNode(blockHeader, newSet(), dag.dagParams.K)
newNode.status = statusInvalidAncestor
dag.index.AddNode(newNode)
return dag.index.flushToDB()
}
// maybeAcceptBlock potentially accepts a block into the block DAG. It
// performs several validation checks which depend on its position within
// the block DAG before adding it. The block is expected to have already
// gone through ProcessBlock before calling this function with it.
//
// The flags are also passed to checkBlockContext and connectToDAG. See
// their documentation for how the flags modify their behavior.
//
// This function MUST be called with the dagLock held (for writes).
func (dag *BlockDAG) maybeAcceptBlock(block *util.Block, flags BehaviorFlags) error {
parents, err := lookupParentNodes(block, dag)
if err != nil {
if rErr, ok := err.(RuleError); ok && rErr.ErrorCode == ErrInvalidAncestorBlock {
err := dag.addNodeToIndexWithInvalidAncestor(block)
if err != nil {
return err
}
}
return err
}
// The block must pass all of the validation rules which depend on the
// position of the block within the block DAG.
err = dag.checkBlockContext(block, parents, flags)
if err != nil {
return err
}
// Create a new block node for the block and add it to the node index.
newNode := newBlockNode(&block.MsgBlock().Header, parents, dag.dagParams.K)
newNode.status = statusDataStored
dag.index.AddNode(newNode)
// Insert the block into the database if it's not already there. Even
// though it is possible the block will ultimately fail to connect, it
// has already passed all proof-of-work and validity tests which means
// it would be prohibitively expensive for an attacker to fill up the
// disk with a bunch of blocks that fail to connect. This is necessary
// since it allows block download to be decoupled from the much more
// expensive connection logic. It also has some other nice properties
// such as making blocks that never become part of the DAG or
// blocks that fail to connect available for further analysis.
err = dag.db.Update(func(dbTx database.Tx) error {
err := dbStoreBlock(dbTx, block)
if err != nil {
return err
}
return dag.index.flushToDBWithTx(dbTx)
})
if err != nil {
return err
}
// Make sure that all the block's transactions are finalized
fastAdd := flags&BFFastAdd == BFFastAdd
bluestParent := parents.bluest()
if !fastAdd {
if err := dag.validateAllTxsFinalized(block, newNode, bluestParent); err != nil {
return err
}
}
block.SetChainHeight(newNode.chainHeight)
// Connect the passed block to the DAG. This also handles validation of the
// transaction scripts.
chainUpdates, err := dag.addBlock(newNode, parents, block, flags)
if err != nil {
return err
}
// Notify the caller that the new block was accepted into the block
// DAG. The caller would typically want to react by relaying the
// inventory to other peers.
dag.dagLock.Unlock()
dag.sendNotification(NTBlockAdded, &BlockAddedNotificationData{
Block: block,
WasUnorphaned: flags&BFWasUnorphaned != 0,
})
if len(chainUpdates.addedChainBlockHashes) > 0 {
dag.sendNotification(NTChainChanged, &ChainChangedNotificationData{
RemovedChainBlockHashes: chainUpdates.removedChainBlockHashes,
AddedChainBlockHashes: chainUpdates.addedChainBlockHashes,
})
}
dag.dagLock.Lock()
return nil
}
func lookupParentNodes(block *util.Block, blockDAG *BlockDAG) (blockSet, error) {
header := block.MsgBlock().Header
parentHashes := header.ParentHashes
nodes := newSet()
for _, parentHash := range parentHashes {
node := blockDAG.index.LookupNode(parentHash)
if node == nil {
str := fmt.Sprintf("parent block %s is unknown", parentHashes)
return nil, ruleError(ErrParentBlockUnknown, str)
} else if blockDAG.index.NodeStatus(node).KnownInvalid() {
str := fmt.Sprintf("parent block %s is known to be invalid", parentHashes)
return nil, ruleError(ErrInvalidAncestorBlock, str)
}
nodes.add(node)
}
return nodes, nil
}

143
blockdag/accept_test.go Normal file
View File

@@ -0,0 +1,143 @@
package blockdag
import (
"github.com/pkg/errors"
"path/filepath"
"strings"
"testing"
"bou.ke/monkey"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
)
func TestMaybeAcceptBlockErrors(t *testing.T) {
// Create a new database and DAG instance to run tests against.
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", Config{
DAGParams: &dagconfig.SimNetParams,
})
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
dag.TestSetCoinbaseMaturity(0)
// Test rejecting the block if its parents are missing
orphanBlockFile := "blk_3B.dat"
loadedBlocks, err := LoadBlocks(filepath.Join("testdata/", orphanBlockFile))
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: "+
"Error loading file '%s': %s\n", orphanBlockFile, err)
}
block := loadedBlocks[0]
err = dag.maybeAcceptBlock(block, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Expected: %s, got: <nil>", ErrParentBlockUnknown)
}
ruleErr, ok := err.(RuleError)
if !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrParentBlockUnknown {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Unexpected error code. Want: %s, got: %s", ErrParentBlockUnknown, ruleErr.ErrorCode)
}
// Test rejecting the block if its parents are invalid
blocksFile := "blk_0_to_4.dat"
blocks, err := LoadBlocks(filepath.Join("testdata/", blocksFile))
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: "+
"Error loading file '%s': %s\n", blocksFile, err)
}
// Add a valid block and mark it as invalid
block1 := blocks[1]
isOrphan, delay, err := dag.ProcessBlock(block1, BFNone)
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: Valid block unexpectedly returned an error: %s", err)
}
if delay != 0 {
t.Fatalf("TestMaybeAcceptBlockErrors: block 1 is too far in the future")
}
if isOrphan {
t.Fatalf("TestMaybeAcceptBlockErrors: incorrectly returned block 1 is an orphan")
}
blockNode1 := dag.index.LookupNode(block1.Hash())
dag.index.SetStatusFlags(blockNode1, statusValidateFailed)
block2 := blocks[2]
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Expected: %s, got: <nil>", ErrInvalidAncestorBlock)
}
ruleErr, ok = err.(RuleError)
if !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrInvalidAncestorBlock {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Unexpected error. Want: %s, got: %s", ErrInvalidAncestorBlock, ruleErr.ErrorCode)
}
// Set block1's status back to valid for next tests
dag.index.UnsetStatusFlags(blockNode1, statusValidateFailed)
// Test rejecting the block due to bad context
originalBits := block2.MsgBlock().Header.Bits
block2.MsgBlock().Header.Bits = 0
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Expected: %s, got: <nil>", ErrUnexpectedDifficulty)
}
ruleErr, ok = err.(RuleError)
if !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrUnexpectedDifficulty {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Unexpected error. Want: %s, got: %s", ErrUnexpectedDifficulty, ruleErr.ErrorCode)
}
// Set block2's bits back to valid for next tests
block2.MsgBlock().Header.Bits = originalBits
// Test rejecting the node due to database error
databaseErrorMessage := "database error"
guard := monkey.Patch(dbStoreBlock, func(dbTx database.Tx, block *util.Block) error {
return errors.New(databaseErrorMessage)
})
defer guard.Unpatch()
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to database error: "+
"Expected: %s, got: <nil>", databaseErrorMessage)
}
if !strings.Contains(err.Error(), databaseErrorMessage) {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to database error: "+
"Unexpected error. Want: %s, got: %s", databaseErrorMessage, err)
}
guard.Unpatch()
// Test rejecting the node due to index error
indexErrorMessage := "index error"
guard = monkey.Patch((*blockIndex).flushToDB, func(_ *blockIndex) error {
return errors.New(indexErrorMessage)
})
defer guard.Unpatch()
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to index error: "+
"Expected %s, got: <nil>", indexErrorMessage)
}
if !strings.Contains(err.Error(), indexErrorMessage) {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to index error: "+
"Unexpected error. Want: %s, got: %s", indexErrorMessage, err)
}
}

88
blockdag/blockheap.go Normal file
View File

@@ -0,0 +1,88 @@
package blockdag
import (
"container/heap"
"github.com/kaspanet/kaspad/util/daghash"
)
// baseHeap is an implementation for heap.Interface that sorts blocks by their height
type baseHeap []*blockNode
func (h baseHeap) Len() int { return len(h) }
func (h baseHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *baseHeap) Push(x interface{}) {
*h = append(*h, x.(*blockNode))
}
func (h *baseHeap) Pop() interface{} {
oldHeap := *h
oldLength := len(oldHeap)
popped := oldHeap[oldLength-1]
*h = oldHeap[0 : oldLength-1]
return popped
}
// upHeap extends baseHeap to include Less operation that traverses from bottom to top
type upHeap struct{ baseHeap }
func (h upHeap) Less(i, j int) bool {
if h.baseHeap[i].blueScore == h.baseHeap[j].blueScore {
return daghash.HashToBig(h.baseHeap[i].hash).Cmp(daghash.HashToBig(h.baseHeap[j].hash)) < 0
}
return h.baseHeap[i].blueScore < h.baseHeap[j].blueScore
}
// downHeap extends baseHeap to include Less operation that traverses from top to bottom
type downHeap struct{ baseHeap }
func (h downHeap) Less(i, j int) bool {
if h.baseHeap[i].blueScore == h.baseHeap[j].blueScore {
return daghash.HashToBig(h.baseHeap[i].hash).Cmp(daghash.HashToBig(h.baseHeap[j].hash)) > 0
}
return h.baseHeap[i].blueScore > h.baseHeap[j].blueScore
}
// blockHeap represents a mutable heap of Blocks, sorted by their height
type blockHeap struct {
impl heap.Interface
}
// newDownHeap initializes and returns a new blockHeap
func newDownHeap() blockHeap {
h := blockHeap{impl: &downHeap{}}
heap.Init(h.impl)
return h
}
// newUpHeap initializes and returns a new blockHeap
func newUpHeap() blockHeap {
h := blockHeap{impl: &upHeap{}}
heap.Init(h.impl)
return h
}
// pop removes the block with lowest height from this heap and returns it
func (bh blockHeap) pop() *blockNode {
return heap.Pop(bh.impl).(*blockNode)
}
// Push pushes the block onto the heap
func (bh blockHeap) Push(block *blockNode) {
heap.Push(bh.impl, block)
}
// pushSet pushes a blockset to the heap.
func (bh blockHeap) pushSet(bs blockSet) {
for _, block := range bs {
heap.Push(bh.impl, block)
}
}
// Len returns the length of this heap
func (bh blockHeap) Len() int {
return bh.impl.Len()
}

120
blockdag/blockheap_test.go Normal file
View File

@@ -0,0 +1,120 @@
package blockdag
import (
"testing"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/daghash"
)
// TestBlockHeap tests pushing, popping, and determining the length of the heap.
func TestBlockHeap(t *testing.T) {
block0Header := dagconfig.MainNetParams.GenesisBlock.Header
block0 := newBlockNode(&block0Header, newSet(), dagconfig.MainNetParams.K)
block100000Header := Block100000.Header
block100000 := newBlockNode(&block100000Header, setFromSlice(block0), dagconfig.MainNetParams.K)
block0smallHash := newBlockNode(&block0Header, newSet(), dagconfig.MainNetParams.K)
block0smallHash.hash = &daghash.Hash{}
tests := []struct {
name string
toPush []*blockNode
expectedLength int
expectedPopUp *blockNode
expectedPopDown *blockNode
}{
{
name: "empty heap must have length 0",
toPush: []*blockNode{},
expectedLength: 0,
expectedPopDown: nil,
expectedPopUp: nil,
},
{
name: "heap with one push must have length 1",
toPush: []*blockNode{block0},
expectedLength: 1,
expectedPopDown: nil,
expectedPopUp: nil,
},
{
name: "heap with one push and one pop",
toPush: []*blockNode{block0},
expectedLength: 0,
expectedPopDown: block0,
expectedPopUp: block0,
},
{
name: "push two blocks with different heights, heap shouldn't have to rebalance " +
"for down direction, but will have to rebalance for up direction",
toPush: []*blockNode{block100000, block0},
expectedLength: 1,
expectedPopDown: block100000,
expectedPopUp: block0,
},
{
name: "push two blocks with different heights, heap shouldn't have to rebalance " +
"for up direction, but will have to rebalance for down direction",
toPush: []*blockNode{block0, block100000},
expectedLength: 1,
expectedPopDown: block100000,
expectedPopUp: block0,
},
{
name: "push two blocks with equal heights but different hashes, heap shouldn't have to rebalance " +
"for down direction, but will have to rebalance for up direction",
toPush: []*blockNode{block0, block0smallHash},
expectedLength: 1,
expectedPopDown: block0,
expectedPopUp: block0smallHash,
},
{
name: "push two blocks with equal heights but different hashes, heap shouldn't have to rebalance " +
"for up direction, but will have to rebalance for down direction",
toPush: []*blockNode{block0smallHash, block0},
expectedLength: 1,
expectedPopDown: block0,
expectedPopUp: block0smallHash,
},
}
for _, test := range tests {
dHeap := newDownHeap()
for _, block := range test.toPush {
dHeap.Push(block)
}
var poppedBlock *blockNode
if test.expectedPopDown != nil {
poppedBlock = dHeap.pop()
}
if dHeap.Len() != test.expectedLength {
t.Errorf("unexpected down heap length in test \"%s\". "+
"Expected: %v, got: %v", test.name, test.expectedLength, dHeap.Len())
}
if poppedBlock != test.expectedPopDown {
t.Errorf("unexpected popped block for down heap in test \"%s\". "+
"Expected: %v, got: %v", test.name, test.expectedPopDown, poppedBlock)
}
uHeap := newUpHeap()
for _, block := range test.toPush {
uHeap.Push(block)
}
poppedBlock = nil
if test.expectedPopUp != nil {
poppedBlock = uHeap.pop()
}
if uHeap.Len() != test.expectedLength {
t.Errorf("unexpected up heap length in test \"%s\". "+
"Expected: %v, got: %v", test.name, test.expectedLength, uHeap.Len())
}
if poppedBlock != test.expectedPopUp {
t.Errorf("unexpected popped block for up heap in test \"%s\". "+
"Expected: %v, got: %v", test.name, test.expectedPopDown, poppedBlock)
}
}
}

136
blockdag/blockidhash.go Normal file
View File

@@ -0,0 +1,136 @@
package blockdag
import (
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/pkg/errors"
)
var (
// idByHashIndexBucketName is the name of the db bucket used to house
// the block hash -> block id index.
idByHashIndexBucketName = []byte("idbyhashidx")
// hashByIDIndexBucketName is the name of the db bucket used to house
// the block id -> block hash index.
hashByIDIndexBucketName = []byte("hashbyididx")
currentBlockIDKey = []byte("currentblockid")
)
// -----------------------------------------------------------------------------
// This is a mapping between block hashes and unique IDs. The ID
// is simply a sequentially incremented uint64 that is used instead of block hash
// for the indexers. This is useful because it is only 8 bytes versus 32 bytes
// hashes and thus saves a ton of space when a block is referenced in an index.
// It consists of three buckets: the first bucket maps the hash of each
// block to the unique ID and the second maps that ID back to the block hash.
// The third bucket contains the last received block ID, and is used
// when starting the node to check that the enabled indexes are up to date
// with the latest received block, and if not, initiate recovery process.
//
// The serialized format for keys and values in the block hash to ID bucket is:
// <hash> = <ID>
//
// Field Type Size
// hash daghash.Hash 32 bytes
// ID uint64 8 bytes
// -----
// Total: 40 bytes
//
// The serialized format for keys and values in the ID to block hash bucket is:
// <ID> = <hash>
//
// Field Type Size
// ID uint64 8 bytes
// hash daghash.Hash 32 bytes
// -----
// Total: 40 bytes
//
// -----------------------------------------------------------------------------
const blockIDSize = 8 // 8 bytes for block ID
// DBFetchBlockIDByHash uses an existing database transaction to retrieve the
// block id for the provided hash from the index.
func DBFetchBlockIDByHash(dbTx database.Tx, hash *daghash.Hash) (uint64, error) {
hashIndex := dbTx.Metadata().Bucket(idByHashIndexBucketName)
serializedID := hashIndex.Get(hash[:])
if serializedID == nil {
return 0, errors.Errorf("no entry in the block ID index for block with hash %s", hash)
}
return DeserializeBlockID(serializedID), nil
}
// DBFetchBlockHashBySerializedID uses an existing database transaction to
// retrieve the hash for the provided serialized block id from the index.
func DBFetchBlockHashBySerializedID(dbTx database.Tx, serializedID []byte) (*daghash.Hash, error) {
idIndex := dbTx.Metadata().Bucket(hashByIDIndexBucketName)
hashBytes := idIndex.Get(serializedID)
if hashBytes == nil {
return nil, errors.Errorf("no entry in the block ID index for block with id %d", byteOrder.Uint64(serializedID))
}
var hash daghash.Hash
copy(hash[:], hashBytes)
return &hash, nil
}
// dbPutBlockIDIndexEntry uses an existing database transaction to update or add
// the index entries for the hash to id and id to hash mappings for the provided
// values.
func dbPutBlockIDIndexEntry(dbTx database.Tx, hash *daghash.Hash, serializedID []byte) error {
// Add the block hash to ID mapping to the index.
meta := dbTx.Metadata()
hashIndex := meta.Bucket(idByHashIndexBucketName)
if err := hashIndex.Put(hash[:], serializedID[:]); err != nil {
return err
}
// Add the block ID to hash mapping to the index.
idIndex := meta.Bucket(hashByIDIndexBucketName)
return idIndex.Put(serializedID[:], hash[:])
}
// DBFetchCurrentBlockID returns the last known block ID.
func DBFetchCurrentBlockID(dbTx database.Tx) uint64 {
serializedID := dbTx.Metadata().Get(currentBlockIDKey)
if serializedID == nil {
return 0
}
return DeserializeBlockID(serializedID)
}
// DeserializeBlockID returns a deserialized block id
func DeserializeBlockID(serializedID []byte) uint64 {
return byteOrder.Uint64(serializedID)
}
// SerializeBlockID returns a serialized block id
func SerializeBlockID(blockID uint64) []byte {
serializedBlockID := make([]byte, blockIDSize)
byteOrder.PutUint64(serializedBlockID, blockID)
return serializedBlockID
}
// DBFetchBlockHashByID uses an existing database transaction to retrieve the
// hash for the provided block id from the index.
func DBFetchBlockHashByID(dbTx database.Tx, id uint64) (*daghash.Hash, error) {
return DBFetchBlockHashBySerializedID(dbTx, SerializeBlockID(id))
}
func createBlockID(dbTx database.Tx, blockHash *daghash.Hash) (uint64, error) {
currentBlockID := DBFetchCurrentBlockID(dbTx)
newBlockID := currentBlockID + 1
serializedNewBlockID := SerializeBlockID(newBlockID)
err := dbTx.Metadata().Put(currentBlockIDKey, serializedNewBlockID)
if err != nil {
return 0, err
}
err = dbPutBlockIDIndexEntry(dbTx, blockHash, serializedNewBlockID)
if err != nil {
return 0, err
}
return newBlockID, nil
}

144
blockdag/blockindex.go Normal file
View File

@@ -0,0 +1,144 @@
// Copyright (c) 2015-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"sync"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util/daghash"
)
// blockIndex provides facilities for keeping track of an in-memory index of the
// block chain. Although the name block chain suggests a single chain of
// blocks, it is actually a tree-shaped structure where any node can have
// multiple children. However, there can only be one active branch which does
// indeed form a chain from the tip all the way back to the genesis block.
type blockIndex struct {
// The following fields are set when the instance is created and can't
// be changed afterwards, so there is no need to protect them with a
// separate mutex.
db database.DB
dagParams *dagconfig.Params
sync.RWMutex
index map[daghash.Hash]*blockNode
dirty map[*blockNode]struct{}
}
// newBlockIndex returns a new empty instance of a block index. The index will
// be dynamically populated as block nodes are loaded from the database and
// manually added.
func newBlockIndex(db database.DB, dagParams *dagconfig.Params) *blockIndex {
return &blockIndex{
db: db,
dagParams: dagParams,
index: make(map[daghash.Hash]*blockNode),
dirty: make(map[*blockNode]struct{}),
}
}
// HaveBlock returns whether or not the block index contains the provided hash.
//
// This function is safe for concurrent access.
func (bi *blockIndex) HaveBlock(hash *daghash.Hash) bool {
bi.RLock()
_, hasBlock := bi.index[*hash]
bi.RUnlock()
return hasBlock
}
// LookupNode returns the block node identified by the provided hash. It will
// return nil if there is no entry for the hash.
//
// This function is safe for concurrent access.
func (bi *blockIndex) LookupNode(hash *daghash.Hash) *blockNode {
bi.RLock()
node := bi.index[*hash]
bi.RUnlock()
return node
}
// AddNode adds the provided node to the block index and marks it as dirty.
// Duplicate entries are not checked so it is up to caller to avoid adding them.
//
// This function is safe for concurrent access.
func (bi *blockIndex) AddNode(node *blockNode) {
bi.Lock()
bi.addNode(node)
bi.dirty[node] = struct{}{}
bi.Unlock()
}
// addNode adds the provided node to the block index, but does not mark it as
// dirty. This can be used while initializing the block index.
//
// This function is NOT safe for concurrent access.
func (bi *blockIndex) addNode(node *blockNode) {
bi.index[*node.hash] = node
}
// NodeStatus provides concurrent-safe access to the status field of a node.
//
// This function is safe for concurrent access.
func (bi *blockIndex) NodeStatus(node *blockNode) blockStatus {
bi.RLock()
status := node.status
bi.RUnlock()
return status
}
// SetStatusFlags flips the provided status flags on the block node to on,
// regardless of whether they were on or off previously. This does not unset any
// flags currently on.
//
// This function is safe for concurrent access.
func (bi *blockIndex) SetStatusFlags(node *blockNode, flags blockStatus) {
bi.Lock()
node.status |= flags
bi.dirty[node] = struct{}{}
bi.Unlock()
}
// UnsetStatusFlags flips the provided status flags on the block node to off,
// regardless of whether they were on or off previously.
//
// This function is safe for concurrent access.
func (bi *blockIndex) UnsetStatusFlags(node *blockNode, flags blockStatus) {
bi.Lock()
node.status &^= flags
bi.dirty[node] = struct{}{}
bi.Unlock()
}
// flushToDB writes all dirty block nodes to the database. If all writes
// succeed, this clears the dirty set.
func (bi *blockIndex) flushToDB() error {
return bi.db.Update(func(dbTx database.Tx) error {
return bi.flushToDBWithTx(dbTx)
})
}
// flushToDBWithTx writes all dirty block nodes to the database. If all
// writes succeed, this clears the dirty set.
func (bi *blockIndex) flushToDBWithTx(dbTx database.Tx) error {
bi.Lock()
defer bi.Unlock()
if len(bi.dirty) == 0 {
return nil
}
for node := range bi.dirty {
err := dbStoreBlockNode(dbTx, node)
if err != nil {
return err
}
}
// If write was successful, clear the dirty set.
bi.dirty = make(map[*blockNode]struct{})
return nil
}

View File

@@ -0,0 +1,58 @@
package blockdag
import (
"github.com/pkg/errors"
"strings"
"testing"
"time"
"bou.ke/monkey"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/database"
)
func TestAncestorErrors(t *testing.T) {
node := newTestNode(newSet(), int32(0x10000000), 0, time.Unix(0, 0), dagconfig.MainNetParams.K)
node.chainHeight = 2
ancestor := node.SelectedAncestor(3)
if ancestor != nil {
t.Errorf("TestAncestorErrors: Ancestor() unexpectedly returned a node. Expected: <nil>")
}
}
func TestFlushToDBErrors(t *testing.T) {
// Create a new database and DAG instance to run tests against.
dag, teardownFunc, err := DAGSetup("TestFlushToDBErrors", Config{
DAGParams: &dagconfig.SimNetParams,
})
if err != nil {
t.Fatalf("TestFlushToDBErrors: Failed to setup DAG instance: %s", err)
}
defer teardownFunc()
// Call flushToDB without anything to flush. This should succeed
err = dag.index.flushToDB()
if err != nil {
t.Errorf("TestFlushToDBErrors: flushToDB without anything to flush: "+
"Unexpected flushToDB error: %s", err)
}
// Mark the genesis block as dirty
dag.index.SetStatusFlags(dag.genesis, statusValid)
// Test flushToDB failure due to database error
databaseErrorMessage := "database error"
guard := monkey.Patch(dbStoreBlockNode, func(_ database.Tx, _ *blockNode) error {
return errors.New(databaseErrorMessage)
})
defer guard.Unpatch()
err = dag.index.flushToDB()
if err == nil {
t.Errorf("TestFlushToDBErrors: flushToDB failure due to database error: "+
"Expected: %s, got: <nil>", databaseErrorMessage)
}
if !strings.Contains(err.Error(), databaseErrorMessage) {
t.Errorf("TestFlushToDBErrors: flushToDB failure due to database error: "+
"Unexpected flushToDB error. Expected: %s, got: %s", databaseErrorMessage, err)
}
}

143
blockdag/blocklocator.go Normal file
View File

@@ -0,0 +1,143 @@
package blockdag
import (
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
// BlockLocator is used to help locate a specific block. The algorithm for
// building the block locator is to add block hashes in reverse order on the
// block's selected parent chain until the desired stop block is reached.
// In order to keep the list of locator hashes to a reasonable number of entries,
// the step between each entry is doubled each loop iteration to exponentially
// decrease the number of hashes as a function of the distance from the block
// being located.
//
// For example, assume a selected parent chain with IDs as depicted below, and the
// stop block is genesis:
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
//
// The block locator for block 17 would be the hashes of blocks:
// [17 16 14 11 7 2 genesis]
type BlockLocator []*daghash.Hash
// BlockLocatorFromHashes returns a block locator from start and stop hash.
// See BlockLocator for details on the algorithm used to create a block locator.
//
// In addition to the general algorithm referenced above, this function will
// return the block locator for the selected tip if the passed hash is not currently
// known.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) BlockLocatorFromHashes(startHash, stopHash *daghash.Hash) BlockLocator {
dag.dagLock.RLock()
defer dag.dagLock.RUnlock()
startNode := dag.index.LookupNode(startHash)
var stopNode *blockNode
if !stopHash.IsEqual(&daghash.ZeroHash) {
stopNode = dag.index.LookupNode(stopHash)
}
return dag.blockLocator(startNode, stopNode)
}
// LatestBlockLocator returns a block locator for the current tips of the DAG.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) LatestBlockLocator() BlockLocator {
dag.dagLock.RLock()
defer dag.dagLock.RUnlock()
return dag.blockLocator(nil, nil)
}
// blockLocator returns a block locator for the passed start and stop nodes.
// The default value for the start node is the selected tip, and the default
// values of the stop node is the genesis block.
//
// See the BlockLocator type comments for more details.
//
// This function MUST be called with the DAG state lock held (for reads).
func (dag *BlockDAG) blockLocator(startNode, stopNode *blockNode) BlockLocator {
// Use the selected tip if requested.
if startNode == nil {
startNode = dag.virtual.selectedParent
}
if stopNode == nil {
stopNode = dag.genesis
}
// We use the selected parent of the start node, so the
// block locator won't contain the start node.
startNode = startNode.selectedParent
// If the start node or the stop node are not in the
// virtual's selected parent chain, we replace them with their
// closest selected parent that is part of the virtual's
// selected parent chain.
for !dag.IsInSelectedParentChain(stopNode.hash) {
stopNode = stopNode.selectedParent
}
for !dag.IsInSelectedParentChain(startNode.hash) {
startNode = startNode.selectedParent
}
// Calculate the max number of entries that will ultimately be in the
// block locator. See the description of the algorithm for how these
// numbers are derived.
// startNode.hash + stopNode.hash.
// Then floor(log2(startNode.chainHeight-stopNode.chainHeight)) entries for the skip portion.
maxEntries := 2 + util.FastLog2Floor(startNode.chainHeight-stopNode.chainHeight)
locator := make(BlockLocator, 0, maxEntries)
step := uint64(1)
for node := startNode; node != nil; {
locator = append(locator, node.hash)
// Nothing more to add once the stop node has been added.
if node.chainHeight == stopNode.chainHeight {
break
}
// Calculate chainHeight of previous node to include ensuring the
// final node is stopNode.
nextChainHeight := node.chainHeight - step
if nextChainHeight < stopNode.chainHeight {
nextChainHeight = stopNode.chainHeight
}
// walk backwards through the nodes to the correct ancestor.
node = node.SelectedAncestor(nextChainHeight)
// Double the distance between included hashes.
step *= 2
}
return locator
}
// FindNextLocatorBoundaries returns the lowest unknown block locator, hash
// and the highest known block locator hash. This is used to create the
// next block locator to find the highest shared known chain block with the
// sync peer.
//
// This function MUST be called with the DAG state lock held (for reads).
func (dag *BlockDAG) FindNextLocatorBoundaries(locator BlockLocator) (startHash, stopHash *daghash.Hash) {
// Find the most recent locator block hash in the DAG. In the case none of
// the hashes in the locator are in the DAG, fall back to the genesis block.
stopNode := dag.genesis
nextBlockLocatorIndex := int64(len(locator) - 1)
for i, hash := range locator {
node := dag.index.LookupNode(hash)
if node != nil {
stopNode = node
nextBlockLocatorIndex = int64(i) - 1
break
}
}
if nextBlockLocatorIndex < 0 {
return nil, stopNode.hash
}
return locator[nextBlockLocatorIndex], stopNode.hash
}

231
blockdag/blocknode.go Normal file
View File

@@ -0,0 +1,231 @@
// Copyright (c) 2015-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"fmt"
"time"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
)
// blockStatus is a bit field representing the validation state of the block.
type blockStatus byte
const (
// statusDataStored indicates that the block's payload is stored on disk.
statusDataStored blockStatus = 1 << iota
// statusValid indicates that the block has been fully validated.
statusValid
// statusValidateFailed indicates that the block has failed validation.
statusValidateFailed
// statusInvalidAncestor indicates that one of the block's ancestors has
// has failed validation, thus the block is also invalid.
statusInvalidAncestor
// statusNone indicates that the block has no validation state flags set.
//
// NOTE: This must be defined last in order to avoid influencing iota.
statusNone blockStatus = 0
)
// KnownValid returns whether the block is known to be valid. This will return
// false for a valid block that has not been fully validated yet.
func (status blockStatus) KnownValid() bool {
return status&statusValid != 0
}
// KnownInvalid returns whether the block is known to be invalid. This may be
// because the block itself failed validation or any of its ancestors is
// invalid. This will return false for invalid blocks that have not been proven
// invalid yet.
func (status blockStatus) KnownInvalid() bool {
return status&(statusValidateFailed|statusInvalidAncestor) != 0
}
// blockNode represents a block within the block DAG. The DAG is stored into
// the block database.
type blockNode struct {
// NOTE: Additions, deletions, or modifications to the order of the
// definitions in this struct should not be changed without considering
// how it affects alignment on 64-bit platforms. The current order is
// specifically crafted to result in minimal padding. There will be
// hundreds of thousands of these in memory, so a few extra bytes of
// padding adds up.
// parents is the parent blocks for this node.
parents blockSet
// selectedParent is the selected parent for this node.
// The selected parent is the parent that if chosen will maximize the blue score of this block
selectedParent *blockNode
// children are all the blocks that refer to this block as a parent
children blockSet
// blues are all blue blocks in this block's worldview that are in its selected parent anticone
blues []*blockNode
// blueScore is the count of all the blue blocks in this block's past
blueScore uint64
// hash is the double sha 256 of the block.
hash *daghash.Hash
// chainHeight is the number of hops you need to go down the selected parent chain in order to get to the genesis block.
chainHeight uint64
// Some fields from block headers to aid in best chain selection and
// reconstructing headers from memory. These must be treated as
// immutable and are intentionally ordered to avoid padding on 64-bit
// platforms.
version int32
bits uint32
nonce uint64
timestamp int64
hashMerkleRoot *daghash.Hash
acceptedIDMerkleRoot *daghash.Hash
utxoCommitment *daghash.Hash
// status is a bitfield representing the validation state of the block. The
// status field, unlike the other fields, may be written to and so should
// only be accessed using the concurrent-safe NodeStatus method on
// blockIndex once the node has been added to the global index.
status blockStatus
// isFinalized determines whether the node is below the finality point.
isFinalized bool
}
// initBlockNode initializes a block node from the given header and parent nodes.
// This function is NOT safe for concurrent access. It must only be called when
// initially creating a node.
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents blockSet, phantomK uint32) {
*node = blockNode{
parents: parents,
children: make(blockSet),
timestamp: time.Now().Unix(),
}
// blockHeader is nil only for the virtual block
if blockHeader != nil {
node.hash = blockHeader.BlockHash()
node.version = blockHeader.Version
node.bits = blockHeader.Bits
node.nonce = blockHeader.Nonce
node.timestamp = blockHeader.Timestamp.Unix()
node.hashMerkleRoot = blockHeader.HashMerkleRoot
node.acceptedIDMerkleRoot = blockHeader.AcceptedIDMerkleRoot
node.utxoCommitment = blockHeader.UTXOCommitment
} else {
node.hash = &daghash.ZeroHash
}
if len(parents) > 0 {
node.blues, node.selectedParent, node.blueScore = phantom(node, phantomK)
node.chainHeight = calculateChainHeight(node)
}
}
func calculateChainHeight(node *blockNode) uint64 {
if node.isGenesis() {
return 0
}
return node.selectedParent.chainHeight + 1
}
// newBlockNode returns a new block node for the given block header and parent
//nodes. This function is NOT safe for concurrent access.
func newBlockNode(blockHeader *wire.BlockHeader, parents blockSet, phantomK uint32) *blockNode {
var node blockNode
initBlockNode(&node, blockHeader, parents, phantomK)
return &node
}
// updateParentsChildren updates the node's parents to point to new node
func (node *blockNode) updateParentsChildren() {
for _, parent := range node.parents {
parent.children.add(node)
}
}
// Header constructs a block header from the node and returns it.
//
// This function is safe for concurrent access.
func (node *blockNode) Header() *wire.BlockHeader {
// No lock is needed because all accessed fields are immutable.
return &wire.BlockHeader{
Version: node.version,
ParentHashes: node.ParentHashes(),
HashMerkleRoot: node.hashMerkleRoot,
AcceptedIDMerkleRoot: node.acceptedIDMerkleRoot,
UTXOCommitment: node.utxoCommitment,
Timestamp: time.Unix(node.timestamp, 0),
Bits: node.bits,
Nonce: node.nonce,
}
}
// SelectedAncestor returns the ancestor block node at the provided chain-height by following
// the selected-parents chain backwards from this node. The returned block will be nil when a
// height is requested that is after the height of the passed node.
//
// This function is safe for concurrent access.
func (node *blockNode) SelectedAncestor(chainHeight uint64) *blockNode {
if chainHeight < 0 || chainHeight > node.chainHeight {
return nil
}
n := node
for ; n != nil && n.chainHeight != chainHeight; n = n.selectedParent {
// Intentionally left blank
}
return n
}
// RelativeAncestor returns the ancestor block node a relative 'distance' of
// chain-blocks before this node. This is equivalent to calling Ancestor with
// the node's chain-height minus provided distance.
//
// This function is safe for concurrent access.
func (node *blockNode) RelativeAncestor(distance uint64) *blockNode {
return node.SelectedAncestor(node.chainHeight - distance)
}
// CalcPastMedianTime returns the median time of the previous few blocks
// prior to, and including, the block node.
//
// This function is safe for concurrent access.
func (node *blockNode) PastMedianTime(dag *BlockDAG) time.Time {
window := blueBlockWindow(node, 2*dag.TimestampDeviationTolerance-1)
medianTimestamp, err := window.medianTimestamp()
if err != nil {
panic(fmt.Sprintf("blueBlockWindow: %s", err))
}
return time.Unix(medianTimestamp, 0)
}
func (node *blockNode) ParentHashes() []*daghash.Hash {
return node.parents.hashes()
}
// isGenesis returns if the current block is the genesis block
func (node *blockNode) isGenesis() bool {
return len(node.parents) == 0
}
func (node *blockNode) finalityScore(dag *BlockDAG) uint64 {
return node.blueScore / uint64(dag.dagParams.FinalityInterval)
}
// String returns a string that contains the block hash.
func (node blockNode) String() string {
return node.hash.String()
}

View File

@@ -0,0 +1,86 @@
package blockdag
import (
"testing"
)
func TestChainHeight(t *testing.T) {
phantomK := uint32(2)
buildNode := buildNodeGenerator(phantomK, true)
node0 := buildNode(setFromSlice())
node1 := buildNode(setFromSlice(node0))
node2 := buildNode(setFromSlice(node0))
node3 := buildNode(setFromSlice(node0))
node4 := buildNode(setFromSlice(node1, node2, node3))
node5 := buildNode(setFromSlice(node1, node2, node3))
node6 := buildNode(setFromSlice(node1, node2, node3))
node7 := buildNode(setFromSlice(node0))
node8 := buildNode(setFromSlice(node7))
node9 := buildNode(setFromSlice(node8))
node10 := buildNode(setFromSlice(node9, node6))
// Because nodes 7 & 8 were mined secretly, node10's selected
// parent will be node6, although node9 is higher. So in this
// case, node10.height and node10.chainHeight will be different
tests := []struct {
node *blockNode
expectedChainHeight uint64
}{
{
node: node0,
expectedChainHeight: 0,
},
{
node: node1,
expectedChainHeight: 1,
},
{
node: node2,
expectedChainHeight: 1,
},
{
node: node3,
expectedChainHeight: 1,
},
{
node: node4,
expectedChainHeight: 2,
},
{
node: node5,
expectedChainHeight: 2,
},
{
node: node6,
expectedChainHeight: 2,
},
{
node: node7,
expectedChainHeight: 1,
},
{
node: node8,
expectedChainHeight: 2,
},
{
node: node9,
expectedChainHeight: 3,
},
{
node: node10,
expectedChainHeight: 3,
},
}
for _, test := range tests {
if test.node.chainHeight != test.expectedChainHeight {
t.Errorf("block %v expected chain height %v but got %v", test.node, test.expectedChainHeight, test.node.chainHeight)
}
if calculateChainHeight(test.node) != test.expectedChainHeight {
t.Errorf("block %v expected calculated chain height %v but got %v", test.node, test.expectedChainHeight, test.node.chainHeight)
}
}
}

151
blockdag/blockset.go Normal file
View File

@@ -0,0 +1,151 @@
package blockdag
import (
"strings"
"github.com/kaspanet/kaspad/util/daghash"
)
// blockSet implements a basic unsorted set of blocks
type blockSet map[daghash.Hash]*blockNode
// newSet creates a new, empty BlockSet
func newSet() blockSet {
return map[daghash.Hash]*blockNode{}
}
// setFromSlice converts a slice of blocks into an unordered set represented as map
func setFromSlice(blocks ...*blockNode) blockSet {
set := newSet()
for _, block := range blocks {
set.add(block)
}
return set
}
// add adds a block to this BlockSet
func (bs blockSet) add(block *blockNode) {
bs[*block.hash] = block
}
// remove removes a block from this BlockSet, if exists
// Does nothing if this set does not contain the block
func (bs blockSet) remove(block *blockNode) {
delete(bs, *block.hash)
}
// clone clones thie block set
func (bs blockSet) clone() blockSet {
clone := newSet()
for _, block := range bs {
clone.add(block)
}
return clone
}
// subtract returns the difference between the BlockSet and another BlockSet
func (bs blockSet) subtract(other blockSet) blockSet {
diff := newSet()
for _, block := range bs {
if !other.contains(block) {
diff.add(block)
}
}
return diff
}
// addSet adds all blocks in other set to this set
func (bs blockSet) addSet(other blockSet) {
for _, block := range other {
bs.add(block)
}
}
// addSlice adds provided slice to this set
func (bs blockSet) addSlice(slice []*blockNode) {
for _, block := range slice {
bs.add(block)
}
}
// union returns a BlockSet that contains all blocks included in this set,
// the other set, or both
func (bs blockSet) union(other blockSet) blockSet {
union := bs.clone()
union.addSet(other)
return union
}
// contains returns true iff this set contains block
func (bs blockSet) contains(block *blockNode) bool {
_, ok := bs[*block.hash]
return ok
}
// containsHash returns true iff this set contains a block hash
func (bs blockSet) containsHash(hash *daghash.Hash) bool {
_, ok := bs[*hash]
return ok
}
// hashesEqual returns true if the given hashes are equal to the hashes
// of the blocks in this set.
// NOTE: The given hash slice must not contain duplicates.
func (bs blockSet) hashesEqual(hashes []*daghash.Hash) bool {
if len(hashes) != len(bs) {
return false
}
for _, hash := range hashes {
if _, wasFound := bs[*hash]; !wasFound {
return false
}
}
return true
}
// hashes returns the hashes of the blocks in this set.
func (bs blockSet) hashes() []*daghash.Hash {
hashes := make([]*daghash.Hash, 0, len(bs))
for _, node := range bs {
hashes = append(hashes, node.hash)
}
daghash.Sort(hashes)
return hashes
}
func (bs blockSet) String() string {
nodeStrs := make([]string, 0, len(bs))
for _, node := range bs {
nodeStrs = append(nodeStrs, node.String())
}
return strings.Join(nodeStrs, ",")
}
// anyChildInSet returns true iff any child of block is contained within this set
func (bs blockSet) anyChildInSet(block *blockNode) bool {
for _, child := range block.children {
if bs.contains(child) {
return true
}
}
return false
}
func (bs blockSet) bluest() *blockNode {
var bluestNode *blockNode
var maxScore uint64
for _, node := range bs {
if bluestNode == nil ||
node.blueScore > maxScore ||
(node.blueScore == maxScore && daghash.Less(node.hash, bluestNode.hash)) {
bluestNode = node
maxScore = node.blueScore
}
}
return bluestNode
}

296
blockdag/blockset_test.go Normal file
View File

@@ -0,0 +1,296 @@
package blockdag
import (
"reflect"
"testing"
"github.com/kaspanet/kaspad/util/daghash"
)
func TestHashes(t *testing.T) {
bs := setFromSlice(
&blockNode{
hash: &daghash.Hash{3},
},
&blockNode{
hash: &daghash.Hash{1},
},
&blockNode{
hash: &daghash.Hash{0},
},
&blockNode{
hash: &daghash.Hash{2},
},
)
expected := []*daghash.Hash{
{0},
{1},
{2},
{3},
}
hashes := bs.hashes()
if !daghash.AreEqual(hashes, expected) {
t.Errorf("TestHashes: hashes order is %s but expected %s", hashes, expected)
}
}
func TestBlockSetSubtract(t *testing.T) {
node1 := &blockNode{hash: &daghash.Hash{10}}
node2 := &blockNode{hash: &daghash.Hash{20}}
node3 := &blockNode{hash: &daghash.Hash{30}}
tests := []struct {
name string
setA blockSet
setB blockSet
expectedResult blockSet
}{
{
name: "both sets empty",
setA: setFromSlice(),
setB: setFromSlice(),
expectedResult: setFromSlice(),
},
{
name: "subtract an empty set",
setA: setFromSlice(node1),
setB: setFromSlice(),
expectedResult: setFromSlice(node1),
},
{
name: "subtract from empty set",
setA: setFromSlice(),
setB: setFromSlice(node1),
expectedResult: setFromSlice(),
},
{
name: "subtract unrelated set",
setA: setFromSlice(node1),
setB: setFromSlice(node2),
expectedResult: setFromSlice(node1),
},
{
name: "typical case",
setA: setFromSlice(node1, node2),
setB: setFromSlice(node2, node3),
expectedResult: setFromSlice(node1),
},
}
for _, test := range tests {
result := test.setA.subtract(test.setB)
if !reflect.DeepEqual(result, test.expectedResult) {
t.Errorf("blockSet.subtract: unexpected result in test '%s'. "+
"Expected: %v, got: %v", test.name, test.expectedResult, result)
}
}
}
func TestBlockSetAddSet(t *testing.T) {
node1 := &blockNode{hash: &daghash.Hash{10}}
node2 := &blockNode{hash: &daghash.Hash{20}}
node3 := &blockNode{hash: &daghash.Hash{30}}
tests := []struct {
name string
setA blockSet
setB blockSet
expectedResult blockSet
}{
{
name: "both sets empty",
setA: setFromSlice(),
setB: setFromSlice(),
expectedResult: setFromSlice(),
},
{
name: "add an empty set",
setA: setFromSlice(node1),
setB: setFromSlice(),
expectedResult: setFromSlice(node1),
},
{
name: "add to empty set",
setA: setFromSlice(),
setB: setFromSlice(node1),
expectedResult: setFromSlice(node1),
},
{
name: "add already added member",
setA: setFromSlice(node1, node2),
setB: setFromSlice(node1),
expectedResult: setFromSlice(node1, node2),
},
{
name: "typical case",
setA: setFromSlice(node1, node2),
setB: setFromSlice(node2, node3),
expectedResult: setFromSlice(node1, node2, node3),
},
}
for _, test := range tests {
test.setA.addSet(test.setB)
if !reflect.DeepEqual(test.setA, test.expectedResult) {
t.Errorf("blockSet.addSet: unexpected result in test '%s'. "+
"Expected: %v, got: %v", test.name, test.expectedResult, test.setA)
}
}
}
func TestBlockSetAddSlice(t *testing.T) {
node1 := &blockNode{hash: &daghash.Hash{10}}
node2 := &blockNode{hash: &daghash.Hash{20}}
node3 := &blockNode{hash: &daghash.Hash{30}}
tests := []struct {
name string
set blockSet
slice []*blockNode
expectedResult blockSet
}{
{
name: "add empty slice to empty set",
set: setFromSlice(),
slice: []*blockNode{},
expectedResult: setFromSlice(),
},
{
name: "add an empty slice",
set: setFromSlice(node1),
slice: []*blockNode{},
expectedResult: setFromSlice(node1),
},
{
name: "add to empty set",
set: setFromSlice(),
slice: []*blockNode{node1},
expectedResult: setFromSlice(node1),
},
{
name: "add already added member",
set: setFromSlice(node1, node2),
slice: []*blockNode{node1},
expectedResult: setFromSlice(node1, node2),
},
{
name: "typical case",
set: setFromSlice(node1, node2),
slice: []*blockNode{node2, node3},
expectedResult: setFromSlice(node1, node2, node3),
},
}
for _, test := range tests {
test.set.addSlice(test.slice)
if !reflect.DeepEqual(test.set, test.expectedResult) {
t.Errorf("blockSet.addSlice: unexpected result in test '%s'. "+
"Expected: %v, got: %v", test.name, test.expectedResult, test.set)
}
}
}
func TestBlockSetUnion(t *testing.T) {
node1 := &blockNode{hash: &daghash.Hash{10}}
node2 := &blockNode{hash: &daghash.Hash{20}}
node3 := &blockNode{hash: &daghash.Hash{30}}
tests := []struct {
name string
setA blockSet
setB blockSet
expectedResult blockSet
}{
{
name: "both sets empty",
setA: setFromSlice(),
setB: setFromSlice(),
expectedResult: setFromSlice(),
},
{
name: "union against an empty set",
setA: setFromSlice(node1),
setB: setFromSlice(),
expectedResult: setFromSlice(node1),
},
{
name: "union from an empty set",
setA: setFromSlice(),
setB: setFromSlice(node1),
expectedResult: setFromSlice(node1),
},
{
name: "union with subset",
setA: setFromSlice(node1, node2),
setB: setFromSlice(node1),
expectedResult: setFromSlice(node1, node2),
},
{
name: "typical case",
setA: setFromSlice(node1, node2),
setB: setFromSlice(node2, node3),
expectedResult: setFromSlice(node1, node2, node3),
},
}
for _, test := range tests {
result := test.setA.union(test.setB)
if !reflect.DeepEqual(result, test.expectedResult) {
t.Errorf("blockSet.union: unexpected result in test '%s'. "+
"Expected: %v, got: %v", test.name, test.expectedResult, result)
}
}
}
func TestBlockSetHashesEqual(t *testing.T) {
node1 := &blockNode{hash: &daghash.Hash{10}}
node2 := &blockNode{hash: &daghash.Hash{20}}
tests := []struct {
name string
set blockSet
hashes []*daghash.Hash
expectedResult bool
}{
{
name: "empty set, no hashes",
set: setFromSlice(),
hashes: []*daghash.Hash{},
expectedResult: true,
},
{
name: "empty set, one hash",
set: setFromSlice(),
hashes: []*daghash.Hash{node1.hash},
expectedResult: false,
},
{
name: "set and hashes of different length",
set: setFromSlice(node1, node2),
hashes: []*daghash.Hash{node1.hash},
expectedResult: false,
},
{
name: "set equal to hashes",
set: setFromSlice(node1, node2),
hashes: []*daghash.Hash{node1.hash, node2.hash},
expectedResult: true,
},
{
name: "set equal to hashes, different order",
set: setFromSlice(node1, node2),
hashes: []*daghash.Hash{node2.hash, node1.hash},
expectedResult: true,
},
}
for _, test := range tests {
result := test.set.hashesEqual(test.hashes)
if result != test.expectedResult {
t.Errorf("blockSet.hashesEqual: unexpected result in test '%s'. "+
"Expected: %t, got: %t", test.name, test.expectedResult, result)
}
}
}

75
blockdag/blockwindow.go Normal file
View File

@@ -0,0 +1,75 @@
package blockdag
import (
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
"math"
"math/big"
"sort"
)
type blockWindow []*blockNode
// blueBlockWindow returns a blockWindow of the given size that contains the
// blues in the past of startindNode, sorted by phantom order.
// If the number of blues in the past of startingNode is less then windowSize,
// the window will be padded by genesis blocks to achieve a size of windowSize.
func blueBlockWindow(startingNode *blockNode, windowSize uint64) blockWindow {
window := make(blockWindow, 0, windowSize)
currentNode := startingNode
for uint64(len(window)) < windowSize && currentNode.selectedParent != nil {
if currentNode.selectedParent != nil {
for _, blue := range currentNode.blues {
window = append(window, blue)
if uint64(len(window)) == windowSize {
break
}
}
currentNode = currentNode.selectedParent
}
}
if uint64(len(window)) < windowSize {
genesis := currentNode
for uint64(len(window)) < windowSize {
window = append(window, genesis)
}
}
return window
}
func (window blockWindow) minMaxTimestamps() (min, max int64) {
min = math.MaxInt64
max = 0
for _, node := range window {
if node.timestamp < min {
min = node.timestamp
}
if node.timestamp > max {
max = node.timestamp
}
}
return
}
func (window blockWindow) averageTarget() *big.Int {
averageTarget := big.NewInt(0)
for _, node := range window {
target := util.CompactToBig(node.bits)
averageTarget.Add(averageTarget, target)
}
return averageTarget.Div(averageTarget, big.NewInt(int64(len(window))))
}
func (window blockWindow) medianTimestamp() (int64, error) {
if len(window) == 0 {
return 0, errors.New("Cannot calculate median timestamp for an empty block window")
}
timestamps := make([]int64, len(window))
for i, node := range window {
timestamps[i] = node.timestamp
}
sort.Sort(timeSorter(timestamps))
return timestamps[len(timestamps)/2], nil
}

View File

@@ -0,0 +1,138 @@
package blockdag
import (
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/pkg/errors"
"reflect"
"testing"
"time"
)
func TestBlueBlockWindow(t *testing.T) {
params := dagconfig.SimNetParams
params.K = 1
dag := newTestDAG(&params)
windowSize := uint64(10)
genesisNode := dag.genesis
blockTime := genesisNode.Header().Timestamp
blockByIDMap := make(map[string]*blockNode)
idByBlockMap := make(map[*blockNode]string)
blockByIDMap["A"] = genesisNode
idByBlockMap[genesisNode] = "A"
blockVersion := int32(0x10000000)
blocksData := []*struct {
parents []string
id string //id is a virtual entity that is used only for tests so we can define relations between blocks without knowing their hash
expectedWindowWithGenesisPadding []string
}{
{
parents: []string{"A"},
id: "B",
expectedWindowWithGenesisPadding: []string{"A", "A", "A", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"B"},
id: "C",
expectedWindowWithGenesisPadding: []string{"B", "A", "A", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"B"},
id: "D",
expectedWindowWithGenesisPadding: []string{"B", "A", "A", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"C", "D"},
id: "E",
expectedWindowWithGenesisPadding: []string{"D", "C", "B", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"C", "D"},
id: "F",
expectedWindowWithGenesisPadding: []string{"D", "C", "B", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"A"},
id: "G",
expectedWindowWithGenesisPadding: []string{"A", "A", "A", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"G"},
id: "H",
expectedWindowWithGenesisPadding: []string{"G", "A", "A", "A", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"H", "F"},
id: "I",
expectedWindowWithGenesisPadding: []string{"F", "D", "C", "B", "A", "A", "A", "A", "A", "A"},
},
{
parents: []string{"I"},
id: "J",
expectedWindowWithGenesisPadding: []string{"I", "F", "D", "C", "B", "A", "A", "A", "A", "A"},
},
{
parents: []string{"J"},
id: "K",
expectedWindowWithGenesisPadding: []string{"J", "I", "F", "D", "C", "B", "A", "A", "A", "A"},
},
{
parents: []string{"K"},
id: "L",
expectedWindowWithGenesisPadding: []string{"K", "J", "I", "F", "D", "C", "B", "A", "A", "A"},
},
{
parents: []string{"L"},
id: "M",
expectedWindowWithGenesisPadding: []string{"L", "K", "J", "I", "F", "D", "C", "B", "A", "A"},
},
{
parents: []string{"M"},
id: "N",
expectedWindowWithGenesisPadding: []string{"M", "L", "K", "J", "I", "F", "D", "C", "B", "A"},
},
{
parents: []string{"N"},
id: "O",
expectedWindowWithGenesisPadding: []string{"N", "M", "L", "K", "J", "I", "F", "D", "C", "B"},
},
}
for _, blockData := range blocksData {
blockTime = blockTime.Add(time.Second)
parents := blockSet{}
for _, parentID := range blockData.parents {
parent := blockByIDMap[parentID]
parents.add(parent)
}
node := newTestNode(parents, blockVersion, 0, blockTime, dag.dagParams.K)
node.hash = &daghash.Hash{} // It helps to predict hash order
for i, char := range blockData.id {
node.hash[i] = byte(char)
}
dag.index.AddNode(node)
node.updateParentsChildren()
blockByIDMap[blockData.id] = node
idByBlockMap[node] = blockData.id
window := blueBlockWindow(node, windowSize)
if err := checkWindowIDs(window, blockData.expectedWindowWithGenesisPadding, idByBlockMap); err != nil {
t.Errorf("Unexpected values for window for block %s: %s", blockData.id, err)
}
}
}
func checkWindowIDs(window []*blockNode, expectedIDs []string, idByBlockMap map[*blockNode]string) error {
ids := make([]string, len(window))
for i, node := range window {
ids[i] = idByBlockMap[node]
}
if !reflect.DeepEqual(ids, expectedIDs) {
return errors.Errorf("window expected to have blocks %s but got %s", expectedIDs, ids)
}
return nil
}

258
blockdag/checkpoints.go Normal file
View File

@@ -0,0 +1,258 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"fmt"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/pkg/errors"
)
// CheckpointConfirmations is the number of blocks before the end of the current
// best block chain that a good checkpoint candidate must be.
const CheckpointConfirmations = 2016
// newHashFromStr converts the passed big-endian hex string into a
// daghash.Hash. It only differs from the one available in daghash in that
// it ignores the error since it will only (and must only) be called with
// hard-coded, and therefore known good, hashes.
func newHashFromStr(hexStr string) *daghash.Hash {
hash, _ := daghash.NewHashFromStr(hexStr)
return hash
}
// newTxIDFromStr converts the passed big-endian hex string into a
// daghash.TxID. It only differs from the one available in daghash in that
// it ignores the error since it will only (and must only) be called with
// hard-coded, and therefore known good, IDs.
func newTxIDFromStr(hexStr string) *daghash.TxID {
txID, _ := daghash.NewTxIDFromStr(hexStr)
return txID
}
// Checkpoints returns a slice of checkpoints (regardless of whether they are
// already known). When there are no checkpoints for the chain, it will return
// nil.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) Checkpoints() []dagconfig.Checkpoint {
return dag.checkpoints
}
// HasCheckpoints returns whether this BlockDAG has checkpoints defined.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) HasCheckpoints() bool {
return len(dag.checkpoints) > 0
}
// LatestCheckpoint returns the most recent checkpoint (regardless of whether it
// is already known). When there are no defined checkpoints for the active chain
// instance, it will return nil.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) LatestCheckpoint() *dagconfig.Checkpoint {
if !dag.HasCheckpoints() {
return nil
}
return &dag.checkpoints[len(dag.checkpoints)-1]
}
// verifyCheckpoint returns whether the passed block chain height and hash combination
// match the checkpoint data. It also returns true if there is no checkpoint
// data for the passed block chain height.
func (dag *BlockDAG) verifyCheckpoint(chainHeight uint64, hash *daghash.Hash) bool {
if !dag.HasCheckpoints() {
return true
}
// Nothing to check if there is no checkpoint data for the block chainHeight.
checkpoint, exists := dag.checkpointsByChainHeight[chainHeight]
if !exists {
return true
}
if !checkpoint.Hash.IsEqual(hash) {
return false
}
log.Infof("Verified checkpoint at chainHeight %d/block %s", checkpoint.ChainHeight,
checkpoint.Hash)
return true
}
// findPreviousCheckpoint finds the most recent checkpoint that is already
// available in the downloaded portion of the block chain and returns the
// associated block node. It returns nil if a checkpoint can't be found (this
// should really only happen for blocks before the first checkpoint).
//
// This function MUST be called with the DAG lock held (for reads).
func (dag *BlockDAG) findPreviousCheckpoint() (*blockNode, error) {
if !dag.HasCheckpoints() {
return nil, nil
}
// Perform the initial search to find and cache the latest known
// checkpoint if the best chain is not known yet or we haven't already
// previously searched.
checkpoints := dag.checkpoints
numCheckpoints := len(checkpoints)
if dag.checkpointNode == nil && dag.nextCheckpoint == nil {
// Loop backwards through the available checkpoints to find one
// that is already available.
for i := numCheckpoints - 1; i >= 0; i-- {
node := dag.index.LookupNode(checkpoints[i].Hash)
if node == nil {
continue
}
// Checkpoint found. Cache it for future lookups and
// set the next expected checkpoint accordingly.
dag.checkpointNode = node
if i < numCheckpoints-1 {
dag.nextCheckpoint = &checkpoints[i+1]
}
return dag.checkpointNode, nil
}
// No known latest checkpoint. This will only happen on blocks
// before the first known checkpoint. So, set the next expected
// checkpoint to the first checkpoint and return the fact there
// is no latest known checkpoint block.
dag.nextCheckpoint = &checkpoints[0]
return nil, nil
}
// At this point we've already searched for the latest known checkpoint,
// so when there is no next checkpoint, the current checkpoint lockin
// will always be the latest known checkpoint.
if dag.nextCheckpoint == nil {
return dag.checkpointNode, nil
}
// When there is a next checkpoint and the chain height of the current
// selected tip of the DAG does not exceed it, the current checkpoint
// lockin is still the latest known checkpoint.
if dag.selectedTip().chainHeight < dag.nextCheckpoint.ChainHeight {
return dag.checkpointNode, nil
}
// We've reached or exceeded the next checkpoint height. Note that
// once a checkpoint lockin has been reached, forks are prevented from
// any blocks before the checkpoint, so we don't have to worry about the
// checkpoint going away out from under us due to a chain reorganize.
// Cache the latest known checkpoint for future lookups. Note that if
// this lookup fails something is very wrong since the chain has already
// passed the checkpoint which was verified as accurate before inserting
// it.
checkpointNode := dag.index.LookupNode(dag.nextCheckpoint.Hash)
if checkpointNode == nil {
return nil, AssertError(fmt.Sprintf("findPreviousCheckpoint "+
"failed lookup of known good block node %s",
dag.nextCheckpoint.Hash))
}
dag.checkpointNode = checkpointNode
// Set the next expected checkpoint.
checkpointIndex := -1
for i := numCheckpoints - 1; i >= 0; i-- {
if checkpoints[i].Hash.IsEqual(dag.nextCheckpoint.Hash) {
checkpointIndex = i
break
}
}
dag.nextCheckpoint = nil
if checkpointIndex != -1 && checkpointIndex < numCheckpoints-1 {
dag.nextCheckpoint = &checkpoints[checkpointIndex+1]
}
return dag.checkpointNode, nil
}
// isNonstandardTransaction determines whether a transaction contains any
// scripts which are not one of the standard types.
func isNonstandardTransaction(tx *util.Tx) bool {
// Check all of the output public key scripts for non-standard scripts.
for _, txOut := range tx.MsgTx().TxOut {
scriptClass := txscript.GetScriptClass(txOut.ScriptPubKey)
if scriptClass == txscript.NonStandardTy {
return true
}
}
return false
}
// IsCheckpointCandidate returns whether or not the passed block is a good
// checkpoint candidate.
//
// The factors used to determine a good checkpoint are:
// - The block must be in the main chain
// - The block must be at least 'CheckpointConfirmations' blocks prior to the
// current end of the main chain
// - The timestamps for the blocks before and after the checkpoint must have
// timestamps which are also before and after the checkpoint, respectively
// (due to the median time allowance this is not always the case)
// - The block must not contain any strange transaction such as those with
// nonstandard scripts
//
// The intent is that candidates are reviewed by a developer to make the final
// decision and then manually added to the list of checkpoints for a network.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) IsCheckpointCandidate(block *util.Block) (bool, error) {
dag.dagLock.RLock()
defer dag.dagLock.RUnlock()
// A checkpoint must be in the DAG.
node := dag.index.LookupNode(block.Hash())
if node == nil {
return false, nil
}
// Ensure the chain height of the passed block and the entry for the block
// in the DAG match. This should always be the case unless the
// caller provided an invalid block.
if node.chainHeight != block.ChainHeight() {
return false, errors.Errorf("passed block chain height of %d does not "+
"match the its height in the DAG: %d", block.ChainHeight(),
node.chainHeight)
}
// A checkpoint must be at least CheckpointConfirmations blocks
// before the end of the main chain.
dagChainHeight := dag.selectedTip().chainHeight
if node.chainHeight > (dagChainHeight - CheckpointConfirmations) {
return false, nil
}
// A checkpoint must be have at least one block after it.
//
// This should always succeed since the check above already made sure it
// is CheckpointConfirmations back, but be safe in case the constant
// changes.
if len(node.children) == 0 {
return false, nil
}
// A checkpoint must be have at least one block before it.
if &node.selectedParent == nil {
return false, nil
}
// A checkpoint must have transactions that only contain standard
// scripts.
for _, tx := range block.Transactions() {
if isNonstandardTransaction(tx) {
return false, nil
}
}
// All of the checks passed, so the block is a candidate.
return true, nil
}

278
blockdag/coinbase.go Normal file
View File

@@ -0,0 +1,278 @@
package blockdag
import (
"bufio"
"bytes"
"encoding/binary"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/pkg/errors"
"io"
"math"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/txsort"
"github.com/kaspanet/kaspad/wire"
)
// compactFeeData is a specialized data type to store a compact list of fees
// inside a block.
// Every transaction gets a single uint64 value, stored as a plain binary list.
// The transactions are ordered the same way they are ordered inside the block, making it easy
// to traverse every transaction in a block and extract its fee.
//
// compactFeeFactory is used to create such a list.
// compactFeeIterator is used to iterate over such a list.
type compactFeeData []byte
func (cfd compactFeeData) Len() int {
return len(cfd) / 8
}
type compactFeeFactory struct {
buffer *bytes.Buffer
writer *bufio.Writer
}
func newCompactFeeFactory() *compactFeeFactory {
buffer := bytes.NewBuffer([]byte{})
return &compactFeeFactory{
buffer: buffer,
writer: bufio.NewWriter(buffer),
}
}
func (cfw *compactFeeFactory) add(txFee uint64) error {
return binary.Write(cfw.writer, binary.LittleEndian, txFee)
}
func (cfw *compactFeeFactory) data() (compactFeeData, error) {
err := cfw.writer.Flush()
return compactFeeData(cfw.buffer.Bytes()), err
}
type compactFeeIterator struct {
reader io.Reader
}
func (cfd compactFeeData) iterator() *compactFeeIterator {
return &compactFeeIterator{
reader: bufio.NewReader(bytes.NewBuffer(cfd)),
}
}
func (cfr *compactFeeIterator) next() (uint64, error) {
var txFee uint64
err := binary.Read(cfr.reader, binary.LittleEndian, &txFee)
return txFee, err
}
// The following functions relate to storing and retrieving fee data from the database
var feeBucket = []byte("fees")
// getBluesFeeData returns the compactFeeData for all nodes's blues,
// used to calculate the fees this blockNode needs to pay
func (node *blockNode) getBluesFeeData(dag *BlockDAG) (map[daghash.Hash]compactFeeData, error) {
bluesFeeData := make(map[daghash.Hash]compactFeeData)
err := dag.db.View(func(dbTx database.Tx) error {
for _, blueBlock := range node.blues {
feeData, err := dbFetchFeeData(dbTx, blueBlock.hash)
if err != nil {
return errors.Errorf("Error getting fee data for block %s: %s", blueBlock.hash, err)
}
bluesFeeData[*blueBlock.hash] = feeData
}
return nil
})
if err != nil {
return nil, err
}
return bluesFeeData, nil
}
func dbStoreFeeData(dbTx database.Tx, blockHash *daghash.Hash, feeData compactFeeData) error {
feeBucket, err := dbTx.Metadata().CreateBucketIfNotExists(feeBucket)
if err != nil {
return errors.Errorf("Error creating or retrieving fee bucket: %s", err)
}
return feeBucket.Put(blockHash.CloneBytes(), feeData)
}
func dbFetchFeeData(dbTx database.Tx, blockHash *daghash.Hash) (compactFeeData, error) {
feeBucket := dbTx.Metadata().Bucket(feeBucket)
if feeBucket == nil {
return nil, errors.New("Fee bucket does not exist")
}
feeData := feeBucket.Get(blockHash.CloneBytes())
if feeData == nil {
return nil, errors.Errorf("No fee data found for block %s", blockHash)
}
return feeData, nil
}
// The following functions deal with building and validating the coinbase transaction
func (node *blockNode) validateCoinbaseTransaction(dag *BlockDAG, block *util.Block, txsAcceptanceData MultiBlockTxsAcceptanceData) error {
if node.isGenesis() {
return nil
}
blockCoinbaseTx := block.CoinbaseTransaction().MsgTx()
scriptPubKey, extraData, err := DeserializeCoinbasePayload(blockCoinbaseTx)
if err != nil {
return err
}
expectedCoinbaseTransaction, err := node.expectedCoinbaseTransaction(dag, txsAcceptanceData, scriptPubKey, extraData)
if err != nil {
return err
}
if !expectedCoinbaseTransaction.Hash().IsEqual(block.CoinbaseTransaction().Hash()) {
return ruleError(ErrBadCoinbaseTransaction, "Coinbase transaction is not built as expected")
}
return nil
}
// expectedCoinbaseTransaction returns the coinbase transaction for the current block
func (node *blockNode) expectedCoinbaseTransaction(dag *BlockDAG, txsAcceptanceData MultiBlockTxsAcceptanceData, scriptPubKey []byte, extraData []byte) (*util.Tx, error) {
bluesFeeData, err := node.getBluesFeeData(dag)
if err != nil {
return nil, err
}
txIns := []*wire.TxIn{}
txOuts := []*wire.TxOut{}
for _, blue := range node.blues {
txIn, txOut, err := coinbaseInputAndOutputForBlueBlock(dag, blue, txsAcceptanceData, bluesFeeData)
if err != nil {
return nil, err
}
txIns = append(txIns, txIn)
if txOut != nil {
txOuts = append(txOuts, txOut)
}
}
payload, err := SerializeCoinbasePayload(scriptPubKey, extraData)
if err != nil {
return nil, err
}
coinbaseTx := wire.NewSubnetworkMsgTx(wire.TxVersion, txIns, txOuts, subnetworkid.SubnetworkIDCoinbase, 0, payload)
sortedCoinbaseTx := txsort.Sort(coinbaseTx)
return util.NewTx(sortedCoinbaseTx), nil
}
// SerializeCoinbasePayload builds the coinbase payload based on the provided scriptPubKey and extra data.
func SerializeCoinbasePayload(scriptPubKey []byte, extraData []byte) ([]byte, error) {
w := &bytes.Buffer{}
err := wire.WriteVarInt(w, uint64(len(scriptPubKey)))
if err != nil {
return nil, err
}
_, err = w.Write(scriptPubKey)
if err != nil {
return nil, err
}
_, err = w.Write(extraData)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
// DeserializeCoinbasePayload deserialize the coinbase payload to its component (scriptPubKey and extra data).
func DeserializeCoinbasePayload(tx *wire.MsgTx) (scriptPubKey []byte, extraData []byte, err error) {
r := bytes.NewReader(tx.Payload)
scriptPubKeyLen, err := wire.ReadVarInt(r)
if err != nil {
return nil, nil, err
}
scriptPubKey = make([]byte, scriptPubKeyLen)
_, err = r.Read(scriptPubKey)
if err != nil {
return nil, nil, err
}
extraData = make([]byte, r.Len())
if r.Len() != 0 {
_, err = r.Read(extraData)
if err != nil {
return nil, nil, err
}
}
return scriptPubKey, extraData, nil
}
// feeInputAndOutputForBlueBlock calculates the input and output that should go into the coinbase transaction of blueBlock
// If blueBlock gets no fee - returns only txIn and nil for txOut
func coinbaseInputAndOutputForBlueBlock(dag *BlockDAG, blueBlock *blockNode,
txsAcceptanceData MultiBlockTxsAcceptanceData, feeData map[daghash.Hash]compactFeeData) (
*wire.TxIn, *wire.TxOut, error) {
blockTxsAcceptanceData, ok := txsAcceptanceData.FindAcceptanceData(blueBlock.hash)
if !ok {
return nil, nil, errors.Errorf("No txsAcceptanceData for block %s", blueBlock.hash)
}
blockFeeData, ok := feeData[*blueBlock.hash]
if !ok {
return nil, nil, errors.Errorf("No feeData for block %s", blueBlock.hash)
}
if len(blockTxsAcceptanceData.TxAcceptanceData) != blockFeeData.Len() {
return nil, nil, errors.Errorf(
"length of accepted transaction data(%d) and fee data(%d) is not equal for block %s",
len(blockTxsAcceptanceData.TxAcceptanceData), blockFeeData.Len(), blueBlock.hash)
}
txIn := &wire.TxIn{
SignatureScript: []byte{},
PreviousOutpoint: wire.Outpoint{
TxID: daghash.TxID(*blueBlock.hash),
Index: math.MaxUint32,
},
Sequence: wire.MaxTxInSequenceNum,
}
totalFees := uint64(0)
feeIterator := blockFeeData.iterator()
for _, txAcceptanceData := range blockTxsAcceptanceData.TxAcceptanceData {
fee, err := feeIterator.next()
if err != nil {
return nil, nil, errors.Errorf("Error retrieving fee from compactFeeData iterator: %s", err)
}
if txAcceptanceData.IsAccepted {
totalFees += fee
}
}
totalReward := CalcBlockSubsidy(blueBlock.blueScore, dag.dagParams) + totalFees
if totalReward == 0 {
return txIn, nil, nil
}
// the ScriptPubKey for the coinbase is parsed from the coinbase payload
scriptPubKey, _, err := DeserializeCoinbasePayload(blockTxsAcceptanceData.TxAcceptanceData[0].Tx.MsgTx())
if err != nil {
return nil, nil, err
}
txOut := &wire.TxOut{
Value: totalReward,
ScriptPubKey: scriptPubKey,
}
return txIn, txOut, nil
}

60
blockdag/coinbase_test.go Normal file
View File

@@ -0,0 +1,60 @@
package blockdag
import (
"io"
"reflect"
"testing"
)
func TestFeeAccumulators(t *testing.T) {
fees := []uint64{1, 2, 3, 4, 5, 6, 7, 0xffffffffffffffff}
factory := newCompactFeeFactory()
for _, fee := range fees {
err := factory.add(fee)
if err != nil {
t.Fatalf("Error writing %d as tx fee: %s", fee, err)
}
}
expectedData := compactFeeData{
1, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0,
5, 0, 0, 0, 0, 0, 0, 0,
6, 0, 0, 0, 0, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 255, 255, 255, 255,
}
actualData, err := factory.data()
if err != nil {
t.Fatalf("Error getting bytes from writer: %s", err)
}
if !reflect.DeepEqual(expectedData, actualData) {
t.Errorf("Expected bytes: %v, but got: %v", expectedData, actualData)
}
iterator := actualData.iterator()
for i, expectedFee := range fees {
actualFee, err := iterator.next()
if err != nil {
t.Fatalf("Error getting fee for Tx#%d: %s", i, err)
}
if actualFee != expectedFee {
t.Errorf("Tx #%d: Expected fee: %d, but got %d", i, expectedFee, actualFee)
}
}
_, err = iterator.next()
if err == nil {
t.Fatal("No error from iterator.nextTxFee after done reading all transactions")
}
if err != io.EOF {
t.Fatalf("Error from iterator.nextTxFee after done reading all transactions is not io.EOF: %s", err)
}
}

211
blockdag/common_test.go Normal file
View File

@@ -0,0 +1,211 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"compress/bzip2"
"encoding/binary"
"github.com/pkg/errors"
"io"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"time"
"github.com/kaspanet/kaspad/dagconfig"
_ "github.com/kaspanet/kaspad/database/ffldb"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
)
func loadBlocksWithLog(t *testing.T, filename string) ([]*util.Block, error) {
blocks, err := LoadBlocks(filename)
if err == nil {
t.Logf("Loaded %d blocks from file %s", len(blocks), filename)
for i, b := range blocks {
t.Logf("Block #%d: %s", i, b.Hash())
}
}
return blocks, err
}
// loadUTXOSet returns a utxo view loaded from a file.
func loadUTXOSet(filename string) (UTXOSet, error) {
// The utxostore file format is:
// <tx hash><output index><serialized utxo len><serialized utxo>
//
// The output index and serialized utxo len are little endian uint32s
// and the serialized utxo uses the format described in dagio.go.
filename = filepath.Join("testdata", filename)
fi, err := os.Open(filename)
if err != nil {
return nil, err
}
// Choose read based on whether the file is compressed or not.
var r io.Reader
if strings.HasSuffix(filename, ".bz2") {
r = bzip2.NewReader(fi)
} else {
r = fi
}
defer fi.Close()
utxoSet := NewFullUTXOSet()
for {
// Tx ID of the utxo entry.
var txID daghash.TxID
_, err := io.ReadAtLeast(r, txID[:], len(txID[:]))
if err != nil {
// Expected EOF at the right offset.
if err == io.EOF {
break
}
return nil, err
}
// Output index of the utxo entry.
var index uint32
err = binary.Read(r, binary.LittleEndian, &index)
if err != nil {
return nil, err
}
// Num of serialized utxo entry bytes.
var numBytes uint32
err = binary.Read(r, binary.LittleEndian, &numBytes)
if err != nil {
return nil, err
}
// Serialized utxo entry.
serialized := make([]byte, numBytes)
_, err = io.ReadAtLeast(r, serialized, int(numBytes))
if err != nil {
return nil, err
}
// Deserialize it and add it to the view.
entry, err := deserializeUTXOEntry(serialized)
if err != nil {
return nil, err
}
utxoSet.utxoCollection[wire.Outpoint{TxID: txID, Index: index}] = entry
}
return utxoSet, nil
}
// TestSetCoinbaseMaturity makes the ability to set the coinbase maturity
// available when running tests.
func (dag *BlockDAG) TestSetCoinbaseMaturity(maturity uint64) {
dag.dagParams.BlockCoinbaseMaturity = maturity
}
// newTestDAG returns a DAG that is usable for syntetic tests. It is
// important to note that this chain has no database associated with it, so
// it is not usable with all functions and the tests must take care when making
// use of it.
func newTestDAG(params *dagconfig.Params) *BlockDAG {
// Create a genesis block node and block index index populated with it
// for use when creating the fake chain below.
node := newBlockNode(&params.GenesisBlock.Header, newSet(), params.K)
index := newBlockIndex(nil, params)
index.AddNode(node)
targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
return &BlockDAG{
dagParams: params,
timeSource: NewMedianTime(),
targetTimePerBlock: targetTimePerBlock,
difficultyAdjustmentWindowSize: params.DifficultyAdjustmentWindowSize,
TimestampDeviationTolerance: params.TimestampDeviationTolerance,
powMaxBits: util.BigToCompact(params.PowMax),
index: index,
virtual: newVirtualBlock(setFromSlice(node), params.K),
genesis: index.LookupNode(params.GenesisHash),
warningCaches: newThresholdCaches(vbNumBits),
deploymentCaches: newThresholdCaches(dagconfig.DefinedDeployments),
}
}
// newTestNode creates a block node connected to the passed parent with the
// provided fields populated and fake values for the other fields.
func newTestNode(parents blockSet, blockVersion int32, bits uint32, timestamp time.Time, phantomK uint32) *blockNode {
// Make up a header and create a block node from it.
header := &wire.BlockHeader{
Version: blockVersion,
ParentHashes: parents.hashes(),
Bits: bits,
Timestamp: timestamp,
HashMerkleRoot: &daghash.ZeroHash,
AcceptedIDMerkleRoot: &daghash.ZeroHash,
UTXOCommitment: &daghash.ZeroHash,
}
return newBlockNode(header, parents, phantomK)
}
func addNodeAsChildToParents(node *blockNode) {
for _, parent := range node.parents {
parent.children.add(node)
}
}
func buildNodeGenerator(phantomK uint32, withChildren bool) func(parents blockSet) *blockNode {
// For the purposes of these tests, we'll create blockNodes whose hashes are a
// series of numbers from 1 to 255.
hashCounter := byte(1)
buildNode := func(parents blockSet) *blockNode {
block := newBlockNode(nil, parents, phantomK)
block.hash = &daghash.Hash{hashCounter}
hashCounter++
return block
}
if withChildren {
return func(parents blockSet) *blockNode {
node := buildNode(parents)
addNodeAsChildToParents(node)
return node
}
}
return buildNode
}
// checkRuleError ensures the type of the two passed errors are of the
// same type (either both nil or both of type RuleError) and their error codes
// match when not nil.
func checkRuleError(gotErr, wantErr error) error {
// Ensure the error code is of the expected type and the error
// code matches the value specified in the test instance.
if reflect.TypeOf(gotErr) != reflect.TypeOf(wantErr) {
return errors.Errorf("wrong error - got %T (%[1]v), want %T",
gotErr, wantErr)
}
if gotErr == nil {
return nil
}
// Ensure the want error type is a script error.
werr, ok := wantErr.(RuleError)
if !ok {
return errors.Errorf("unexpected test error type %T", wantErr)
}
// Ensure the error codes match. It's safe to use a raw type assert
// here since the code above already proved they are the same type and
// the want error is a script error.
gotErrorCode := gotErr.(RuleError).ErrorCode
if gotErrorCode != werr.ErrorCode {
return errors.Errorf("mismatched error code - got %v (%v), want %v",
gotErrorCode, gotErr, werr.ErrorCode)
}
return nil
}

586
blockdag/compress.go Normal file
View File

@@ -0,0 +1,586 @@
// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"github.com/kaspanet/kaspad/btcec"
"github.com/kaspanet/kaspad/txscript"
)
// -----------------------------------------------------------------------------
// A variable length quantity (VLQ) is an encoding that uses an arbitrary number
// of binary octets to represent an arbitrarily large integer. The scheme
// employs a most significant byte (MSB) base-128 encoding where the high bit in
// each byte indicates whether or not the byte is the final one. In addition,
// to ensure there are no redundant encodings, an offset is subtracted every
// time a group of 7 bits is shifted out. Therefore each integer can be
// represented in exactly one way, and each representation stands for exactly
// one integer.
//
// Another nice property of this encoding is that it provides a compact
// representation of values that are typically used to indicate sizes. For
// example, the values 0 - 127 are represented with a single byte, 128 - 16511
// with two bytes, and 16512 - 2113663 with three bytes.
//
// While the encoding allows arbitrarily large integers, it is artificially
// limited in this code to an unsigned 64-bit integer for efficiency purposes.
//
// Example encodings:
// 0 -> [0x00]
// 127 -> [0x7f] * Max 1-byte value
// 128 -> [0x80 0x00]
// 129 -> [0x80 0x01]
// 255 -> [0x80 0x7f]
// 256 -> [0x81 0x00]
// 16511 -> [0xff 0x7f] * Max 2-byte value
// 16512 -> [0x80 0x80 0x00]
// 32895 -> [0x80 0xff 0x7f]
// 2113663 -> [0xff 0xff 0x7f] * Max 3-byte value
// 270549119 -> [0xff 0xff 0xff 0x7f] * Max 4-byte value
// 2^64-1 -> [0x80 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0x7f]
//
// References:
// https://en.wikipedia.org/wiki/Variable-length_quantity
// http://www.codecodex.com/wiki/Variable-Length_Integers
// -----------------------------------------------------------------------------
// serializeSizeVLQ returns the number of bytes it would take to serialize the
// passed number as a variable-length quantity according to the format described
// above.
func serializeSizeVLQ(n uint64) int {
size := 1
for ; n > 0x7f; n = (n >> 7) - 1 {
size++
}
return size
}
// putVLQ serializes the provided number to a variable-length quantity according
// to the format described above and returns the number of bytes of the encoded
// value. The result is placed directly into the passed byte slice which must
// be at least large enough to handle the number of bytes returned by the
// serializeSizeVLQ function or it will panic.
func putVLQ(target []byte, n uint64) int {
offset := 0
for ; ; offset++ {
// The high bit is set when another byte follows.
highBitMask := byte(0x80)
if offset == 0 {
highBitMask = 0x00
}
target[offset] = byte(n&0x7f) | highBitMask
if n <= 0x7f {
break
}
n = (n >> 7) - 1
}
// Reverse the bytes so it is MSB-encoded.
for i, j := 0, offset; i < j; i, j = i+1, j-1 {
target[i], target[j] = target[j], target[i]
}
return offset + 1
}
// deserializeVLQ deserializes the provided variable-length quantity according
// to the format described above. It also returns the number of bytes
// deserialized.
func deserializeVLQ(serialized []byte) (uint64, int) {
var n uint64
var size int
for _, val := range serialized {
size++
n = (n << 7) | uint64(val&0x7f)
if val&0x80 != 0x80 {
break
}
n++
}
return n, size
}
// -----------------------------------------------------------------------------
// In order to reduce the size of stored scripts, a domain specific compression
// algorithm is used which recognizes standard scripts and stores them using
// less bytes than the original script. The compression algorithm used here was
// obtained from Bitcoin Core, so all credits for the algorithm go to it.
//
// The general serialized format is:
//
// <script size or type><script data>
//
// Field Type Size
// script size or type VLQ variable
// script data []byte variable
//
// The specific serialized format for each recognized standard script is:
//
// - Pay-to-pubkey-hash: (21 bytes) - <0><20-byte pubkey hash>
// - Pay-to-script-hash: (21 bytes) - <1><20-byte script hash>
// - Pay-to-pubkey**: (33 bytes) - <2, 3, 4, or 5><32-byte pubkey X value>
// 2, 3 = compressed pubkey with bit 0 specifying the y coordinate to use
// 4, 5 = uncompressed pubkey with bit 0 specifying the y coordinate to use
// ** Only valid public keys starting with 0x02, 0x03, and 0x04 are supported.
//
// Any scripts which are not recognized as one of the aforementioned standard
// scripts are encoded using the general serialized format and encode the script
// size as the sum of the actual size of the script and the number of special
// cases.
// -----------------------------------------------------------------------------
// The following constants specify the special constants used to identify a
// special script type in the domain-specific compressed script encoding.
//
// NOTE: This section specifically does not use iota since these values are
// serialized and must be stable for long-term storage.
const (
// cstPayToPubKeyHash identifies a compressed pay-to-pubkey-hash script.
cstPayToPubKeyHash = 0
// cstPayToScriptHash identifies a compressed pay-to-script-hash script.
cstPayToScriptHash = 1
// cstPayToPubKeyComp2 identifies a compressed pay-to-pubkey script to
// a compressed pubkey. Bit 0 specifies which y-coordinate to use
// to reconstruct the full uncompressed pubkey.
cstPayToPubKeyComp2 = 2
// cstPayToPubKeyComp3 identifies a compressed pay-to-pubkey script to
// a compressed pubkey. Bit 0 specifies which y-coordinate to use
// to reconstruct the full uncompressed pubkey.
cstPayToPubKeyComp3 = 3
// cstPayToPubKeyUncomp4 identifies a compressed pay-to-pubkey script to
// an uncompressed pubkey. Bit 0 specifies which y-coordinate to use
// to reconstruct the full uncompressed pubkey.
cstPayToPubKeyUncomp4 = 4
// cstPayToPubKeyUncomp5 identifies a compressed pay-to-pubkey script to
// an uncompressed pubkey. Bit 0 specifies which y-coordinate to use
// to reconstruct the full uncompressed pubkey.
cstPayToPubKeyUncomp5 = 5
// numSpecialScripts is the number of special scripts recognized by the
// domain-specific script compression algorithm.
numSpecialScripts = 6
)
// isPubKeyHash returns whether or not the passed public key script is a
// standard pay-to-pubkey-hash script along with the pubkey hash it is paying to
// if it is.
func isPubKeyHash(script []byte) (bool, []byte) {
if len(script) == 25 && script[0] == txscript.OpDup &&
script[1] == txscript.OpHash160 &&
script[2] == txscript.OpData20 &&
script[23] == txscript.OpEqualVerify &&
script[24] == txscript.OpCheckSig {
return true, script[3:23]
}
return false, nil
}
// isScriptHash returns whether or not the passed public key script is a
// standard pay-to-script-hash script along with the script hash it is paying to
// if it is.
func isScriptHash(script []byte) (bool, []byte) {
if len(script) == 23 && script[0] == txscript.OpHash160 &&
script[1] == txscript.OpData20 &&
script[22] == txscript.OpEqual {
return true, script[2:22]
}
return false, nil
}
// isPubKey returns whether or not the passed public key script is a standard
// pay-to-pubkey script that pays to a valid compressed or uncompressed public
// key along with the serialized pubkey it is paying to if it is.
//
// NOTE: This function ensures the public key is actually valid since the
// compression algorithm requires valid pubkeys. It does not support hybrid
// pubkeys. This means that even if the script has the correct form for a
// pay-to-pubkey script, this function will only return true when it is paying
// to a valid compressed or uncompressed pubkey.
func isPubKey(script []byte) (bool, []byte) {
// Pay-to-compressed-pubkey script.
if len(script) == 35 && script[0] == txscript.OpData33 &&
script[34] == txscript.OpCheckSig && (script[1] == 0x02 ||
script[1] == 0x03) {
// Ensure the public key is valid.
serializedPubKey := script[1:34]
_, err := btcec.ParsePubKey(serializedPubKey, btcec.S256())
if err == nil {
return true, serializedPubKey
}
}
// Pay-to-uncompressed-pubkey script.
if len(script) == 67 && script[0] == txscript.OpData65 &&
script[66] == txscript.OpCheckSig && script[1] == 0x04 {
// Ensure the public key is valid.
serializedPubKey := script[1:66]
_, err := btcec.ParsePubKey(serializedPubKey, btcec.S256())
if err == nil {
return true, serializedPubKey
}
}
return false, nil
}
// compressedScriptSize returns the number of bytes the passed script would take
// when encoded with the domain specific compression algorithm described above.
func compressedScriptSize(scriptPubKey []byte) int {
// Pay-to-pubkey-hash script.
if valid, _ := isPubKeyHash(scriptPubKey); valid {
return 21
}
// Pay-to-script-hash script.
if valid, _ := isScriptHash(scriptPubKey); valid {
return 21
}
// Pay-to-pubkey (compressed or uncompressed) script.
if valid, _ := isPubKey(scriptPubKey); valid {
return 33
}
// When none of the above special cases apply, encode the script as is
// preceded by the sum of its size and the number of special cases
// encoded as a variable length quantity.
return serializeSizeVLQ(uint64(len(scriptPubKey)+numSpecialScripts)) +
len(scriptPubKey)
}
// decodeCompressedScriptSize treats the passed serialized bytes as a compressed
// script, possibly followed by other data, and returns the number of bytes it
// occupies taking into account the special encoding of the script size by the
// domain specific compression algorithm described above.
func decodeCompressedScriptSize(serialized []byte) int {
scriptSize, bytesRead := deserializeVLQ(serialized)
if bytesRead == 0 {
return 0
}
switch scriptSize {
case cstPayToPubKeyHash:
return 21
case cstPayToScriptHash:
return 21
case cstPayToPubKeyComp2, cstPayToPubKeyComp3, cstPayToPubKeyUncomp4,
cstPayToPubKeyUncomp5:
return 33
}
scriptSize -= numSpecialScripts
scriptSize += uint64(bytesRead)
return int(scriptSize)
}
// putCompressedScript compresses the passed script according to the domain
// specific compression algorithm described above directly into the passed
// target byte slice. The target byte slice must be at least large enough to
// handle the number of bytes returned by the compressedScriptSize function or
// it will panic.
func putCompressedScript(target, scriptPubKey []byte) int {
// Pay-to-pubkey-hash script.
if valid, hash := isPubKeyHash(scriptPubKey); valid {
target[0] = cstPayToPubKeyHash
copy(target[1:21], hash)
return 21
}
// Pay-to-script-hash script.
if valid, hash := isScriptHash(scriptPubKey); valid {
target[0] = cstPayToScriptHash
copy(target[1:21], hash)
return 21
}
// Pay-to-pubkey (compressed or uncompressed) script.
if valid, serializedPubKey := isPubKey(scriptPubKey); valid {
pubKeyFormat := serializedPubKey[0]
switch pubKeyFormat {
case 0x02, 0x03:
target[0] = pubKeyFormat
copy(target[1:33], serializedPubKey[1:33])
return 33
case 0x04:
// Encode the oddness of the serialized pubkey into the
// compressed script type.
target[0] = pubKeyFormat | (serializedPubKey[64] & 0x01)
copy(target[1:33], serializedPubKey[1:33])
return 33
}
}
// When none of the above special cases apply, encode the unmodified
// script preceded by the sum of its size and the number of special
// cases encoded as a variable length quantity.
encodedSize := uint64(len(scriptPubKey) + numSpecialScripts)
vlqSizeLen := putVLQ(target, encodedSize)
copy(target[vlqSizeLen:], scriptPubKey)
return vlqSizeLen + len(scriptPubKey)
}
// decompressScript returns the original script obtained by decompressing the
// passed compressed script according to the domain specific compression
// algorithm described above.
//
// NOTE: The script parameter must already have been proven to be long enough
// to contain the number of bytes returned by decodeCompressedScriptSize or it
// will panic. This is acceptable since it is only an internal function.
func decompressScript(compressedScriptPubKey []byte) []byte {
// In practice this function will not be called with a zero-length or
// nil script since the nil script encoding includes the length, however
// the code below assumes the length exists, so just return nil now if
// the function ever ends up being called with a nil script in the
// future.
if len(compressedScriptPubKey) == 0 {
return nil
}
// Decode the script size and examine it for the special cases.
encodedScriptSize, bytesRead := deserializeVLQ(compressedScriptPubKey)
switch encodedScriptSize {
// Pay-to-pubkey-hash script. The resulting script is:
// <OP_DUP><OP_HASH160><20 byte hash><OP_EQUALVERIFY><OP_CHECKSIG>
case cstPayToPubKeyHash:
scriptPubKey := make([]byte, 25)
scriptPubKey[0] = txscript.OpDup
scriptPubKey[1] = txscript.OpHash160
scriptPubKey[2] = txscript.OpData20
copy(scriptPubKey[3:], compressedScriptPubKey[bytesRead:bytesRead+20])
scriptPubKey[23] = txscript.OpEqualVerify
scriptPubKey[24] = txscript.OpCheckSig
return scriptPubKey
// Pay-to-script-hash script. The resulting script is:
// <OP_HASH160><20 byte script hash><OP_EQUAL>
case cstPayToScriptHash:
scriptPubKey := make([]byte, 23)
scriptPubKey[0] = txscript.OpHash160
scriptPubKey[1] = txscript.OpData20
copy(scriptPubKey[2:], compressedScriptPubKey[bytesRead:bytesRead+20])
scriptPubKey[22] = txscript.OpEqual
return scriptPubKey
// Pay-to-compressed-pubkey script. The resulting script is:
// <OP_DATA_33><33 byte compressed pubkey><OP_CHECKSIG>
case cstPayToPubKeyComp2, cstPayToPubKeyComp3:
scriptPubKey := make([]byte, 35)
scriptPubKey[0] = txscript.OpData33
scriptPubKey[1] = byte(encodedScriptSize)
copy(scriptPubKey[2:], compressedScriptPubKey[bytesRead:bytesRead+32])
scriptPubKey[34] = txscript.OpCheckSig
return scriptPubKey
// Pay-to-uncompressed-pubkey script. The resulting script is:
// <OP_DATA_65><65 byte uncompressed pubkey><OP_CHECKSIG>
case cstPayToPubKeyUncomp4, cstPayToPubKeyUncomp5:
// Change the leading byte to the appropriate compressed pubkey
// identifier (0x02 or 0x03) so it can be decoded as a
// compressed pubkey. This really should never fail since the
// encoding ensures it is valid before compressing to this type.
compressedKey := make([]byte, 33)
compressedKey[0] = byte(encodedScriptSize - 2)
copy(compressedKey[1:], compressedScriptPubKey[1:])
key, err := btcec.ParsePubKey(compressedKey, btcec.S256())
if err != nil {
return nil
}
scriptPubKey := make([]byte, 67)
scriptPubKey[0] = txscript.OpData65
copy(scriptPubKey[1:], key.SerializeUncompressed())
scriptPubKey[66] = txscript.OpCheckSig
return scriptPubKey
}
// When none of the special cases apply, the script was encoded using
// the general format, so reduce the script size by the number of
// special cases and return the unmodified script.
scriptSize := int(encodedScriptSize - numSpecialScripts)
scriptPubKey := make([]byte, scriptSize)
copy(scriptPubKey, compressedScriptPubKey[bytesRead:bytesRead+scriptSize])
return scriptPubKey
}
// -----------------------------------------------------------------------------
// In order to reduce the size of stored amounts, a domain specific compression
// algorithm is used which relies on there typically being a lot of zeroes at
// end of the amounts. The compression algorithm used here was obtained from
// Bitcoin Core, so all credits for the algorithm go to it.
//
// While this is simply exchanging one uint64 for another, the resulting value
// for typical amounts has a much smaller magnitude which results in fewer bytes
// when encoded as variable length quantity. For example, consider the amount
// of 0.1 BTC which is 10000000 satoshi. Encoding 10000000 as a VLQ would take
// 4 bytes while encoding the compressed value of 8 as a VLQ only takes 1 byte.
//
// Essentially the compression is achieved by splitting the value into an
// exponent in the range [0-9] and a digit in the range [1-9], when possible,
// and encoding them in a way that can be decoded. More specifically, the
// encoding is as follows:
// - 0 is 0
// - Find the exponent, e, as the largest power of 10 that evenly divides the
// value up to a maximum of 9
// - When e < 9, the final digit can't be 0 so store it as d and remove it by
// dividing the value by 10 (call the result n). The encoded value is thus:
// 1 + 10*(9*n + d-1) + e
// - When e==9, the only thing known is the amount is not 0. The encoded value
// is thus:
// 1 + 10*(n-1) + e == 10 + 10*(n-1)
//
// Example encodings:
// (The numbers in parenthesis are the number of bytes when serialized as a VLQ)
// 0 (1) -> 0 (1) * 0.00000000 BTC
// 1000 (2) -> 4 (1) * 0.00001000 BTC
// 10000 (2) -> 5 (1) * 0.00010000 BTC
// 12345678 (4) -> 111111101(4) * 0.12345678 BTC
// 50000000 (4) -> 47 (1) * 0.50000000 BTC
// 100000000 (4) -> 9 (1) * 1.00000000 BTC
// 500000000 (5) -> 49 (1) * 5.00000000 BTC
// 1000000000 (5) -> 10 (1) * 10.00000000 BTC
// -----------------------------------------------------------------------------
// compressTxOutAmount compresses the passed amount according to the domain
// specific compression algorithm described above.
func compressTxOutAmount(amount uint64) uint64 {
// No need to do any work if it's zero.
if amount == 0 {
return 0
}
// Find the largest power of 10 (max of 9) that evenly divides the
// value.
exponent := uint64(0)
for amount%10 == 0 && exponent < 9 {
amount /= 10
exponent++
}
// The compressed result for exponents less than 9 is:
// 1 + 10*(9*n + d-1) + e
if exponent < 9 {
lastDigit := amount % 10
amount /= 10
return 1 + 10*(9*amount+lastDigit-1) + exponent
}
// The compressed result for an exponent of 9 is:
// 1 + 10*(n-1) + e == 10 + 10*(n-1)
return 10 + 10*(amount-1)
}
// decompressTxOutAmount returns the original amount the passed compressed
// amount represents according to the domain specific compression algorithm
// described above.
func decompressTxOutAmount(amount uint64) uint64 {
// No need to do any work if it's zero.
if amount == 0 {
return 0
}
// The decompressed amount is either of the following two equations:
// x = 1 + 10*(9*n + d - 1) + e
// x = 1 + 10*(n - 1) + 9
amount--
// The decompressed amount is now one of the following two equations:
// x = 10*(9*n + d - 1) + e
// x = 10*(n - 1) + 9
exponent := amount % 10
amount /= 10
// The decompressed amount is now one of the following two equations:
// x = 9*n + d - 1 | where e < 9
// x = n - 1 | where e = 9
n := uint64(0)
if exponent < 9 {
lastDigit := amount%9 + 1
amount /= 9
n = amount*10 + lastDigit
} else {
n = amount + 1
}
// Apply the exponent.
for ; exponent > 0; exponent-- {
n *= 10
}
return n
}
// -----------------------------------------------------------------------------
// Compressed transaction outputs consist of an amount and a public key script
// both compressed using the domain specific compression algorithms previously
// described.
//
// The serialized format is:
//
// <compressed amount><compressed script>
//
// Field Type Size
// compressed amount VLQ variable
// compressed script []byte variable
// -----------------------------------------------------------------------------
// compressedTxOutSize returns the number of bytes the passed transaction output
// fields would take when encoded with the format described above.
func compressedTxOutSize(amount uint64, scriptPubKey []byte) int {
return serializeSizeVLQ(compressTxOutAmount(amount)) +
compressedScriptSize(scriptPubKey)
}
// putCompressedTxOut compresses the passed amount and script according to their
// domain specific compression algorithms and encodes them directly into the
// passed target byte slice with the format described above. The target byte
// slice must be at least large enough to handle the number of bytes returned by
// the compressedTxOutSize function or it will panic.
func putCompressedTxOut(target []byte, amount uint64, scriptPubKey []byte) int {
offset := putVLQ(target, compressTxOutAmount(amount))
offset += putCompressedScript(target[offset:], scriptPubKey)
return offset
}
// decodeCompressedTxOut decodes the passed compressed txout, possibly followed
// by other data, into its uncompressed amount and script and returns them along
// with the number of bytes they occupied prior to decompression.
func decodeCompressedTxOut(serialized []byte) (uint64, []byte, int, error) {
// Deserialize the compressed amount and ensure there are bytes
// remaining for the compressed script.
compressedAmount, bytesRead := deserializeVLQ(serialized)
if bytesRead >= len(serialized) {
return 0, nil, bytesRead, errDeserialize("unexpected end of " +
"data after compressed amount")
}
// Decode the compressed script size and ensure there are enough bytes
// left in the slice for it.
scriptSize := decodeCompressedScriptSize(serialized[bytesRead:])
if len(serialized[bytesRead:]) < scriptSize {
return 0, nil, bytesRead, errDeserialize("unexpected end of " +
"data after script size")
}
// Decompress and return the amount and script.
amount := decompressTxOutAmount(compressedAmount)
script := decompressScript(serialized[bytesRead : bytesRead+scriptSize])
return amount, script, bytesRead + scriptSize, nil
}

436
blockdag/compress_test.go Normal file
View File

@@ -0,0 +1,436 @@
// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"bytes"
"encoding/hex"
"testing"
)
// hexToBytes converts the passed hex string into bytes and will panic if there
// is an error. This is only provided for the hard-coded constants so errors in
// the source code can be detected. It will only (and must only) be called with
// hard-coded values.
func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}
// TestVLQ ensures the variable length quantity serialization, deserialization,
// and size calculation works as expected.
func TestVLQ(t *testing.T) {
t.Parallel()
tests := []struct {
val uint64
serialized []byte
}{
{0, hexToBytes("00")},
{1, hexToBytes("01")},
{127, hexToBytes("7f")},
{128, hexToBytes("8000")},
{129, hexToBytes("8001")},
{255, hexToBytes("807f")},
{256, hexToBytes("8100")},
{16383, hexToBytes("fe7f")},
{16384, hexToBytes("ff00")},
{16511, hexToBytes("ff7f")}, // Max 2-byte value
{16512, hexToBytes("808000")},
{16513, hexToBytes("808001")},
{16639, hexToBytes("80807f")},
{32895, hexToBytes("80ff7f")},
{2113663, hexToBytes("ffff7f")}, // Max 3-byte value
{2113664, hexToBytes("80808000")},
{270549119, hexToBytes("ffffff7f")}, // Max 4-byte value
{270549120, hexToBytes("8080808000")},
{2147483647, hexToBytes("86fefefe7f")},
{2147483648, hexToBytes("86fefeff00")},
{4294967295, hexToBytes("8efefefe7f")}, // Max uint32, 5 bytes
// Max uint64, 10 bytes
{18446744073709551615, hexToBytes("80fefefefefefefefe7f")},
}
for _, test := range tests {
// Ensure the function to calculate the serialized size without
// actually serializing the value is calculated properly.
gotSize := serializeSizeVLQ(test.val)
if gotSize != len(test.serialized) {
t.Errorf("serializeSizeVLQ: did not get expected size "+
"for %d - got %d, want %d", test.val, gotSize,
len(test.serialized))
continue
}
// Ensure the value serializes to the expected bytes.
gotBytes := make([]byte, gotSize)
gotBytesWritten := putVLQ(gotBytes, test.val)
if !bytes.Equal(gotBytes, test.serialized) {
t.Errorf("putVLQUnchecked: did not get expected bytes "+
"for %d - got %x, want %x", test.val, gotBytes,
test.serialized)
continue
}
if gotBytesWritten != len(test.serialized) {
t.Errorf("putVLQUnchecked: did not get expected number "+
"of bytes written for %d - got %d, want %d",
test.val, gotBytesWritten, len(test.serialized))
continue
}
// Ensure the serialized bytes deserialize to the expected
// value.
gotVal, gotBytesRead := deserializeVLQ(test.serialized)
if gotVal != test.val {
t.Errorf("deserializeVLQ: did not get expected value "+
"for %x - got %d, want %d", test.serialized,
gotVal, test.val)
continue
}
if gotBytesRead != len(test.serialized) {
t.Errorf("deserializeVLQ: did not get expected number "+
"of bytes read for %d - got %d, want %d",
test.serialized, gotBytesRead,
len(test.serialized))
continue
}
}
}
// TestScriptCompression ensures the domain-specific script compression and
// decompression works as expected.
func TestScriptCompression(t *testing.T) {
t.Parallel()
tests := []struct {
name string
uncompressed []byte
compressed []byte
}{
{
name: "nil",
uncompressed: nil,
compressed: hexToBytes("06"),
},
{
name: "pay-to-pubkey-hash 1",
uncompressed: hexToBytes("76a9141018853670f9f3b0582c5b9ee8ce93764ac32b9388ac"),
compressed: hexToBytes("001018853670f9f3b0582c5b9ee8ce93764ac32b93"),
},
{
name: "pay-to-pubkey-hash 2",
uncompressed: hexToBytes("76a914e34cce70c86373273efcc54ce7d2a491bb4a0e8488ac"),
compressed: hexToBytes("00e34cce70c86373273efcc54ce7d2a491bb4a0e84"),
},
{
name: "pay-to-script-hash 1",
uncompressed: hexToBytes("a914da1745e9b549bd0bfa1a569971c77eba30cd5a4b87"),
compressed: hexToBytes("01da1745e9b549bd0bfa1a569971c77eba30cd5a4b"),
},
{
name: "pay-to-script-hash 2",
uncompressed: hexToBytes("a914f815b036d9bbbce5e9f2a00abd1bf3dc91e9551087"),
compressed: hexToBytes("01f815b036d9bbbce5e9f2a00abd1bf3dc91e95510"),
},
{
name: "pay-to-pubkey compressed 0x02",
uncompressed: hexToBytes("2102192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4ac"),
compressed: hexToBytes("02192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"),
},
{
name: "pay-to-pubkey compressed 0x03",
uncompressed: hexToBytes("2103b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65ac"),
compressed: hexToBytes("03b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65"),
},
{
name: "pay-to-pubkey uncompressed 0x04 even",
uncompressed: hexToBytes("4104192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b40d45264838c0bd96852662ce6a847b197376830160c6d2eb5e6a4c44d33f453eac"),
compressed: hexToBytes("04192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"),
},
{
name: "pay-to-pubkey uncompressed 0x04 odd",
uncompressed: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
compressed: hexToBytes("0511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
},
{
name: "pay-to-pubkey invalid pubkey",
uncompressed: hexToBytes("3302aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"),
compressed: hexToBytes("293302aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"),
},
{
name: "requires 2 size bytes - data push 200 bytes",
uncompressed: append(hexToBytes("4cc8"), bytes.Repeat([]byte{0x00}, 200)...),
// [0x80, 0x50] = 208 as a variable length quantity
// [0x4c, 0xc8] = OP_PUSHDATA1 200
compressed: append(hexToBytes("80504cc8"), bytes.Repeat([]byte{0x00}, 200)...),
},
}
for _, test := range tests {
// Ensure the function to calculate the serialized size without
// actually serializing the value is calculated properly.
gotSize := compressedScriptSize(test.uncompressed)
if gotSize != len(test.compressed) {
t.Errorf("compressedScriptSize (%s): did not get "+
"expected size - got %d, want %d", test.name,
gotSize, len(test.compressed))
continue
}
// Ensure the script compresses to the expected bytes.
gotCompressed := make([]byte, gotSize)
gotBytesWritten := putCompressedScript(gotCompressed,
test.uncompressed)
if !bytes.Equal(gotCompressed, test.compressed) {
t.Errorf("putCompressedScript (%s): did not get "+
"expected bytes - got %x, want %x", test.name,
gotCompressed, test.compressed)
continue
}
if gotBytesWritten != len(test.compressed) {
t.Errorf("putCompressedScript (%s): did not get "+
"expected number of bytes written - got %d, "+
"want %d", test.name, gotBytesWritten,
len(test.compressed))
continue
}
// Ensure the compressed script size is properly decoded from
// the compressed script.
gotDecodedSize := decodeCompressedScriptSize(test.compressed)
if gotDecodedSize != len(test.compressed) {
t.Errorf("decodeCompressedScriptSize (%s): did not get "+
"expected size - got %d, want %d", test.name,
gotDecodedSize, len(test.compressed))
continue
}
// Ensure the script decompresses to the expected bytes.
gotDecompressed := decompressScript(test.compressed)
if !bytes.Equal(gotDecompressed, test.uncompressed) {
t.Errorf("decompressScript (%s): did not get expected "+
"bytes - got %x, want %x", test.name,
gotDecompressed, test.uncompressed)
continue
}
}
}
// TestScriptCompressionErrors ensures calling various functions related to
// script compression with incorrect data returns the expected results.
func TestScriptCompressionErrors(t *testing.T) {
t.Parallel()
// A nil script must result in a decoded size of 0.
if gotSize := decodeCompressedScriptSize(nil); gotSize != 0 {
t.Fatalf("decodeCompressedScriptSize with nil script did not "+
"return 0 - got %d", gotSize)
}
// A nil script must result in a nil decompressed script.
if gotScript := decompressScript(nil); gotScript != nil {
t.Fatalf("decompressScript with nil script did not return nil "+
"decompressed script - got %x", gotScript)
}
// A compressed script for a pay-to-pubkey (uncompressed) that results
// in an invalid pubkey must result in a nil decompressed script.
compressedScript := hexToBytes("04012d74d0cb94344c9569c2e77901573d8d" +
"7903c3ebec3a957724895dca52c6b4")
if gotScript := decompressScript(compressedScript); gotScript != nil {
t.Fatalf("decompressScript with compressed pay-to-"+
"uncompressed-pubkey that is invalid did not return "+
"nil decompressed script - got %x", gotScript)
}
}
// TestAmountCompression ensures the domain-specific transaction output amount
// compression and decompression works as expected.
func TestAmountCompression(t *testing.T) {
t.Parallel()
tests := []struct {
name string
uncompressed uint64
compressed uint64
}{
{
name: "0 BTC",
uncompressed: 0,
compressed: 0,
},
{
name: "546 Satoshi (current network dust value)",
uncompressed: 546,
compressed: 4911,
},
{
name: "0.00001 BTC (typical transaction fee)",
uncompressed: 1000,
compressed: 4,
},
{
name: "0.0001 BTC (typical transaction fee)",
uncompressed: 10000,
compressed: 5,
},
{
name: "0.12345678 BTC",
uncompressed: 12345678,
compressed: 111111101,
},
{
name: "0.5 BTC",
uncompressed: 50000000,
compressed: 48,
},
{
name: "1 BTC",
uncompressed: 100000000,
compressed: 9,
},
{
name: "5 BTC",
uncompressed: 500000000,
compressed: 49,
},
{
name: "21000000 BTC (max minted coins)",
uncompressed: 2100000000000000,
compressed: 21000000,
},
}
for _, test := range tests {
// Ensure the amount compresses to the expected value.
gotCompressed := compressTxOutAmount(test.uncompressed)
if gotCompressed != test.compressed {
t.Errorf("compressTxOutAmount (%s): did not get "+
"expected value - got %d, want %d", test.name,
gotCompressed, test.compressed)
continue
}
// Ensure the value decompresses to the expected value.
gotDecompressed := decompressTxOutAmount(test.compressed)
if gotDecompressed != test.uncompressed {
t.Errorf("decompressTxOutAmount (%s): did not get "+
"expected value - got %d, want %d", test.name,
gotDecompressed, test.uncompressed)
continue
}
}
}
// TestCompressedTxOut ensures the transaction output serialization and
// deserialization works as expected.
func TestCompressedTxOut(t *testing.T) {
t.Parallel()
tests := []struct {
name string
amount uint64
scriptPubKey []byte
compressed []byte
}{
{
name: "pay-to-pubkey-hash dust",
amount: 546,
scriptPubKey: hexToBytes("76a9141018853670f9f3b0582c5b9ee8ce93764ac32b9388ac"),
compressed: hexToBytes("a52f001018853670f9f3b0582c5b9ee8ce93764ac32b93"),
},
{
name: "pay-to-pubkey uncompressed 1 BTC",
amount: 100000000,
scriptPubKey: hexToBytes("4104192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b40d45264838c0bd96852662ce6a847b197376830160c6d2eb5e6a4c44d33f453eac"),
compressed: hexToBytes("0904192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"),
},
}
for _, test := range tests {
// Ensure the function to calculate the serialized size without
// actually serializing the txout is calculated properly.
gotSize := compressedTxOutSize(test.amount, test.scriptPubKey)
if gotSize != len(test.compressed) {
t.Errorf("compressedTxOutSize (%s): did not get "+
"expected size - got %d, want %d", test.name,
gotSize, len(test.compressed))
continue
}
// Ensure the txout compresses to the expected value.
gotCompressed := make([]byte, gotSize)
gotBytesWritten := putCompressedTxOut(gotCompressed,
test.amount, test.scriptPubKey)
if !bytes.Equal(gotCompressed, test.compressed) {
t.Errorf("compressTxOut (%s): did not get expected "+
"bytes - got %x, want %x", test.name,
gotCompressed, test.compressed)
continue
}
if gotBytesWritten != len(test.compressed) {
t.Errorf("compressTxOut (%s): did not get expected "+
"number of bytes written - got %d, want %d",
test.name, gotBytesWritten,
len(test.compressed))
continue
}
// Ensure the serialized bytes are decoded back to the expected
// uncompressed values.
gotAmount, gotScript, gotBytesRead, err := decodeCompressedTxOut(
test.compressed)
if err != nil {
t.Errorf("decodeCompressedTxOut (%s): unexpected "+
"error: %v", test.name, err)
continue
}
if gotAmount != test.amount {
t.Errorf("decodeCompressedTxOut (%s): did not get "+
"expected amount - got %d, want %d",
test.name, gotAmount, test.amount)
continue
}
if !bytes.Equal(gotScript, test.scriptPubKey) {
t.Errorf("decodeCompressedTxOut (%s): did not get "+
"expected script - got %x, want %x",
test.name, gotScript, test.scriptPubKey)
continue
}
if gotBytesRead != len(test.compressed) {
t.Errorf("decodeCompressedTxOut (%s): did not get "+
"expected number of bytes read - got %d, want %d",
test.name, gotBytesRead, len(test.compressed))
continue
}
}
}
// TestTxOutCompressionErrors ensures calling various functions related to
// txout compression with incorrect data returns the expected results.
func TestTxOutCompressionErrors(t *testing.T) {
t.Parallel()
// A compressed txout with missing compressed script must error.
compressedTxOut := hexToBytes("00")
_, _, _, err := decodeCompressedTxOut(compressedTxOut)
if !isDeserializeErr(err) {
t.Fatalf("decodeCompressedTxOut with missing compressed script "+
"did not return expected error type - got %T, want "+
"errDeserialize", err)
}
// A compressed txout with short compressed script must error.
compressedTxOut = hexToBytes("0010")
_, _, _, err = decodeCompressedTxOut(compressedTxOut)
if !isDeserializeErr(err) {
t.Fatalf("decodeCompressedTxOut with short compressed script "+
"did not return expected error type - got %T, want "+
"errDeserialize", err)
}
}

2031
blockdag/dag.go Normal file

File diff suppressed because it is too large Load Diff

1406
blockdag/dag_test.go Normal file

File diff suppressed because it is too large Load Diff

854
blockdag/dagio.go Normal file
View File

@@ -0,0 +1,854 @@
// Copyright (c) 2015-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"io"
"sync"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/binaryserializer"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/kaspanet/kaspad/wire"
)
const (
// blockHdrSize is the size of a block header. This is simply the
// constant from wire and is only provided here for convenience since
// wire.MaxBlockHeaderPayload is quite long.
blockHdrSize = wire.MaxBlockHeaderPayload
// latestUTXOSetBucketVersion is the current version of the UTXO set
// bucket that is used to track all unspent outputs.
latestUTXOSetBucketVersion = 1
)
var (
// blockIndexBucketName is the name of the db bucket used to house to the
// block headers and contextual information.
blockIndexBucketName = []byte("blockheaderidx")
// dagStateKeyName is the name of the db key used to store the DAG
// tip hashes.
dagStateKeyName = []byte("dagstate")
// utxoSetVersionKeyName is the name of the db key used to store the
// version of the utxo set currently in the database.
utxoSetVersionKeyName = []byte("utxosetversion")
// utxoSetBucketName is the name of the db bucket used to house the
// unspent transaction output set.
utxoSetBucketName = []byte("utxoset")
// utxoDiffsBucketName is the name of the db bucket used to house the
// diffs and diff children of blocks.
utxoDiffsBucketName = []byte("utxodiffs")
// subnetworksBucketName is the name of the db bucket used to store the
// subnetwork registry.
subnetworksBucketName = []byte("subnetworks")
// localSubnetworkKeyName is the name of the db key used to store the
// node's local subnetwork ID.
localSubnetworkKeyName = []byte("localsubnetworkidkey")
// byteOrder is the preferred byte order used for serializing numeric
// fields for storage in the database.
byteOrder = binary.LittleEndian
)
// errNotInDAG signifies that a block hash or height that is not in the
// DAG was requested.
type errNotInDAG string
// Error implements the error interface.
func (e errNotInDAG) Error() string {
return string(e)
}
// isNotInDAGErr returns whether or not the passed error is an
// errNotInDAG error.
func isNotInDAGErr(err error) bool {
_, ok := err.(errNotInDAG)
return ok
}
// errDeserialize signifies that a problem was encountered when deserializing
// data.
type errDeserialize string
// Error implements the error interface.
func (e errDeserialize) Error() string {
return string(e)
}
// isDeserializeErr returns whether or not the passed error is an errDeserialize
// error.
func isDeserializeErr(err error) bool {
_, ok := err.(errDeserialize)
return ok
}
// dbPutVersion uses an existing database transaction to update the provided
// key in the metadata bucket to the given version. It is primarily used to
// track versions on entities such as buckets.
func dbPutVersion(dbTx database.Tx, key []byte, version uint32) error {
var serialized [4]byte
byteOrder.PutUint32(serialized[:], version)
return dbTx.Metadata().Put(key, serialized[:])
}
// -----------------------------------------------------------------------------
// The unspent transaction output (UTXO) set consists of an entry for each
// unspent output using a format that is optimized to reduce space using domain
// specific compression algorithms. This format is a slightly modified version
// of the format used in Bitcoin Core.
//
// Each entry is keyed by an outpoint as specified below. It is important to
// note that the key encoding uses a VLQ, which employs an MSB encoding so
// iteration of UTXOs when doing byte-wise comparisons will produce them in
// order.
//
// The serialized key format is:
// <hash><output index>
//
// Field Type Size
// hash daghash.Hash daghash.HashSize
// output index VLQ variable
//
// The serialized value format is:
//
// <header code><compressed txout>
//
// Field Type Size
// header code VLQ variable
// compressed txout
// compressed amount VLQ variable
// compressed script []byte variable
//
// The serialized header code format is:
// bit 0 - containing transaction is a coinbase
// bits 1-x - height of the block that contains the unspent txout
//
// Example 1:
// From tx in main blockchain:
// Blk 1, b7c3332bc138e2c9429818f5fed500bcc1746544218772389054dc8047d7cd3f:0
//
// 03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52
// <><------------------------------------------------------------------>
// | |
// header code compressed txout
//
// - header code: 0x03 (coinbase, height 1)
// - compressed txout:
// - 0x32: VLQ-encoded compressed amount for 5000000000 (50 BTC)
// - 0x04: special script type pay-to-pubkey
// - 0x96...52: x-coordinate of the pubkey
//
// Example 2:
// From tx in main blockchain:
// Blk 113931, 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f:2
//
// 8cf316800900b8025be1b3efc63b0ad48e7f9f10e87544528d58
// <----><------------------------------------------>
// | |
// header code compressed txout
//
// - header code: 0x8cf316 (not coinbase, height 113931)
// - compressed txout:
// - 0x8009: VLQ-encoded compressed amount for 15000000 (0.15 BTC)
// - 0x00: special script type pay-to-pubkey-hash
// - 0xb8...58: pubkey hash
//
// Example 3:
// From tx in main blockchain:
// Blk 338156, 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620:22
//
// a8a2588ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6
// <----><-------------------------------------------------->
// | |
// header code compressed txout
//
// - header code: 0xa8a258 (not coinbase, height 338156)
// - compressed txout:
// - 0x8ba5b9e763: VLQ-encoded compressed amount for 366875659 (3.66875659 BTC)
// - 0x01: special script type pay-to-script-hash
// - 0x1d...e6: script hash
// -----------------------------------------------------------------------------
// maxUint32VLQSerializeSize is the maximum number of bytes a max uint32 takes
// to serialize as a VLQ.
var maxUint32VLQSerializeSize = serializeSizeVLQ(1<<32 - 1)
// outpointKeyPool defines a concurrent safe free list of byte slices used to
// provide temporary buffers for outpoint database keys.
var outpointKeyPool = sync.Pool{
New: func() interface{} {
b := make([]byte, daghash.HashSize+maxUint32VLQSerializeSize)
return &b // Pointer to slice to avoid boxing alloc.
},
}
// outpointKey returns a key suitable for use as a database key in the UTXO set
// while making use of a free list. A new buffer is allocated if there are not
// already any available on the free list. The returned byte slice should be
// returned to the free list by using the recycleOutpointKey function when the
// caller is done with it _unless_ the slice will need to live for longer than
// the caller can calculate such as when used to write to the database.
func outpointKey(outpoint wire.Outpoint) *[]byte {
// A VLQ employs an MSB encoding, so they are useful not only to reduce
// the amount of storage space, but also so iteration of UTXOs when
// doing byte-wise comparisons will produce them in order.
key := outpointKeyPool.Get().(*[]byte)
idx := uint64(outpoint.Index)
*key = (*key)[:daghash.HashSize+serializeSizeVLQ(idx)]
copy(*key, outpoint.TxID[:])
putVLQ((*key)[daghash.HashSize:], idx)
return key
}
// recycleOutpointKey puts the provided byte slice, which should have been
// obtained via the outpointKey function, back on the free list.
func recycleOutpointKey(key *[]byte) {
outpointKeyPool.Put(key)
}
// dbPutUTXODiff uses an existing database transaction to update the UTXO set
// in the database based on the provided UTXO view contents and state. In
// particular, only the entries that have been marked as modified are written
// to the database.
func dbPutUTXODiff(dbTx database.Tx, diff *UTXODiff) error {
utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName)
for outpoint := range diff.toRemove {
key := outpointKey(outpoint)
err := utxoBucket.Delete(*key)
recycleOutpointKey(key)
if err != nil {
return err
}
}
for outpoint, entry := range diff.toAdd {
// Serialize and store the UTXO entry.
serialized := serializeUTXOEntry(entry)
key := outpointKey(outpoint)
err := utxoBucket.Put(*key, serialized)
// NOTE: The key is intentionally not recycled here since the
// database interface contract prohibits modifications. It will
// be garbage collected normally when the database is done with
// it.
if err != nil {
return err
}
}
return nil
}
type dagState struct {
TipHashes []*daghash.Hash
LastFinalityPoint *daghash.Hash
}
// serializeDAGState returns the serialization of the DAG state.
// This is data to be stored in the DAG state bucket.
func serializeDAGState(state *dagState) ([]byte, error) {
return json.Marshal(state)
}
// deserializeDAGState deserializes the passed serialized DAG state.
// This is data stored in the DAG state bucket and is updated after
// every block is connected to the DAG.
func deserializeDAGState(serializedData []byte) (*dagState, error) {
var state *dagState
err := json.Unmarshal(serializedData, &state)
if err != nil {
return nil, database.Error{
ErrorCode: database.ErrCorruption,
Description: "corrupt DAG state",
}
}
return state, nil
}
// dbPutDAGState uses an existing database transaction to store the latest
// tip hashes of the DAG.
func dbPutDAGState(dbTx database.Tx, state *dagState) error {
serializedData, err := serializeDAGState(state)
if err != nil {
return err
}
return dbTx.Metadata().Put(dagStateKeyName, serializedData)
}
// createDAGState initializes both the database and the DAG state to the
// genesis block. This includes creating the necessary buckets, so it
// must only be called on an uninitialized database.
func (dag *BlockDAG) createDAGState() error {
// Create the initial the database DAG state including creating the
// necessary index buckets and inserting the genesis block.
err := dag.db.Update(func(dbTx database.Tx) error {
meta := dbTx.Metadata()
// Create the bucket that houses the block index data.
_, err := meta.CreateBucket(blockIndexBucketName)
if err != nil {
return err
}
// Create the buckets that house the utxo set, the utxo diffs, and their
// version.
_, err = meta.CreateBucket(utxoSetBucketName)
if err != nil {
return err
}
_, err = meta.CreateBucket(utxoDiffsBucketName)
if err != nil {
return err
}
err = dbPutVersion(dbTx, utxoSetVersionKeyName,
latestUTXOSetBucketVersion)
if err != nil {
return err
}
// Create the bucket that houses the registered subnetworks.
_, err = meta.CreateBucket(subnetworksBucketName)
if err != nil {
return err
}
if err := dbPutLocalSubnetworkID(dbTx, dag.subnetworkID); err != nil {
return err
}
if _, err := meta.CreateBucketIfNotExists(idByHashIndexBucketName); err != nil {
return err
}
if _, err := meta.CreateBucketIfNotExists(hashByIDIndexBucketName); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
}
func (dag *BlockDAG) removeDAGState() error {
err := dag.db.Update(func(dbTx database.Tx) error {
meta := dbTx.Metadata()
err := meta.DeleteBucket(blockIndexBucketName)
if err != nil {
return err
}
err = meta.DeleteBucket(utxoSetBucketName)
if err != nil {
return err
}
err = meta.DeleteBucket(utxoDiffsBucketName)
if err != nil {
return err
}
err = dbTx.Metadata().Delete(utxoSetVersionKeyName)
if err != nil {
return err
}
err = meta.DeleteBucket(subnetworksBucketName)
if err != nil {
return err
}
err = dbTx.Metadata().Delete(localSubnetworkKeyName)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
}
func dbPutLocalSubnetworkID(dbTx database.Tx, subnetworkID *subnetworkid.SubnetworkID) error {
if subnetworkID == nil {
return dbTx.Metadata().Put(localSubnetworkKeyName, []byte{})
}
return dbTx.Metadata().Put(localSubnetworkKeyName, subnetworkID[:])
}
// initDAGState attempts to load and initialize the DAG state from the
// database. When the db does not yet contain any DAG state, both it and the
// DAG state are initialized to the genesis block.
func (dag *BlockDAG) initDAGState() error {
// Determine the state of the DAG database. We may need to initialize
// everything from scratch or upgrade certain buckets.
var initialized bool
err := dag.db.View(func(dbTx database.Tx) error {
initialized = dbTx.Metadata().Get(dagStateKeyName) != nil
if initialized {
var localSubnetworkID *subnetworkid.SubnetworkID
localSubnetworkIDBytes := dbTx.Metadata().Get(localSubnetworkKeyName)
if len(localSubnetworkIDBytes) != 0 {
localSubnetworkID = &subnetworkid.SubnetworkID{}
localSubnetworkID.SetBytes(localSubnetworkIDBytes)
}
if !localSubnetworkID.IsEqual(dag.subnetworkID) {
return errors.Errorf("Cannot start btcd with subnetwork ID %s because"+
" its database is already built with subnetwork ID %s. If you"+
" want to switch to a new database, please reset the"+
" database by starting btcd with --reset-db flag", dag.subnetworkID, localSubnetworkID)
}
}
return nil
})
if err != nil {
return err
}
if !initialized {
// At this point the database has not already been initialized, so
// initialize both it and the chain state to the genesis block.
return dag.createDAGState()
}
// Attempt to load the DAG state from the database.
return dag.db.View(func(dbTx database.Tx) error {
// Fetch the stored DAG tipHashes from the database metadata.
// When it doesn't exist, it means the database hasn't been
// initialized for use with the DAG yet, so break out now to allow
// that to happen under a writable database transaction.
serializedData := dbTx.Metadata().Get(dagStateKeyName)
log.Tracef("Serialized DAG tip hashes: %x", serializedData)
state, err := deserializeDAGState(serializedData)
if err != nil {
return err
}
// Load all of the headers from the data for the known DAG
// and construct the block index accordingly. Since the
// number of nodes are already known, perform a single alloc
// for them versus a whole bunch of little ones to reduce
// pressure on the GC.
log.Infof("Loading block index...")
blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)
var i int32
var lastNode *blockNode
var unprocessedBlockNodes []*blockNode
cursor := blockIndexBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() {
node, err := dag.deserializeBlockNode(cursor.Value())
if err != nil {
return err
}
// Check to see if this node had been stored in the the block DB
// but not yet accepted. If so, add it to a slice to be processed later.
if node.status == statusDataStored {
unprocessedBlockNodes = append(unprocessedBlockNodes, node)
continue
}
if lastNode == nil {
if !node.hash.IsEqual(dag.dagParams.GenesisHash) {
return AssertError(fmt.Sprintf("initDAGState: Expected "+
"first entry in block index to be genesis block, "+
"found %s", node.hash))
}
} else {
if len(node.parents) == 0 {
return AssertError(fmt.Sprintf("initDAGState: Could "+
"not find any parent for block %s", node.hash))
}
}
// Add the node to its parents children, connect it,
// and add it to the block index.
node.updateParentsChildren()
dag.index.addNode(node)
if node.status.KnownValid() {
dag.blockCount++
}
lastNode = node
i++
}
// Load all of the known UTXO entries and construct the full
// UTXO set accordingly. Since the number of entries is already
// known, perform a single alloc for them versus a whole bunch
// of little ones to reduce pressure on the GC.
log.Infof("Loading UTXO set...")
utxoEntryBucket := dbTx.Metadata().Bucket(utxoSetBucketName)
// Determine how many UTXO entries will be loaded into the index so we can
// allocate the right amount.
var utxoEntryCount int32
cursor = utxoEntryBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() {
utxoEntryCount++
}
fullUTXOCollection := make(utxoCollection, utxoEntryCount)
for ok := cursor.First(); ok; ok = cursor.Next() {
// Deserialize the outpoint
outpoint, err := deserializeOutpoint(cursor.Key())
if err != nil {
// Ensure any deserialization errors are returned as database
// corruption errors.
if isDeserializeErr(err) {
return database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("corrupt outpoint: %s", err),
}
}
return err
}
// Deserialize the utxo entry
entry, err := deserializeUTXOEntry(cursor.Value())
if err != nil {
// Ensure any deserialization errors are returned as database
// corruption errors.
if isDeserializeErr(err) {
return database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("corrupt utxo entry: %s", err),
}
}
return err
}
fullUTXOCollection[*outpoint] = entry
}
// Apply the loaded utxoCollection to the virtual block.
dag.virtual.utxoSet, err = newFullUTXOSetFromUTXOCollection(fullUTXOCollection)
if err != nil {
return AssertError(fmt.Sprintf("Error loading UTXOSet: %s", err))
}
// Apply the stored tips to the virtual block.
tips := newSet()
for _, tipHash := range state.TipHashes {
tip := dag.index.LookupNode(tipHash)
if tip == nil {
return AssertError(fmt.Sprintf("initDAGState: cannot find "+
"DAG tip %s in block index", state.TipHashes))
}
tips.add(tip)
}
dag.virtual.SetTips(tips)
// Set the last finality point
dag.lastFinalityPoint = dag.index.LookupNode(state.LastFinalityPoint)
dag.finalizeNodesBelowFinalityPoint(false)
// Go over any unprocessed blockNodes and process them now.
for _, node := range unprocessedBlockNodes {
// Check to see if the block exists in the block DB. If it
// doesn't, the database has certainly been corrupted.
blockExists, err := dbTx.HasBlock(node.hash)
if err != nil {
return AssertError(fmt.Sprintf("initDAGState: HasBlock "+
"for block %s failed: %s", node.hash, err))
}
if !blockExists {
return AssertError(fmt.Sprintf("initDAGState: block %s "+
"exists in block index but not in block db", node.hash))
}
// Attempt to accept the block.
block, err := dbFetchBlockByNode(dbTx, node)
isOrphan, delay, err := dag.ProcessBlock(block, BFWasStored)
if err != nil {
log.Warnf("Block %s, which was not previously processed, "+
"failed to be accepted to the DAG: %s", node.hash, err)
continue
}
// If the block is an orphan or is delayed then it couldn't have
// possibly been written to the block index in the first place.
if isOrphan {
return AssertError(fmt.Sprintf("Block %s, which was not "+
"previously processed, turned out to be an orphan, which is "+
"impossible.", node.hash))
}
if delay != 0 {
return AssertError(fmt.Sprintf("Block %s, which was not "+
"previously processed, turned out to be delayed, which is "+
"impossible.", node.hash))
}
}
return nil
})
}
// deserializeBlockNode parses a value in the block index bucket and returns a block node.
func (dag *BlockDAG) deserializeBlockNode(blockRow []byte) (*blockNode, error) {
buffer := bytes.NewReader(blockRow)
var header wire.BlockHeader
err := header.Deserialize(buffer)
if err != nil {
return nil, err
}
node := &blockNode{
hash: header.BlockHash(),
version: header.Version,
bits: header.Bits,
nonce: header.Nonce,
timestamp: header.Timestamp.Unix(),
hashMerkleRoot: header.HashMerkleRoot,
acceptedIDMerkleRoot: header.AcceptedIDMerkleRoot,
utxoCommitment: header.UTXOCommitment,
}
node.children = newSet()
node.parents = newSet()
for _, hash := range header.ParentHashes {
parent := dag.index.LookupNode(hash)
if parent == nil {
return nil, AssertError(fmt.Sprintf("deserializeBlockNode: Could "+
"not find parent %s for block %s", hash, header.BlockHash()))
}
node.parents.add(parent)
}
statusByte, err := buffer.ReadByte()
if err != nil {
return nil, err
}
node.status = blockStatus(statusByte)
selectedParentHash := &daghash.Hash{}
if _, err := io.ReadFull(buffer, selectedParentHash[:]); err != nil {
return nil, err
}
// Because genesis doesn't have selected parent, it's serialized as zero hash
if !selectedParentHash.IsEqual(&daghash.ZeroHash) {
node.selectedParent = dag.index.LookupNode(selectedParentHash)
}
node.blueScore, err = binaryserializer.Uint64(buffer, byteOrder)
if err != nil {
return nil, err
}
bluesCount, err := wire.ReadVarInt(buffer)
if err != nil {
return nil, err
}
node.blues = make([]*blockNode, bluesCount)
for i := uint64(0); i < bluesCount; i++ {
hash := &daghash.Hash{}
if _, err := io.ReadFull(buffer, hash[:]); err != nil {
return nil, err
}
node.blues[i] = dag.index.LookupNode(hash)
}
node.chainHeight = calculateChainHeight(node)
return node, nil
}
// dbFetchBlockByNode uses an existing database transaction to retrieve the
// raw block for the provided node, deserialize it, and return a util.Block
// of it.
func dbFetchBlockByNode(dbTx database.Tx, node *blockNode) (*util.Block, error) {
// Load the raw block bytes from the database.
blockBytes, err := dbTx.FetchBlock(node.hash)
if err != nil {
return nil, err
}
// Create the encapsulated block.
block, err := util.NewBlockFromBytes(blockBytes)
if err != nil {
return nil, err
}
return block, nil
}
// dbStoreBlockNode stores the block node data into the block
// index bucket. This overwrites the current entry if there exists one.
func dbStoreBlockNode(dbTx database.Tx, node *blockNode) error {
// Serialize block data to be stored.
w := bytes.NewBuffer(make([]byte, 0, blockHdrSize+1))
header := node.Header()
err := header.Serialize(w)
if err != nil {
return err
}
err = w.WriteByte(byte(node.status))
if err != nil {
return err
}
// Because genesis doesn't have selected parent, it's serialized as zero hash
selectedParentHash := &daghash.ZeroHash
if node.selectedParent != nil {
selectedParentHash = node.selectedParent.hash
}
_, err = w.Write(selectedParentHash[:])
if err != nil {
return err
}
err = binaryserializer.PutUint64(w, byteOrder, node.blueScore)
if err != nil {
return err
}
err = wire.WriteVarInt(w, uint64(len(node.blues)))
if err != nil {
return err
}
for _, blue := range node.blues {
_, err = w.Write(blue.hash[:])
if err != nil {
return err
}
}
value := w.Bytes()
// Write block header data to block index bucket.
blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)
key := BlockIndexKey(node.hash, node.blueScore)
return blockIndexBucket.Put(key, value)
}
// dbStoreBlock stores the provided block in the database if it is not already
// there. The full block data is written to ffldb.
func dbStoreBlock(dbTx database.Tx, block *util.Block) error {
hasBlock, err := dbTx.HasBlock(block.Hash())
if err != nil {
return err
}
if hasBlock {
return nil
}
return dbTx.StoreBlock(block)
}
// BlockIndexKey generates the binary key for an entry in the block index
// bucket. The key is composed of the block blue score encoded as a big-endian
// 64-bit unsigned int followed by the 32 byte block hash.
// The blue score component is important for iteration order.
func BlockIndexKey(blockHash *daghash.Hash, blueScore uint64) []byte {
indexKey := make([]byte, daghash.HashSize+8)
binary.BigEndian.PutUint64(indexKey[0:8], blueScore)
copy(indexKey[8:daghash.HashSize+8], blockHash[:])
return indexKey
}
func blockHashFromBlockIndexKey(BlockIndexKey []byte) (*daghash.Hash, error) {
return daghash.NewHash(BlockIndexKey[8 : daghash.HashSize+8])
}
// BlockByHash returns the block from the DAG with the given hash.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) BlockByHash(hash *daghash.Hash) (*util.Block, error) {
// Lookup the block hash in block index and ensure it is in the DAG
node := dag.index.LookupNode(hash)
if node == nil {
str := fmt.Sprintf("block %s is not in the main chain", hash)
return nil, errNotInDAG(str)
}
// Load the block from the database and return it.
var block *util.Block
err := dag.db.View(func(dbTx database.Tx) error {
var err error
block, err = dbFetchBlockByNode(dbTx, node)
return err
})
return block, err
}
// BlockHashesFrom returns a slice of blocks starting from startHash
// ordered by blueScore. If startHash is nil then the genesis block is used.
//
// This method MUST be called with the DAG lock held
func (dag *BlockDAG) BlockHashesFrom(startHash *daghash.Hash, limit int) ([]*daghash.Hash, error) {
blockHashes := make([]*daghash.Hash, 0, limit)
if startHash == nil {
startHash = dag.genesis.hash
// If we're starting from the beginning we should include the
// genesis hash in the result
blockHashes = append(blockHashes, dag.genesis.hash)
}
if !dag.BlockExists(startHash) {
return nil, errors.Errorf("block %s not found", startHash)
}
blueScore, err := dag.BlueScoreByBlockHash(startHash)
if err != nil {
return nil, err
}
err = dag.index.db.View(func(dbTx database.Tx) error {
blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)
startKey := BlockIndexKey(startHash, blueScore)
cursor := blockIndexBucket.Cursor()
cursor.Seek(startKey)
for ok := cursor.Next(); ok; ok = cursor.Next() {
key := cursor.Key()
blockHash, err := blockHashFromBlockIndexKey(key)
if err != nil {
return err
}
blockHashes = append(blockHashes, blockHash)
if len(blockHashes) == limit {
break
}
}
return nil
})
if err != nil {
return nil, err
}
return blockHashes, nil
}

266
blockdag/dagio_test.go Normal file
View File

@@ -0,0 +1,266 @@
// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"bytes"
"github.com/pkg/errors"
"reflect"
"testing"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util/daghash"
)
// TestErrNotInDAG ensures the functions related to errNotInDAG work
// as expected.
func TestErrNotInDAG(t *testing.T) {
errStr := "no block at height 1 exists"
err := error(errNotInDAG(errStr))
// Ensure the stringized output for the error is as expected.
if err.Error() != errStr {
t.Fatalf("errNotInDAG retuned unexpected error string - "+
"got %q, want %q", err.Error(), errStr)
}
// Ensure error is detected as the correct type.
if !isNotInDAGErr(err) {
t.Fatalf("isNotInDAGErr did not detect as expected type")
}
err = errors.New("something else")
if isNotInDAGErr(err) {
t.Fatalf("isNotInDAGErr detected incorrect type")
}
}
// TestUtxoSerialization ensures serializing and deserializing unspent
// trasaction output entries works as expected.
func TestUtxoSerialization(t *testing.T) {
t.Parallel()
tests := []struct {
name string
entry *UTXOEntry
serialized []byte
}{
// From tx in main blockchain:
// b7c3332bc138e2c9429818f5fed500bcc1746544218772389054dc8047d7cd3f:0
{
name: "blue score 1, coinbase",
entry: &UTXOEntry{
amount: 5000000000,
scriptPubKey: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
blockBlueScore: 1,
packedFlags: tfCoinbase,
},
serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"),
},
// From tx in main blockchain:
// 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb:1
{
name: "blue score 100001, not coinbase",
entry: &UTXOEntry{
amount: 1000000,
scriptPubKey: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"),
blockBlueScore: 100001,
packedFlags: 0,
},
serialized: hexToBytes("8b99420700ee8bd501094a7d5ca318da2506de35e1cb025ddc"),
},
}
for i, test := range tests {
// Ensure the utxo entry serializes to the expected value.
gotBytes := serializeUTXOEntry(test.entry)
if !bytes.Equal(gotBytes, test.serialized) {
t.Errorf("serializeUTXOEntry #%d (%s): mismatched "+
"bytes - got %x, want %x", i, test.name,
gotBytes, test.serialized)
continue
}
// Deserialize to a utxo entry.
utxoEntry, err := deserializeUTXOEntry(test.serialized)
if err != nil {
t.Errorf("deserializeUTXOEntry #%d (%s) unexpected "+
"error: %v", i, test.name, err)
continue
}
// Ensure the deserialized entry has the same properties as the
// ones in the test entry.
if utxoEntry.Amount() != test.entry.Amount() {
t.Errorf("deserializeUTXOEntry #%d (%s) mismatched "+
"amounts: got %d, want %d", i, test.name,
utxoEntry.Amount(), test.entry.Amount())
continue
}
if !bytes.Equal(utxoEntry.ScriptPubKey(), test.entry.ScriptPubKey()) {
t.Errorf("deserializeUTXOEntry #%d (%s) mismatched "+
"scripts: got %x, want %x", i, test.name,
utxoEntry.ScriptPubKey(), test.entry.ScriptPubKey())
continue
}
if utxoEntry.BlockBlueScore() != test.entry.BlockBlueScore() {
t.Errorf("deserializeUTXOEntry #%d (%s) mismatched "+
"block blue score: got %d, want %d", i, test.name,
utxoEntry.BlockBlueScore(), test.entry.BlockBlueScore())
continue
}
if utxoEntry.IsCoinbase() != test.entry.IsCoinbase() {
t.Errorf("deserializeUTXOEntry #%d (%s) mismatched "+
"coinbase flag: got %v, want %v", i, test.name,
utxoEntry.IsCoinbase(), test.entry.IsCoinbase())
continue
}
}
}
// TestUtxoEntryDeserializeErrors performs negative tests against deserializing
// unspent transaction outputs to ensure error paths work as expected.
func TestUtxoEntryDeserializeErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
serialized []byte
errType error
}{
{
name: "no data after header code",
serialized: hexToBytes("02"),
errType: errDeserialize(""),
},
{
name: "incomplete compressed txout",
serialized: hexToBytes("0232"),
errType: errDeserialize(""),
},
}
for _, test := range tests {
// Ensure the expected error type is returned and the returned
// entry is nil.
entry, err := deserializeUTXOEntry(test.serialized)
if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
t.Errorf("deserializeUTXOEntry (%s): expected error "+
"type does not match - got %T, want %T",
test.name, err, test.errType)
continue
}
if entry != nil {
t.Errorf("deserializeUTXOEntry (%s): returned entry "+
"is not nil", test.name)
continue
}
}
}
// TestDAGStateSerialization ensures serializing and deserializing the
// DAG state works as expected.
func TestDAGStateSerialization(t *testing.T) {
t.Parallel()
tests := []struct {
name string
state *dagState
serialized []byte
}{
{
name: "genesis",
state: &dagState{
TipHashes: []*daghash.Hash{newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")},
LastFinalityPoint: newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
},
serialized: []byte("{\"TipHashes\":[[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]],\"LastFinalityPoint\":[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]}"),
},
{
name: "block 1",
state: &dagState{
TipHashes: []*daghash.Hash{newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048")},
LastFinalityPoint: newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
},
serialized: []byte("{\"TipHashes\":[[72,96,235,24,191,27,22,32,227,126,148,144,252,138,66,117,20,65,111,215,81,89,171,134,104,142,154,131,0,0,0,0]],\"LastFinalityPoint\":[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]}"),
},
}
for i, test := range tests {
gotBytes, err := serializeDAGState(test.state)
if err != nil {
t.Errorf("serializeDAGState #%d (%s) "+
"unexpected error: %v", i, test.name, err)
continue
}
// Ensure the dagState serializes to the expected value.
if !bytes.Equal(gotBytes, test.serialized) {
t.Errorf("serializeDAGState #%d (%s): mismatched "+
"bytes - got %s, want %s", i, test.name,
string(gotBytes), string(test.serialized))
continue
}
// Ensure the serialized bytes are decoded back to the expected
// dagState.
state, err := deserializeDAGState(test.serialized)
if err != nil {
t.Errorf("deserializeDAGState #%d (%s) "+
"unexpected error: %v", i, test.name, err)
continue
}
if !reflect.DeepEqual(state, test.state) {
t.Errorf("deserializeDAGState #%d (%s) "+
"mismatched state - got %v, want %v", i,
test.name, state, test.state)
continue
}
}
}
// TestDAGStateDeserializeErrors performs negative tests against
// deserializing the DAG state to ensure error paths work as expected.
func TestDAGStateDeserializeErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
serialized []byte
errType error
}{
{
name: "nothing serialized",
serialized: hexToBytes(""),
errType: database.Error{ErrorCode: database.ErrCorruption},
},
{
name: "corrupted data",
serialized: []byte("[[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,7"),
errType: database.Error{ErrorCode: database.ErrCorruption},
},
}
for _, test := range tests {
// Ensure the expected error type and code is returned.
_, err := deserializeDAGState(test.serialized)
if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
t.Errorf("deserializeDAGState (%s): expected "+
"error type does not match - got %T, want %T",
test.name, err, test.errType)
continue
}
if derr, ok := err.(database.Error); ok {
tderr := test.errType.(database.Error)
if derr.ErrorCode != tderr.ErrorCode {
t.Errorf("deserializeDAGState (%s): "+
"wrong error code got: %v, want: %v",
test.name, derr.ErrorCode,
tderr.ErrorCode)
continue
}
}
}
}

52
blockdag/difficulty.go Normal file
View File

@@ -0,0 +1,52 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"math/big"
"time"
"github.com/kaspanet/kaspad/util"
)
// requiredDifficulty calculates the required difficulty for a
// block given its bluest parent.
func (dag *BlockDAG) requiredDifficulty(bluestParent *blockNode, newBlockTime time.Time) uint32 {
// Genesis block.
if bluestParent == nil || bluestParent.blueScore < dag.difficultyAdjustmentWindowSize+1 {
return dag.powMaxBits
}
// Fetch window of dag.difficultyAdjustmentWindowSize + 1 so we can have dag.difficultyAdjustmentWindowSize block intervals
timestampsWindow := blueBlockWindow(bluestParent, dag.difficultyAdjustmentWindowSize+1)
windowMinTimestamp, windowMaxTimeStamp := timestampsWindow.minMaxTimestamps()
// Remove the last block from the window so to calculate the average target of dag.difficultyAdjustmentWindowSize blocks
targetsWindow := timestampsWindow[:dag.difficultyAdjustmentWindowSize]
// Calculate new target difficulty as:
// averageWindowTarget * (windowMinTimestamp / (targetTimePerBlock * windowSize))
// The result uses integer division which means it will be slightly
// rounded down.
newTarget := targetsWindow.averageTarget()
newTarget.
Mul(newTarget, big.NewInt(windowMaxTimeStamp-windowMinTimestamp)).
Div(newTarget, big.NewInt(dag.targetTimePerBlock)).
Div(newTarget, big.NewInt(int64(dag.difficultyAdjustmentWindowSize)))
if newTarget.Cmp(dag.dagParams.PowMax) > 0 {
return dag.powMaxBits
}
newTargetBits := util.BigToCompact(newTarget)
return newTargetBits
}
// NextRequiredDifficulty calculates the required difficulty for a block that will
// be built on top of the current tips.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) NextRequiredDifficulty(timestamp time.Time) uint32 {
difficulty := dag.requiredDifficulty(dag.virtual.parents.bluest(), timestamp)
return difficulty
}

198
blockdag/difficulty_test.go Normal file
View File

@@ -0,0 +1,198 @@
// Copyright (c) 2014-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
"math/big"
"testing"
"time"
"github.com/kaspanet/kaspad/util"
)
// TestBigToCompact ensures BigToCompact converts big integers to the expected
// compact representation.
func TestBigToCompact(t *testing.T) {
tests := []struct {
in int64
out uint32
}{
{0, 0},
{-1, 25231360},
}
for x, test := range tests {
n := big.NewInt(test.in)
r := util.BigToCompact(n)
if r != test.out {
t.Errorf("TestBigToCompact test #%d failed: got %d want %d\n",
x, r, test.out)
return
}
}
}
// TestCompactToBig ensures CompactToBig converts numbers using the compact
// representation to the expected big intergers.
func TestCompactToBig(t *testing.T) {
tests := []struct {
in uint32
out int64
}{
{10000000, 0},
}
for x, test := range tests {
n := util.CompactToBig(test.in)
want := big.NewInt(test.out)
if n.Cmp(want) != 0 {
t.Errorf("TestCompactToBig test #%d failed: got %d want %d\n",
x, n.Int64(), want.Int64())
return
}
}
}
// TestCalcWork ensures CalcWork calculates the expected work value from values
// in compact representation.
func TestCalcWork(t *testing.T) {
tests := []struct {
in uint32
out int64
}{
{10000000, 0},
}
for x, test := range tests {
bits := uint32(test.in)
r := util.CalcWork(bits)
if r.Int64() != test.out {
t.Errorf("TestCalcWork test #%d failed: got %v want %d\n",
x, r.Int64(), test.out)
return
}
}
}
func TestDifficulty(t *testing.T) {
params := dagconfig.SimNetParams
params.K = 1
dag := newTestDAG(&params)
nonce := uint64(0)
zeroTime := time.Unix(0, 0)
addNode := func(parents blockSet, blockTime time.Time) *blockNode {
bluestParent := parents.bluest()
if blockTime == zeroTime {
blockTime = time.Unix(bluestParent.timestamp+1, 0)
}
header := &wire.BlockHeader{
ParentHashes: parents.hashes(),
Bits: dag.requiredDifficulty(bluestParent, blockTime),
Nonce: nonce,
Timestamp: blockTime,
HashMerkleRoot: &daghash.ZeroHash,
AcceptedIDMerkleRoot: &daghash.ZeroHash,
UTXOCommitment: &daghash.ZeroHash,
}
node := newBlockNode(header, parents, dag.dagParams.K)
node.updateParentsChildren()
nonce++
return node
}
tip := dag.genesis
for i := uint64(0); i < dag.difficultyAdjustmentWindowSize; i++ {
tip = addNode(setFromSlice(tip), zeroTime)
if tip.bits != dag.genesis.bits {
t.Fatalf("As long as the bluest parent's blue score is less then the difficulty adjustment window size, the difficulty should be the same as genesis'")
}
}
for i := uint64(0); i < dag.difficultyAdjustmentWindowSize+1000; i++ {
tip = addNode(setFromSlice(tip), zeroTime)
if tip.bits != dag.genesis.bits {
t.Fatalf("As long as the block rate remains the same, the difficulty shouldn't change")
}
}
nodeInThePast := addNode(setFromSlice(tip), tip.PastMedianTime(dag))
if nodeInThePast.bits != tip.bits {
t.Fatalf("The difficulty should only change when nodeInThePast is in the past of a block bluest parent")
}
tip = nodeInThePast
tip = addNode(setFromSlice(tip), zeroTime)
if tip.bits != nodeInThePast.bits {
t.Fatalf("The difficulty should only change when nodeInThePast is in the past of a block bluest parent")
}
tip = addNode(setFromSlice(tip), zeroTime)
if compareBits(tip.bits, nodeInThePast.bits) >= 0 {
t.Fatalf("tip.bits should be smaller than nodeInThePast.bits because nodeInThePast increased the block rate, so the difficulty should increase as well")
}
expectedBits := uint32(0x207ff395)
if tip.bits != expectedBits {
t.Errorf("tip.bits was expected to be %x but got %x", expectedBits, tip.bits)
}
// Increase block rate to increase difficulty
for i := uint64(0); i < dag.difficultyAdjustmentWindowSize; i++ {
tip = addNode(setFromSlice(tip), tip.PastMedianTime(dag))
if compareBits(tip.bits, tip.parents.bluest().bits) > 0 {
t.Fatalf("Because we're increasing the block rate, the difficulty can't decrease")
}
}
// Add blocks until difficulty stabilizes
lastBits := tip.bits
sameBitsCount := uint64(0)
for sameBitsCount < dag.difficultyAdjustmentWindowSize+1 {
tip = addNode(setFromSlice(tip), zeroTime)
if tip.bits == lastBits {
sameBitsCount++
} else {
lastBits = tip.bits
sameBitsCount = 0
}
}
slowNode := addNode(setFromSlice(tip), time.Unix(tip.timestamp+2, 0))
if slowNode.bits != tip.bits {
t.Fatalf("The difficulty should only change when slowNode is in the past of a block bluest parent")
}
tip = slowNode
tip = addNode(setFromSlice(tip), zeroTime)
if tip.bits != slowNode.bits {
t.Fatalf("The difficulty should only change when slowNode is in the past of a block bluest parent")
}
tip = addNode(setFromSlice(tip), zeroTime)
if compareBits(tip.bits, slowNode.bits) <= 0 {
t.Fatalf("tip.bits should be smaller than slowNode.bits because slowNode decreased the block rate, so the difficulty should decrease as well")
}
splitNode := addNode(setFromSlice(tip), zeroTime)
tip = splitNode
for i := 0; i < 100; i++ {
tip = addNode(setFromSlice(tip), zeroTime)
}
blueTip := tip
redChainTip := splitNode
for i := 0; i < 10; i++ {
redChainTip = addNode(setFromSlice(redChainTip), redChainTip.PastMedianTime(dag))
}
tipWithRedPast := addNode(setFromSlice(redChainTip, blueTip), zeroTime)
tipWithoutRedPast := addNode(setFromSlice(blueTip), zeroTime)
if tipWithoutRedPast.bits != tipWithRedPast.bits {
t.Fatalf("tipWithoutRedPast.bits should be the same as tipWithRedPast.bits because red blocks shouldn't affect the difficulty")
}
}
func compareBits(a uint32, b uint32) int {
aTarget := util.CompactToBig(a)
bTarget := util.CompactToBig(b)
return aTarget.Cmp(bTarget)
}

View File

@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
/*
Package blockchain implements bitcoin block handling and chain selection rules.
Package blockdag implements bitcoin block handling and chain selection rules.
The bitcoin block handling and chain selection rules are an integral, and quite
likely the most important, part of bitcoin. Unfortunately, at the time of
@@ -78,4 +78,4 @@ This package includes spec changes outlined by the following BIPs:
BIP0030 (https://en.bitcoin.it/wiki/BIP_0030)
BIP0034 (https://en.bitcoin.it/wiki/BIP_0034)
*/
package blockchain
package blockdag

View File

@@ -1,13 +1,33 @@
// Copyright (c) 2014 The btcsuite developers
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
package blockdag
import (
"fmt"
)
// DeploymentError identifies an error that indicates a deployment ID was
// specified that does not exist.
type DeploymentError uint32
// Error returns the assertion error as a human-readable string and satisfies
// the error interface.
func (e DeploymentError) Error() string {
return fmt.Sprintf("deployment ID %d does not exist", uint32(e))
}
// AssertError identifies an error that indicates an internal code consistency
// issue and should be treated as a critical and unrecoverable error.
type AssertError string
// Error returns the assertion error as a human-readable string and satisfies
// the error interface.
func (e AssertError) Error() string {
return "assertion failed: " + string(e)
}
// ErrorCode identifies a kind of error.
type ErrorCode int
@@ -17,9 +37,9 @@ const (
// exists.
ErrDuplicateBlock ErrorCode = iota
// ErrBlockTooBig indicates the serialized block size exceeds the
// maximum allowed size.
ErrBlockTooBig
// ErrBlockMassTooHigh indicates the mass of a block exceeds the maximum
// allowed limits.
ErrBlockMassTooHigh
// ErrBlockVersionTooOld indicates the block version is too old and is
// no longer accepted since the majority of the network has upgraded
@@ -40,6 +60,12 @@ const (
// the current time.
ErrTimeTooNew
// ErrNoParents indicates that the block is missing parents
ErrNoParents
// ErrWrongParentsOrder indicates that the block's parents are not ordered by hash, as expected
ErrWrongParentsOrder
// ErrDifficultyTooLow indicates the difficulty for the block is lower
// than the difficulty required by the most recent checkpoint.
ErrDifficultyTooLow
@@ -58,38 +84,30 @@ const (
// the expected value.
ErrBadMerkleRoot
// ErrBadUTXOCommitment indicates the calculated UTXO commitment does not match
// the expected value.
ErrBadUTXOCommitment
// ErrBadCheckpoint indicates a block that is expected to be at a
// checkpoint height does not match the expected one.
ErrBadCheckpoint
// ErrForkTooOld indicates a block is attempting to fork the block chain
// before the most recent checkpoint.
ErrForkTooOld
// ErrCheckpointTimeTooOld indicates a block has a timestamp before the
// most recent checkpoint.
ErrCheckpointTimeTooOld
// ErrFinalityPointTimeTooOld indicates a block has a timestamp before the
// last finality point.
ErrFinalityPointTimeTooOld
// ErrNoTransactions indicates the block does not have a least one
// transaction. A valid block must have at least the coinbase
// transaction.
ErrNoTransactions
// ErrTooManyTransactions indicates the block has more transactions than
// are allowed.
ErrTooManyTransactions
// ErrNoTxInputs indicates a transaction does not have any inputs. A
// valid transaction must have at least one input.
ErrNoTxInputs
// ErrNoTxOutputs indicates a transaction does not have any outputs. A
// valid transaction must have at least one output.
ErrNoTxOutputs
// ErrTxTooBig indicates a transaction exceeds the maximum allowed size
// when serialized.
ErrTxTooBig
// ErrTxMassTooHigh indicates the mass of a transaction exceeds the maximum
// allowed limits.
ErrTxMassTooHigh
// ErrBadTxOutValue indicates an output value for a transaction is
// invalid in some way such as being out of range.
@@ -104,9 +122,9 @@ const (
// range or not referencing one at all.
ErrBadTxInput
// ErrMissingTx indicates a transaction referenced by an input is
// missing.
ErrMissingTx
// ErrMissingTxOut indicates a transaction output referenced by an input
// either does not exist or has already been spent.
ErrMissingTxOut
// ErrUnfinalizedTx indicates a transaction has not been finalized.
// A valid block may only contain finalized transactions.
@@ -126,10 +144,6 @@ const (
// coinbase that has not yet reached the required maturity.
ErrImmatureSpend
// ErrDoubleSpend indicates a transaction is attempting to spend coins
// that have already been spent.
ErrDoubleSpend
// ErrSpendTooHigh indicates a transaction is attempting to spend more
// value than the sum of all of its inputs.
ErrSpendTooHigh
@@ -150,23 +164,12 @@ const (
// coinbase transaction.
ErrMultipleCoinbases
// ErrBadCoinbaseScriptLen indicates the length of the signature script
// for a coinbase transaction is not within the valid range.
ErrBadCoinbaseScriptLen
// ErrBadCoinbasePayloadLen indicates the length of the payload
// for a coinbase transaction is too high.
ErrBadCoinbasePayloadLen
// ErrBadCoinbaseValue indicates the amount of a coinbase value does
// not match the expected value of the subsidy plus the sum of all fees.
ErrBadCoinbaseValue
// ErrMissingCoinbaseHeight indicates the coinbase transaction for a
// block does not start with the serialized block block height as
// required for version 2 and higher blocks.
ErrMissingCoinbaseHeight
// ErrBadCoinbaseHeight indicates the serialized block height in the
// coinbase transaction for version 2 and higher blocks does not match
// the expected value.
ErrBadCoinbaseHeight
// ErrBadCoinbaseTransaction indicates that the block's coinbase transaction is not build as expected
ErrBadCoinbaseTransaction
// ErrScriptMalformed indicates a transaction script is malformed in
// some way. For example, it might be longer than the maximum allowed
@@ -178,48 +181,95 @@ const (
// such signature verification failures and execution past the end of
// the stack.
ErrScriptValidation
// ErrParentBlockUnknown indicates that the parent block is not known.
ErrParentBlockUnknown
// ErrInvalidAncestorBlock indicates that an ancestor of this block has
// already failed validation.
ErrInvalidAncestorBlock
// ErrParentBlockNotCurrentTips indicates that the block's parents are not the
// current tips. This is not a block validation rule, but is required
// for block proposals submitted via getblocktemplate RPC.
ErrParentBlockNotCurrentTips
// ErrWithDiff indicates that there was an error with UTXOSet.WithDiff
ErrWithDiff
// ErrFinality indicates that a block doesn't adhere to the finality rules
ErrFinality
// ErrTransactionsNotSorted indicates that transactions in block are not
// sorted by subnetwork
ErrTransactionsNotSorted
// ErrInvalidGas transaction wants to use more GAS than allowed
// by subnetwork
ErrInvalidGas
// ErrInvalidPayload transaction includes a payload in a subnetwork that doesn't allow
// a Payload
ErrInvalidPayload
// ErrInvalidPayloadHash invalid hash of transaction's payload
ErrInvalidPayloadHash
// ErrSubnetwork indicates that a block doesn't adhere to the subnetwork
// registry rules
ErrSubnetworkRegistry
// ErrInvalidParentsRelation indicates that one of the parents of a block
// is also an ancestor of another parent
ErrInvalidParentsRelation
)
// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrDuplicateBlock: "ErrDuplicateBlock",
ErrBlockTooBig: "ErrBlockTooBig",
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
ErrInvalidTime: "ErrInvalidTime",
ErrTimeTooOld: "ErrTimeTooOld",
ErrTimeTooNew: "ErrTimeTooNew",
ErrDifficultyTooLow: "ErrDifficultyTooLow",
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty",
ErrHighHash: "ErrHighHash",
ErrBadMerkleRoot: "ErrBadMerkleRoot",
ErrBadCheckpoint: "ErrBadCheckpoint",
ErrForkTooOld: "ErrForkTooOld",
ErrCheckpointTimeTooOld: "ErrCheckpointTimeTooOld",
ErrNoTransactions: "ErrNoTransactions",
ErrTooManyTransactions: "ErrTooManyTransactions",
ErrNoTxInputs: "ErrNoTxInputs",
ErrNoTxOutputs: "ErrNoTxOutputs",
ErrTxTooBig: "ErrTxTooBig",
ErrBadTxOutValue: "ErrBadTxOutValue",
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
ErrBadTxInput: "ErrBadTxInput",
ErrMissingTx: "ErrMissingTx",
ErrUnfinalizedTx: "ErrUnfinalizedTx",
ErrDuplicateTx: "ErrDuplicateTx",
ErrOverwriteTx: "ErrOverwriteTx",
ErrImmatureSpend: "ErrImmatureSpend",
ErrDoubleSpend: "ErrDoubleSpend",
ErrSpendTooHigh: "ErrSpendTooHigh",
ErrBadFees: "ErrBadFees",
ErrTooManySigOps: "ErrTooManySigOps",
ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase",
ErrMultipleCoinbases: "ErrMultipleCoinbases",
ErrBadCoinbaseScriptLen: "ErrBadCoinbaseScriptLen",
ErrBadCoinbaseValue: "ErrBadCoinbaseValue",
ErrMissingCoinbaseHeight: "ErrMissingCoinbaseHeight",
ErrBadCoinbaseHeight: "ErrBadCoinbaseHeight",
ErrScriptMalformed: "ErrScriptMalformed",
ErrScriptValidation: "ErrScriptValidation",
ErrDuplicateBlock: "ErrDuplicateBlock",
ErrBlockMassTooHigh: "ErrBlockMassTooHigh",
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
ErrInvalidTime: "ErrInvalidTime",
ErrTimeTooOld: "ErrTimeTooOld",
ErrTimeTooNew: "ErrTimeTooNew",
ErrNoParents: "ErrNoParents",
ErrWrongParentsOrder: "ErrWrongParentsOrder",
ErrDifficultyTooLow: "ErrDifficultyTooLow",
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty",
ErrHighHash: "ErrHighHash",
ErrBadMerkleRoot: "ErrBadMerkleRoot",
ErrBadCheckpoint: "ErrBadCheckpoint",
ErrFinalityPointTimeTooOld: "ErrFinalityPointTimeTooOld",
ErrNoTransactions: "ErrNoTransactions",
ErrNoTxInputs: "ErrNoTxInputs",
ErrTxMassTooHigh: "ErrTxMassTooHigh",
ErrBadTxOutValue: "ErrBadTxOutValue",
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
ErrBadTxInput: "ErrBadTxInput",
ErrMissingTxOut: "ErrMissingTxOut",
ErrUnfinalizedTx: "ErrUnfinalizedTx",
ErrDuplicateTx: "ErrDuplicateTx",
ErrOverwriteTx: "ErrOverwriteTx",
ErrImmatureSpend: "ErrImmatureSpend",
ErrSpendTooHigh: "ErrSpendTooHigh",
ErrBadFees: "ErrBadFees",
ErrTooManySigOps: "ErrTooManySigOps",
ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase",
ErrMultipleCoinbases: "ErrMultipleCoinbases",
ErrBadCoinbasePayloadLen: "ErrBadCoinbasePayloadLen",
ErrBadCoinbaseTransaction: "ErrBadCoinbaseTransaction",
ErrScriptMalformed: "ErrScriptMalformed",
ErrScriptValidation: "ErrScriptValidation",
ErrParentBlockUnknown: "ErrParentBlockUnknown",
ErrInvalidAncestorBlock: "ErrInvalidAncestorBlock",
ErrParentBlockNotCurrentTips: "ErrParentBlockNotCurrentTips",
ErrWithDiff: "ErrWithDiff",
ErrFinality: "ErrFinality",
ErrTransactionsNotSorted: "ErrTransactionsNotSorted",
ErrInvalidGas: "ErrInvalidGas",
ErrInvalidPayload: "ErrInvalidPayload",
ErrInvalidPayloadHash: "ErrInvalidPayloadHash",
ErrInvalidParentsRelation: "ErrInvalidParentsRelation",
}
// String returns the ErrorCode as a human-readable name.

145
blockdag/error_test.go Normal file
View File

@@ -0,0 +1,145 @@
// Copyright (c) 2014-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"fmt"
"testing"
)
// TestErrorCodeStringer tests the stringized output for the ErrorCode type.
func TestErrorCodeStringer(t *testing.T) {
tests := []struct {
in ErrorCode
want string
}{
{ErrDuplicateBlock, "ErrDuplicateBlock"},
{ErrBlockMassTooHigh, "ErrBlockMassTooHigh"},
{ErrBlockVersionTooOld, "ErrBlockVersionTooOld"},
{ErrInvalidTime, "ErrInvalidTime"},
{ErrTimeTooOld, "ErrTimeTooOld"},
{ErrTimeTooNew, "ErrTimeTooNew"},
{ErrNoParents, "ErrNoParents"},
{ErrWrongParentsOrder, "ErrWrongParentsOrder"},
{ErrDifficultyTooLow, "ErrDifficultyTooLow"},
{ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"},
{ErrHighHash, "ErrHighHash"},
{ErrBadMerkleRoot, "ErrBadMerkleRoot"},
{ErrBadCheckpoint, "ErrBadCheckpoint"},
{ErrFinalityPointTimeTooOld, "ErrFinalityPointTimeTooOld"},
{ErrNoTransactions, "ErrNoTransactions"},
{ErrNoTxInputs, "ErrNoTxInputs"},
{ErrTxMassTooHigh, "ErrTxMassTooHigh"},
{ErrBadTxOutValue, "ErrBadTxOutValue"},
{ErrDuplicateTxInputs, "ErrDuplicateTxInputs"},
{ErrBadTxInput, "ErrBadTxInput"},
{ErrBadCheckpoint, "ErrBadCheckpoint"},
{ErrMissingTxOut, "ErrMissingTxOut"},
{ErrUnfinalizedTx, "ErrUnfinalizedTx"},
{ErrDuplicateTx, "ErrDuplicateTx"},
{ErrOverwriteTx, "ErrOverwriteTx"},
{ErrImmatureSpend, "ErrImmatureSpend"},
{ErrSpendTooHigh, "ErrSpendTooHigh"},
{ErrBadFees, "ErrBadFees"},
{ErrTooManySigOps, "ErrTooManySigOps"},
{ErrFirstTxNotCoinbase, "ErrFirstTxNotCoinbase"},
{ErrMultipleCoinbases, "ErrMultipleCoinbases"},
{ErrBadCoinbasePayloadLen, "ErrBadCoinbasePayloadLen"},
{ErrBadCoinbaseTransaction, "ErrBadCoinbaseTransaction"},
{ErrScriptMalformed, "ErrScriptMalformed"},
{ErrScriptValidation, "ErrScriptValidation"},
{ErrParentBlockUnknown, "ErrParentBlockUnknown"},
{ErrInvalidAncestorBlock, "ErrInvalidAncestorBlock"},
{ErrParentBlockNotCurrentTips, "ErrParentBlockNotCurrentTips"},
{ErrWithDiff, "ErrWithDiff"},
{ErrFinality, "ErrFinality"},
{ErrTransactionsNotSorted, "ErrTransactionsNotSorted"},
{ErrInvalidGas, "ErrInvalidGas"},
{ErrInvalidPayload, "ErrInvalidPayload"},
{ErrInvalidPayloadHash, "ErrInvalidPayloadHash"},
{ErrInvalidParentsRelation, "ErrInvalidParentsRelation"},
{0xffff, "Unknown ErrorCode (65535)"},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.String()
if result != test.want {
t.Errorf("String #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}
// TestRuleError tests the error output for the RuleError type.
func TestRuleError(t *testing.T) {
tests := []struct {
in RuleError
want string
}{
{
RuleError{Description: "duplicate block"},
"duplicate block",
},
{
RuleError{Description: "human-readable error"},
"human-readable error",
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.Error()
if result != test.want {
t.Errorf("Error #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}
// TestDeploymentError tests the stringized output for the DeploymentError type.
func TestDeploymentError(t *testing.T) {
t.Parallel()
tests := []struct {
in DeploymentError
want string
}{
{
DeploymentError(0),
"deployment ID 0 does not exist",
},
{
DeploymentError(10),
"deployment ID 10 does not exist",
},
{
DeploymentError(123),
"deployment ID 123 does not exist",
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.Error()
if result != test.want {
t.Errorf("Error #%d\n got: %s want: %s", i, result,
test.want)
continue
}
}
}
func TestAssertError(t *testing.T) {
message := "abc 123"
err := AssertError(message)
expectedMessage := fmt.Sprintf("assertion failed: %s", message)
if expectedMessage != err.Error() {
t.Errorf("Unexpected AssertError message. "+
"Got: %s, want: %s", err.Error(), expectedMessage)
}
}

View File

@@ -0,0 +1,558 @@
package blockdag_test
import (
"fmt"
"github.com/pkg/errors"
"math"
"testing"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/testtools"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/mining"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/wire"
)
// TestFinality checks that the finality mechanism works as expected.
// This is how the flow goes:
// 1) We build a chain of params.FinalityInterval blocks and call its tip altChainTip.
// 2) We build another chain (let's call it mainChain) of 2 * params.FinalityInterval
// blocks, which points to genesis, and then we check that the block in that
// chain with height of params.FinalityInterval is marked as finality point (This is
// very predictable, because the blue score of each new block in a chain is the
// parents plus one).
// 3) We make a new child to block with height (2 * params.FinalityInterval - 1)
// in mainChain, and we check that connecting it to the DAG
// doesn't affect the last finality point.
// 4) We make a block that points to genesis, and check that it
// gets rejected because its blue score is lower then the last finality
// point.
// 5) We make a block that points to altChainTip, and check that it
// gets rejected because it doesn't have the last finality point in
// its selected parent chain.
func TestFinality(t *testing.T) {
params := dagconfig.SimNetParams
params.K = 1
params.FinalityInterval = 100
dag, teardownFunc, err := blockdag.DAGSetup("TestFinality", blockdag.Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
buildNodeToDag := func(parentHashes []*daghash.Hash) (*util.Block, error) {
msgBlock, err := mining.PrepareBlockForTest(dag, &params, parentHashes, nil, false)
if err != nil {
return nil, err
}
block := util.NewBlock(msgBlock)
isOrphan, delay, err := dag.ProcessBlock(block, blockdag.BFNoPoWCheck)
if err != nil {
return nil, err
}
if delay != 0 {
return nil, errors.Errorf("ProcessBlock: block " +
"is too far in the future")
}
if isOrphan {
return nil, errors.Errorf("ProcessBlock: unexpected returned orphan block")
}
return block, nil
}
genesis := util.NewBlock(params.GenesisBlock)
currentNode := genesis
// First we build a chain of params.FinalityInterval blocks for future use
for i := 0; i < params.FinalityInterval; i++ {
currentNode, err = buildNodeToDag([]*daghash.Hash{currentNode.Hash()})
if err != nil {
t.Fatalf("TestFinality: buildNodeToDag unexpectedly returned an error: %v", err)
}
}
altChainTip := currentNode
// Now we build a new chain of 2 * params.FinalityInterval blocks, pointed to genesis, and
// we expect the block with height 1 * params.FinalityInterval to be the last finality point
currentNode = genesis
for i := 0; i < params.FinalityInterval; i++ {
currentNode, err = buildNodeToDag([]*daghash.Hash{currentNode.Hash()})
if err != nil {
t.Fatalf("TestFinality: buildNodeToDag unexpectedly returned an error: %v", err)
}
}
expectedFinalityPoint := currentNode
for i := 0; i < params.FinalityInterval; i++ {
currentNode, err = buildNodeToDag([]*daghash.Hash{currentNode.Hash()})
if err != nil {
t.Fatalf("TestFinality: buildNodeToDag unexpectedly returned an error: %v", err)
}
}
if !dag.LastFinalityPointHash().IsEqual(expectedFinalityPoint.Hash()) {
t.Errorf("TestFinality: dag.lastFinalityPoint expected to be %v but got %v", expectedFinalityPoint, dag.LastFinalityPointHash())
}
// Here we check that even if we create a parallel tip (a new tip with
// the same parents as the current one) with the same blue score as the
// current tip, it still won't affect the last finality point.
_, err = buildNodeToDag(currentNode.MsgBlock().Header.ParentHashes)
if err != nil {
t.Fatalf("TestFinality: buildNodeToDag unexpectedly returned an error: %v", err)
}
if !dag.LastFinalityPointHash().IsEqual(expectedFinalityPoint.Hash()) {
t.Errorf("TestFinality: dag.lastFinalityPoint was unexpectly changed")
}
// Here we check that a block with lower blue score than the last finality
// point will get rejected
fakeCoinbaseTx, err := dag.NextBlockCoinbaseTransaction(nil, nil)
if err != nil {
t.Errorf("NextBlockCoinbaseTransaction: %s", err)
}
merkleRoot := blockdag.BuildHashMerkleTreeStore([]*util.Tx{fakeCoinbaseTx}).Root()
beforeFinalityBlock := wire.NewMsgBlock(&wire.BlockHeader{
Version: 0x10000000,
ParentHashes: []*daghash.Hash{genesis.Hash()},
HashMerkleRoot: merkleRoot,
AcceptedIDMerkleRoot: &daghash.ZeroHash,
UTXOCommitment: &daghash.ZeroHash,
Timestamp: dag.SelectedTipHeader().Timestamp,
Bits: genesis.MsgBlock().Header.Bits,
})
beforeFinalityBlock.AddTransaction(fakeCoinbaseTx.MsgTx())
_, _, err = dag.ProcessBlock(util.NewBlock(beforeFinalityBlock), blockdag.BFNoPoWCheck)
if err == nil {
t.Errorf("TestFinality: buildNodeToDag expected an error but got <nil>")
}
rErr, ok := err.(blockdag.RuleError)
if ok {
if rErr.ErrorCode != blockdag.ErrFinality {
t.Errorf("TestFinality: buildNodeToDag expected an error with code %v but instead got %v", blockdag.ErrFinality, rErr.ErrorCode)
}
} else {
t.Errorf("TestFinality: buildNodeToDag got unexpected error: %v", err)
}
// Here we check that a block that doesn't have the last finality point in
// its selected parent chain will get rejected
_, err = buildNodeToDag([]*daghash.Hash{altChainTip.Hash()})
if err == nil {
t.Errorf("TestFinality: buildNodeToDag expected an error but got <nil>")
}
rErr, ok = err.(blockdag.RuleError)
if ok {
if rErr.ErrorCode != blockdag.ErrFinality {
t.Errorf("TestFinality: buildNodeToDag expected an error with code %v but instead got %v", blockdag.ErrFinality, rErr.ErrorCode)
}
} else {
t.Errorf("TestFinality: buildNodeToDag got unexpected error: %v", rErr)
}
}
// TestFinalityInterval tests that the finality interval is
// smaller then wire.MaxInvPerMsg, so when a peer receives
// a getblocks message it should always be able to send
// all the necessary invs.
func TestFinalityInterval(t *testing.T) {
params := dagconfig.SimNetParams
if params.FinalityInterval > wire.MaxInvPerMsg {
t.Errorf("dagconfig.SimNetParams.FinalityInterval should be lower or equal to wire.MaxInvPerMsg")
}
}
// TestSubnetworkRegistry tests the full subnetwork registry flow
func TestSubnetworkRegistry(t *testing.T) {
params := dagconfig.SimNetParams
params.K = 1
params.BlockCoinbaseMaturity = 0
dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
gasLimit := uint64(12345)
subnetworkID, err := testtools.RegisterSubnetworkForTest(dag, &params, gasLimit)
if err != nil {
t.Fatalf("could not register network: %s", err)
}
limit, err := dag.SubnetworkStore.GasLimit(subnetworkID)
if err != nil {
t.Fatalf("could not retrieve gas limit: %s", err)
}
if limit != gasLimit {
t.Fatalf("unexpected gas limit. want: %d, got: %d", gasLimit, limit)
}
}
func TestChainedTransactions(t *testing.T) {
params := dagconfig.SimNetParams
params.BlockCoinbaseMaturity = 0
// Create a new database and dag instance to run tests against.
dag, teardownFunc, err := blockdag.DAGSetup("TestChainedTransactions", blockdag.Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup dag instance: %v", err)
}
defer teardownFunc()
block1, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{params.GenesisHash}, nil, false)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
isOrphan, delay, err := dag.ProcessBlock(util.NewBlock(block1), blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: block1 " +
"is too far in the future")
}
if isOrphan {
t.Fatalf("ProcessBlock: block1 got unexpectedly orphaned")
}
cbTx := block1.Transactions[0]
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
if err != nil {
t.Fatalf("Failed to build signature script: %s", err)
}
txIn := &wire.TxIn{
PreviousOutpoint: wire.Outpoint{TxID: *cbTx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: wire.MaxTxInSequenceNum,
}
txOut := &wire.TxOut{
ScriptPubKey: blockdag.OpTrueScript,
Value: uint64(1),
}
tx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut})
chainedTxIn := &wire.TxIn{
PreviousOutpoint: wire.Outpoint{TxID: *tx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: wire.MaxTxInSequenceNum,
}
scriptPubKey, err := txscript.PayToScriptHashScript(blockdag.OpTrueScript)
if err != nil {
t.Fatalf("Failed to build public key script: %s", err)
}
chainedTxOut := &wire.TxOut{
ScriptPubKey: scriptPubKey,
Value: uint64(1),
}
chainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{chainedTxIn}, []*wire.TxOut{chainedTxOut})
block2, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{tx, chainedTx}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
//Checks that dag.ProcessBlock fails because we don't allow a transaction to spend another transaction from the same block
isOrphan, delay, err = dag.ProcessBlock(util.NewBlock(block2), blockdag.BFNoPoWCheck)
if err == nil {
t.Errorf("ProcessBlock expected an error")
} else if rErr, ok := err.(blockdag.RuleError); ok {
if rErr.ErrorCode != blockdag.ErrMissingTxOut {
t.Errorf("ProcessBlock expected an %v error code but got %v", blockdag.ErrMissingTxOut, rErr.ErrorCode)
}
} else {
t.Errorf("ProcessBlock expected a blockdag.RuleError but got %v", err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: block2 " +
"is too far in the future")
}
if isOrphan {
t.Errorf("ProcessBlock: block2 got unexpectedly orphaned")
}
nonChainedTxIn := &wire.TxIn{
PreviousOutpoint: wire.Outpoint{TxID: *cbTx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: wire.MaxTxInSequenceNum,
}
nonChainedTxOut := &wire.TxOut{
ScriptPubKey: scriptPubKey,
Value: uint64(1),
}
nonChainedTx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{nonChainedTxIn}, []*wire.TxOut{nonChainedTxOut})
block3, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{nonChainedTx}, false)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
//Checks that dag.ProcessBlock doesn't fail because all of its transaction are dependant on transactions from previous blocks
isOrphan, delay, err = dag.ProcessBlock(util.NewBlock(block3), blockdag.BFNoPoWCheck)
if err != nil {
t.Errorf("ProcessBlock: %v", err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: block3 " +
"is too far in the future")
}
if isOrphan {
t.Errorf("ProcessBlock: block3 got unexpectedly orphaned")
}
}
// TestOrderInDiffFromAcceptanceData makes sure that the order of transactions in
// dag.diffFromAcceptanceData is such that if txA is spent by txB then txA is processed
// before txB.
func TestOrderInDiffFromAcceptanceData(t *testing.T) {
// Create a new database and DAG instance to run tests against.
params := dagconfig.SimNetParams
params.K = math.MaxUint32
dag, teardownFunc, err := blockdag.DAGSetup("TestOrderInDiffFromAcceptanceData", blockdag.Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
dag.TestSetCoinbaseMaturity(0)
createBlock := func(previousBlock *util.Block) *util.Block {
// Prepare a transaction that spends the previous block's coinbase transaction
var txs []*wire.MsgTx
if !previousBlock.IsGenesis() {
previousCoinbaseTx := previousBlock.MsgBlock().Transactions[0]
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
if err != nil {
t.Fatalf("TestOrderInDiffFromAcceptanceData: Failed to build signature script: %s", err)
}
txIn := &wire.TxIn{
PreviousOutpoint: wire.Outpoint{TxID: *previousCoinbaseTx.TxID(), Index: 0},
SignatureScript: signatureScript,
Sequence: wire.MaxTxInSequenceNum,
}
txOut := &wire.TxOut{
ScriptPubKey: blockdag.OpTrueScript,
Value: uint64(1),
}
txs = append(txs, wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut}))
}
// Create the block
msgBlock, err := mining.PrepareBlockForTest(dag, &params, []*daghash.Hash{previousBlock.Hash()}, txs, false)
if err != nil {
t.Fatalf("TestOrderInDiffFromAcceptanceData: Failed to prepare block: %s", err)
}
// Add the block to the DAG
newBlock := util.NewBlock(msgBlock)
isOrphan, delay, err := dag.ProcessBlock(newBlock, blockdag.BFNoPoWCheck)
if err != nil {
t.Errorf("TestOrderInDiffFromAcceptanceData: %s", err)
}
if delay != 0 {
t.Fatalf("TestOrderInDiffFromAcceptanceData: block is too far in the future")
}
if isOrphan {
t.Fatalf("TestOrderInDiffFromAcceptanceData: block got unexpectedly orphaned")
}
return newBlock
}
// Create two block chains starting from the genesis block. Every time a block is added
// one of the chains is selected as the selected parent chain while all the blocks in
// the other chain (and their transactions) get accepted by the new virtual. If the
// transactions in the non-selected parent chain get processed in the wrong order then
// diffFromAcceptanceData panics.
blockAmountPerChain := 100
chainATip := util.NewBlock(params.GenesisBlock)
chainBTip := chainATip
for i := 0; i < blockAmountPerChain; i++ {
chainATip = createBlock(chainATip)
chainBTip = createBlock(chainBTip)
}
}
// TestGasLimit tests the gas limit rules
func TestGasLimit(t *testing.T) {
params := dagconfig.SimNetParams
params.K = 1
params.BlockCoinbaseMaturity = 0
dag, teardownFunc, err := blockdag.DAGSetup("TestSubnetworkRegistry", blockdag.Config{
DAGParams: &params,
})
if err != nil {
t.Fatalf("Failed to setup DAG instance: %v", err)
}
defer teardownFunc()
// First we prepare a subnetwork and a block with coinbase outputs to fund our tests
gasLimit := uint64(12345)
subnetworkID, err := testtools.RegisterSubnetworkForTest(dag, &params, gasLimit)
if err != nil {
t.Fatalf("could not register network: %s", err)
}
cbTxs := []*wire.MsgTx{}
for i := 0; i < 4; i++ {
fundsBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), nil, false)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
isOrphan, delay, err := dag.ProcessBlock(util.NewBlock(fundsBlock), blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: the funds block " +
"is too far in the future")
}
if isOrphan {
t.Fatalf("ProcessBlock: fundsBlock got unexpectedly orphan")
}
cbTxs = append(cbTxs, fundsBlock.Transactions[util.CoinbaseTransactionIndex])
}
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
if err != nil {
t.Fatalf("Failed to build signature script: %s", err)
}
scriptPubKey, err := txscript.PayToScriptHashScript(blockdag.OpTrueScript)
if err != nil {
t.Fatalf("Failed to build public key script: %s", err)
}
tx1In := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxs[0].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
tx1Out := &wire.TxOut{
Value: cbTxs[0].TxOut[0].Value,
ScriptPubKey: scriptPubKey,
}
tx1 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{tx1In}, []*wire.TxOut{tx1Out}, subnetworkID, 10000, []byte{})
tx2In := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxs[1].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
tx2Out := &wire.TxOut{
Value: cbTxs[1].TxOut[0].Value,
ScriptPubKey: scriptPubKey,
}
tx2 := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{tx2In}, []*wire.TxOut{tx2Out}, subnetworkID, 10000, []byte{})
// Here we check that we can't process a block that has transactions that exceed the gas limit
overLimitBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1, tx2}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
isOrphan, delay, err := dag.ProcessBlock(util.NewBlock(overLimitBlock), blockdag.BFNoPoWCheck)
if err == nil {
t.Fatalf("ProcessBlock expected to have an error in block that exceeds gas limit")
}
rErr, ok := err.(blockdag.RuleError)
if !ok {
t.Fatalf("ProcessBlock expected a RuleError, but got %v", err)
} else if rErr.ErrorCode != blockdag.ErrInvalidGas {
t.Fatalf("ProcessBlock expected error code %s but got %s", blockdag.ErrInvalidGas, rErr.ErrorCode)
}
if delay != 0 {
t.Fatalf("ProcessBlock: overLimitBlock " +
"is too far in the future")
}
if isOrphan {
t.Fatalf("ProcessBlock: overLimitBlock got unexpectedly orphan")
}
overflowGasTxIn := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxs[2].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
overflowGasTxOut := &wire.TxOut{
Value: cbTxs[2].TxOut[0].Value,
ScriptPubKey: scriptPubKey,
}
overflowGasTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{overflowGasTxIn}, []*wire.TxOut{overflowGasTxOut},
subnetworkID, math.MaxUint64, []byte{})
// Here we check that we can't process a block that its transactions' gas overflows uint64
overflowGasBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1, overflowGasTx}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
isOrphan, delay, err = dag.ProcessBlock(util.NewBlock(overflowGasBlock), blockdag.BFNoPoWCheck)
if err == nil {
t.Fatalf("ProcessBlock expected to have an error")
}
rErr, ok = err.(blockdag.RuleError)
if !ok {
t.Fatalf("ProcessBlock expected a RuleError, but got %v", err)
} else if rErr.ErrorCode != blockdag.ErrInvalidGas {
t.Fatalf("ProcessBlock expected error code %s but got %s", blockdag.ErrInvalidGas, rErr.ErrorCode)
}
if isOrphan {
t.Fatalf("ProcessBlock: overLimitBlock got unexpectedly orphan")
}
nonExistentSubnetwork := &subnetworkid.SubnetworkID{123}
nonExistentSubnetworkTxIn := &wire.TxIn{
PreviousOutpoint: *wire.NewOutpoint(cbTxs[3].TxID(), 0),
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
nonExistentSubnetworkTxOut := &wire.TxOut{
Value: cbTxs[3].TxOut[0].Value,
ScriptPubKey: scriptPubKey,
}
nonExistentSubnetworkTx := wire.NewSubnetworkMsgTx(wire.TxVersion, []*wire.TxIn{nonExistentSubnetworkTxIn},
[]*wire.TxOut{nonExistentSubnetworkTxOut}, nonExistentSubnetwork, 1, []byte{})
nonExistentSubnetworkBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{nonExistentSubnetworkTx, overflowGasTx}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
// Here we check that we can't process a block with a transaction from a non-existent subnetwork
isOrphan, delay, err = dag.ProcessBlock(util.NewBlock(nonExistentSubnetworkBlock), blockdag.BFNoPoWCheck)
expectedErrStr := fmt.Sprintf("Error getting gas limit for subnetworkID '%s': subnetwork '%s' not found",
nonExistentSubnetwork, nonExistentSubnetwork)
if err.Error() != expectedErrStr {
t.Fatalf("ProcessBlock expected error \"%v\" but got \"%v\"", expectedErrStr, err)
}
// Here we check that we can process a block with a transaction that doesn't exceed the gas limit
validBlock, err := mining.PrepareBlockForTest(dag, &params, dag.TipHashes(), []*wire.MsgTx{tx1}, true)
if err != nil {
t.Fatalf("PrepareBlockForTest: %v", err)
}
isOrphan, delay, err = dag.ProcessBlock(util.NewBlock(validBlock), blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock: %v", err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: overLimitBlock " +
"is too far in the future")
}
if isOrphan {
t.Fatalf("ProcessBlock: overLimitBlock got unexpectedly orphan")
}
}

318
blockdag/fullblocks_test.go Normal file
View File

@@ -0,0 +1,318 @@
// Copyright (c) 2016 The Decred developers
// Copyright (c) 2016-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag_test
import (
"bytes"
"github.com/pkg/errors"
"os"
"path/filepath"
"testing"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/blockdag/fullblocktests"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/database"
_ "github.com/kaspanet/kaspad/database/ffldb"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
)
const (
// testDbType is the database backend type to use for the tests.
testDbType = "ffldb"
// testDbRoot is the root directory used to create all test databases.
testDbRoot = "testdbs"
// blockDataNet is the expected network in the test block data.
blockDataNet = wire.MainNet
)
// filesExists returns whether or not the named file or directory exists.
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// isSupportedDbType returns whether or not the passed database type is
// currently supported.
func isSupportedDbType(dbType string) bool {
supportedDrivers := database.SupportedDrivers()
for _, driver := range supportedDrivers {
if dbType == driver {
return true
}
}
return false
}
// DAGSetup is used to create a new db and chain instance with the genesis
// block already inserted. In addition to the new chain instance, it returns
// a teardown function the caller should invoke when done testing to clean up.
func DAGSetup(dbName string, params *dagconfig.Params) (*blockdag.BlockDAG, func(), error) {
if !isSupportedDbType(testDbType) {
return nil, nil, errors.Errorf("unsupported db type %v", testDbType)
}
// Handle memory database specially since it doesn't need the disk
// specific handling.
var db database.DB
var teardown func()
if testDbType == "memdb" {
ndb, err := database.Create(testDbType)
if err != nil {
return nil, nil, errors.Errorf("error creating db: %v", err)
}
db = ndb
// Setup a teardown function for cleaning up. This function is
// returned to the caller to be invoked when it is done testing.
teardown = func() {
db.Close()
}
} else {
// Create the root directory for test databases.
if !fileExists(testDbRoot) {
if err := os.MkdirAll(testDbRoot, 0700); err != nil {
err := errors.Errorf("unable to create test db "+
"root: %v", err)
return nil, nil, err
}
}
// Create a new database to store the accepted blocks into.
dbPath := filepath.Join(testDbRoot, dbName)
_ = os.RemoveAll(dbPath)
ndb, err := database.Create(testDbType, dbPath, blockDataNet)
if err != nil {
return nil, nil, errors.Errorf("error creating db: %v", err)
}
db = ndb
// Setup a teardown function for cleaning up. This function is
// returned to the caller to be invoked when it is done testing.
teardown = func() {
db.Close()
os.RemoveAll(dbPath)
os.RemoveAll(testDbRoot)
}
}
// Copy the chain params to ensure any modifications the tests do to
// the DAG parameters do not affect the global instance.
paramsCopy := *params
// Create the main chain instance.
chain, err := blockdag.New(&blockdag.Config{
DB: db,
DAGParams: &paramsCopy,
Checkpoints: nil,
TimeSource: blockdag.NewMedianTime(),
SigCache: txscript.NewSigCache(1000),
})
if err != nil {
teardown()
err := errors.Errorf("failed to create chain instance: %v", err)
return nil, nil, err
}
return chain, teardown, nil
}
// TestFullBlocks ensures all tests generated by the fullblocktests package
// have the expected result when processed via ProcessBlock.
func TestFullBlocks(t *testing.T) {
// TODO: (Stas) This test was disabled for until we have implemented Phantom
// Ticket: https://daglabs.atlassian.net/browse/DEV-60
t.SkipNow()
tests, err := fullblocktests.Generate(false)
if err != nil {
t.Fatalf("failed to generate tests: %v", err)
}
// Create a new database and chain instance to run tests against.
dag, teardownFunc, err := DAGSetup("fullblocktest",
&dagconfig.RegressionNetParams)
if err != nil {
t.Errorf("Failed to setup chain instance: %v", err)
return
}
defer teardownFunc()
// testAcceptedBlock attempts to process the block in the provided test
// instance and ensures that it was accepted according to the flags
// specified in the test.
testAcceptedBlock := func(item fullblocktests.AcceptedBlock) {
blockHeight := item.Height
block := util.NewBlock(item.Block)
block.SetChainHeight(blockHeight)
t.Logf("Testing block %s (hash %s, height %d)",
item.Name, block.Hash(), blockHeight)
isOrphan, delay, err := dag.ProcessBlock(block,
blockdag.BFNone)
if err != nil {
t.Fatalf("block %q (hash %s, height %d) should "+
"have been accepted: %v", item.Name,
block.Hash(), blockHeight, err)
}
if delay != item.Delay {
t.Fatalf("block %q (hash %s, height %d) unexpected "+
"delay -- got %v, want %v", item.Name,
block.Hash(), blockHeight, delay,
item.Delay)
}
if isOrphan != item.IsOrphan {
t.Fatalf("block %q (hash %s, height %d) unexpected "+
"orphan flag -- got %v, want %v", item.Name,
block.Hash(), blockHeight, isOrphan,
item.IsOrphan)
}
}
// testRejectedBlock attempts to process the block in the provided test
// instance and ensures that it was rejected with the reject code
// specified in the test.
testRejectedBlock := func(item fullblocktests.RejectedBlock) {
blockHeight := item.Height
block := util.NewBlock(item.Block)
block.SetChainHeight(blockHeight)
t.Logf("Testing block %s (hash %s, height %d)",
item.Name, block.Hash(), blockHeight)
_, _, err := dag.ProcessBlock(block, blockdag.BFNone)
if err == nil {
t.Fatalf("block %q (hash %s, height %d) should not "+
"have been accepted", item.Name, block.Hash(),
blockHeight)
}
// Ensure the error code is of the expected type and the reject
// code matches the value specified in the test instance.
rerr, ok := err.(blockdag.RuleError)
if !ok {
t.Fatalf("block %q (hash %s, height %d) returned "+
"unexpected error type -- got %T, want "+
"blockchain.RuleError", item.Name, block.Hash(),
blockHeight, err)
}
if rerr.ErrorCode != item.RejectCode {
t.Fatalf("block %q (hash %s, height %d) does not have "+
"expected reject code -- got %v, want %v",
item.Name, block.Hash(), blockHeight,
rerr.ErrorCode, item.RejectCode)
}
}
// testRejectedNonCanonicalBlock attempts to decode the block in the
// provided test instance and ensures that it failed to decode with a
// message error.
testRejectedNonCanonicalBlock := func(item fullblocktests.RejectedNonCanonicalBlock) {
headerLen := len(item.RawBlock)
if headerLen > 80 {
headerLen = 80
}
blockHash := daghash.DoubleHashH(item.RawBlock[0:headerLen])
blockHeight := item.Height
t.Logf("Testing block %s (hash %s, height %d)", item.Name,
blockHash, blockHeight)
// Ensure there is an error due to deserializing the block.
var msgBlock wire.MsgBlock
err := msgBlock.BtcDecode(bytes.NewReader(item.RawBlock), 0)
if _, ok := err.(*wire.MessageError); !ok {
t.Fatalf("block %q (hash %s, height %d) should have "+
"failed to decode", item.Name, blockHash,
blockHeight)
}
}
// testOrphanOrRejectedBlock attempts to process the block in the
// provided test instance and ensures that it was either accepted as an
// orphan or rejected with a rule violation.
testOrphanOrRejectedBlock := func(item fullblocktests.OrphanOrRejectedBlock) {
blockHeight := item.Height
block := util.NewBlock(item.Block)
block.SetChainHeight(blockHeight)
t.Logf("Testing block %s (hash %s, height %d)",
item.Name, block.Hash(), blockHeight)
isOrphan, delay, err := dag.ProcessBlock(block, blockdag.BFNone)
if err != nil {
// Ensure the error code is of the expected type.
if _, ok := err.(blockdag.RuleError); !ok {
t.Fatalf("block %q (hash %s, height %d) "+
"returned unexpected error type -- "+
"got %T, want blockchain.RuleError",
item.Name, block.Hash(), blockHeight,
err)
}
}
if delay != 0 {
t.Fatalf("block %q (hash %s, height %d) "+
"is too far in the future",
item.Name, block.Hash(), blockHeight)
}
if !isOrphan {
t.Fatalf("block %q (hash %s, height %d) was accepted, "+
"but is not considered an orphan", item.Name,
block.Hash(), blockHeight)
}
}
// testExpectedTip ensures the current tip of the blockchain is the
// block specified in the provided test instance.
testExpectedTip := func(item fullblocktests.ExpectedTip) {
blockHeight := item.Height
block := util.NewBlock(item.Block)
block.SetChainHeight(blockHeight)
t.Logf("Testing tip for block %s (hash %s, height %d)",
item.Name, block.Hash(), blockHeight)
// Ensure hash and height match.
if dag.SelectedTipHash() != item.Block.BlockHash() ||
dag.ChainHeight() != blockHeight { //TODO: (Ori) the use of dag.ChainHeight() and virtualBlock.HighestTipHash() is wrong, and was done only for compilation
t.Fatalf("block %q (hash %s, height %d) should be "+
"the current tip -- got (hash %s, height %d)",
item.Name, block.Hash(), blockHeight, dag.SelectedTipHash(),
dag.ChainHeight()) //TODO: (Ori) the use of dag.ChainHeight() and virtualBlock.HighestTipHash() is wrong, and was done only for compilation
}
}
for testNum, test := range tests {
for itemNum, item := range test {
switch item := item.(type) {
case fullblocktests.AcceptedBlock:
testAcceptedBlock(item)
case fullblocktests.RejectedBlock:
testRejectedBlock(item)
case fullblocktests.RejectedNonCanonicalBlock:
testRejectedNonCanonicalBlock(item)
case fullblocktests.OrphanOrRejectedBlock:
testOrphanOrRejectedBlock(item)
case fullblocktests.ExpectedTip:
testExpectedTip(item)
default:
t.Fatalf("test #%d, item #%d is not one of "+
"the supported test instance types -- "+
"got type: %T", testNum, itemNum, item)
}
}
}
}

View File

@@ -0,0 +1,29 @@
fullblocktests
==============
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/kaspanet/kaspad/blockchain/fullblocktests)
Package fullblocktests provides a set of full block tests to be used for testing
the consensus validation rules. The tests are intended to be flexible enough to
allow both unit-style tests directly against the blockchain code as well as
integration style tests over the peer-to-peer network. To achieve that goal,
each test contains additional information about the expected result, however
that information can be ignored when doing comparison tests between two
independent versions over the peer-to-peer network.
This package has intentionally been designed so it can be used as a standalone
package for any projects needing to test their implementation against a full set
of blocks that exercise the consensus validation rules.
## Installation and Updating
```bash
$ go get -u github.com/kaspanet/kaspad/blockchain/fullblocktests
```
## License
Package fullblocktests is licensed under the [copyfree](http://copyfree.org) ISC
License.

View File

@@ -0,0 +1,20 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
/*
Package fullblocktests provides a set of block consensus validation tests.
All of the generated test instances involve full blocks that are to be used for
testing the consensus validation rules. The tests are intended to be flexible
enough to allow both unit-style tests directly against the blockchain code as
well as integration style tests over the peer-to-peer network. To achieve that
goal, each test contains additional information about the expected result,
however that information can be ignored when doing comparison tests between two
independent versions over the peer-to-peer network.
This package has intentionally been designed so it can be used as a standalone
package for any projects needing to test their implementation against a full set
of blocks that exercise the consensus validation rules.
*/
package fullblocktests

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,143 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package fullblocktests
import (
"encoding/hex"
"math"
"math/big"
"time"
"github.com/kaspanet/kaspad/util/hdkeychain"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
)
// newHashFromStr converts the passed big-endian hex string into a
// wire.Hash. It only differs from the one available in daghash in that
// it panics on an error since it will only (and must only) be called with
// hard-coded, and therefore known good, hashes.
func newHashFromStr(hexStr string) *daghash.Hash {
hash, err := daghash.NewHashFromStr(hexStr)
if err != nil {
panic(err)
}
return hash
}
// newTxIDFromStr converts the passed big-endian hex string into a
// wire.TxID. It only differs from the one available in daghash in that
// it panics on an error since it will only (and must only) be called with
// hard-coded, and therefore known good, hashes.
func newTxIDFromStr(hexStr string) *daghash.TxID {
txID, err := daghash.NewTxIDFromStr(hexStr)
if err != nil {
panic(err)
}
return txID
}
// fromHex converts the passed hex string into a byte slice and will panic if
// there is an error. This is only provided for the hard-coded constants so
// errors in the source code can be detected. It will only (and must only) be
// called for initialization purposes.
func fromHex(s string) []byte {
r, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return r
}
var (
// bigOne is 1 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigOne = big.NewInt(1)
// regressionPowLimit is the highest proof of work value a Bitcoin block
// can have for the regression test network. It is the value 2^255 - 1.
regressionPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne)
// regTestGenesisBlock defines the genesis block of the block chain which serves
// as the public transaction ledger for the regression test network.
regTestGenesisBlock = wire.MsgBlock{
Header: wire.BlockHeader{
Version: 1,
ParentHashes: []*daghash.Hash{},
HashMerkleRoot: newHashFromStr("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"),
Timestamp: time.Unix(0x5b28c636, 0), // 2018-06-19 09:00:38 +0000 UTC
Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000]
Nonce: 1,
},
Transactions: []*wire.MsgTx{{
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutpoint: wire.Outpoint{
TxID: daghash.TxID{},
Index: 0xffffffff,
},
SignatureScript: fromHex("04ffff001d010445" +
"5468652054696d65732030332f4a616e2f" +
"32303039204368616e63656c6c6f72206f" +
"6e206272696e6b206f66207365636f6e64" +
"206261696c6f757420666f72206261686b73"),
Sequence: math.MaxUint64,
}},
TxOut: []*wire.TxOut{{
Value: 0,
ScriptPubKey: fromHex("4104678afdb0fe5548271967f1" +
"a67130b7105cd6a828e03909a67962e0ea1f" +
"61deb649f6bc3f4cef38c4f35504e51ec138" +
"c4f35504e51ec112de5c384df7ba0b8d578a" +
"4c702b6bf11d5fac"),
}},
LockTime: 0,
SubnetworkID: *subnetworkid.SubnetworkIDNative,
}},
}
)
// regressionNetParams defines the network parameters for the regression test
// network.
//
// NOTE: The test generator intentionally does not use the existing definitions
// in the dagconfig package since the intent is to be able to generate known
// good tests which exercise that code. Using the dagconfig parameters would
// allow them to change out from under the tests potentially invalidating them.
var regressionNetParams = &dagconfig.Params{
Name: "regtest",
Net: wire.RegTest,
DefaultPort: "18444",
// DAG parameters
GenesisBlock: &regTestGenesisBlock,
GenesisHash: newHashFromStr("5bec7567af40504e0994db3b573c186fffcc4edefe096ff2e58d00523bd7e8a6"),
PowMax: regressionPowLimit,
BlockCoinbaseMaturity: 100,
SubsidyReductionInterval: 150,
TargetTimePerBlock: time.Second * 10, // 10 seconds
DifficultyAdjustmentWindowSize: 2640,
TimestampDeviationTolerance: 132,
GenerateSupported: true,
// Checkpoints ordered from oldest to newest.
Checkpoints: nil,
// Mempool parameters
RelayNonStdTxs: true,
// Address encoding magics
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)
// BIP32 hierarchical deterministic extended key magics
HDKeyIDPair: hdkeychain.HDKeyPairRegressionNet,
// BIP44 coin type used in the hierarchical deterministic path for
// address generation.
HDCoinType: 1,
}

View File

@@ -0,0 +1,32 @@
indexers
========
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)](https://travis-ci.org/btcsuite/btcd)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://godoc.org/github.com/kaspanet/kaspad/blockchain/indexers?status.png)](http://godoc.org/github.com/kaspanet/kaspad/blockchain/indexers)
Package indexers implements optional block chain indexes.
These indexes are typically used to enhance the amount of information available
via an RPC interface.
## Supported Indexers
- Transaction-by-hash (txbyhashidx) Index
- Creates a mapping from the hash of each transaction to the block that
contains it along with its offset and length within the serialized block
- Transaction-by-address (txbyaddridx) Index
- Creates a mapping from every address to all transactions which either credit
or debit the address
- Requires the transaction-by-hash index
## Installation
```bash
$ go get -u github.com/kaspanet/kaspad/blockchain/indexers
```
## License
Package indexers is licensed under the [copyfree](http://copyfree.org) ISC
License.

View File

@@ -0,0 +1,234 @@
package indexers
import (
"bytes"
"encoding/gob"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
"github.com/pkg/errors"
)
const (
// acceptanceIndexName is the human-readable name for the index.
acceptanceIndexName = "acceptance index"
)
var (
// acceptanceIndexKey is the key of the acceptance index and the db bucket used
// to house it.
acceptanceIndexKey = []byte("acceptanceidx")
)
// AcceptanceIndex implements a txAcceptanceData by block hash index. That is to say,
// it stores a mapping between a block's hash and the set of transactions that the
// block accepts among its blue blocks.
type AcceptanceIndex struct {
db database.DB
dag *blockdag.BlockDAG
}
// Ensure the AcceptanceIndex type implements the Indexer interface.
var _ Indexer = (*AcceptanceIndex)(nil)
// NewAcceptanceIndex returns a new instance of an indexer that is used to create a
// mapping between block hashes and their txAcceptanceData.
//
// It implements the Indexer interface which plugs into the IndexManager that in
// turn is used by the blockdag package. This allows the index to be
// seamlessly maintained along with the DAG.
func NewAcceptanceIndex() *AcceptanceIndex {
return &AcceptanceIndex{}
}
// DropAcceptanceIndex drops the acceptance index from the provided database if it
// exists.
func DropAcceptanceIndex(db database.DB, interrupt <-chan struct{}) error {
return dropIndex(db, acceptanceIndexKey, acceptanceIndexName, interrupt)
}
// Key returns the database key to use for the index as a byte slice.
//
// This is part of the Indexer interface.
func (idx *AcceptanceIndex) Key() []byte {
return acceptanceIndexKey
}
// Name returns the human-readable name of the index.
//
// This is part of the Indexer interface.
func (idx *AcceptanceIndex) Name() string {
return acceptanceIndexName
}
// Create is invoked when the indexer manager determines the index needs
// to be created for the first time. It creates the bucket for the
// acceptance index.
//
// This is part of the Indexer interface.
func (idx *AcceptanceIndex) Create(dbTx database.Tx) error {
_, err := dbTx.Metadata().CreateBucket(acceptanceIndexKey)
return err
}
// Init initializes the hash-based acceptance index.
//
// This is part of the Indexer interface.
func (idx *AcceptanceIndex) Init(db database.DB, dag *blockdag.BlockDAG) error {
idx.db = db
idx.dag = dag
return nil
}
// ConnectBlock is invoked by the index manager when a new block has been
// connected to the DAG.
//
// This is part of the Indexer interface.
func (idx *AcceptanceIndex) ConnectBlock(dbTx database.Tx, _ *util.Block, blockID uint64, _ *blockdag.BlockDAG,
txsAcceptanceData blockdag.MultiBlockTxsAcceptanceData, _ blockdag.MultiBlockTxsAcceptanceData) error {
return dbPutTxsAcceptanceData(dbTx, blockID, txsAcceptanceData)
}
// TxsAcceptanceData returns the acceptance data of all the transactions that
// were accepted by the block with hash blockHash.
func (idx *AcceptanceIndex) TxsAcceptanceData(blockHash *daghash.Hash) (blockdag.MultiBlockTxsAcceptanceData, error) {
var txsAcceptanceData blockdag.MultiBlockTxsAcceptanceData
err := idx.db.View(func(dbTx database.Tx) error {
var err error
txsAcceptanceData, err = dbFetchTxsAcceptanceDataByHash(dbTx, blockHash)
return err
})
if err != nil {
return nil, err
}
return txsAcceptanceData, nil
}
// Recover is invoked when the indexer wasn't turned on for several blocks
// and the indexer needs to close the gaps.
//
// This is part of the Indexer interface.
func (idx *AcceptanceIndex) Recover(dbTx database.Tx, currentBlockID, lastKnownBlockID uint64) error {
for blockID := currentBlockID + 1; blockID <= lastKnownBlockID; blockID++ {
hash, err := blockdag.DBFetchBlockHashByID(dbTx, currentBlockID)
if err != nil {
return err
}
txAcceptanceData, err := idx.dag.TxsAcceptedByBlockHash(hash)
if err != nil {
return err
}
err = idx.ConnectBlock(dbTx, nil, blockID, nil, txAcceptanceData, nil)
if err != nil {
return err
}
}
return nil
}
func dbPutTxsAcceptanceData(dbTx database.Tx, blockID uint64,
txsAcceptanceData blockdag.MultiBlockTxsAcceptanceData) error {
serializedTxsAcceptanceData, err := serializeMultiBlockTxsAcceptanceData(txsAcceptanceData)
if err != nil {
return err
}
bucket := dbTx.Metadata().Bucket(acceptanceIndexKey)
return bucket.Put(blockdag.SerializeBlockID(blockID), serializedTxsAcceptanceData)
}
func dbFetchTxsAcceptanceDataByHash(dbTx database.Tx,
hash *daghash.Hash) (blockdag.MultiBlockTxsAcceptanceData, error) {
blockID, err := blockdag.DBFetchBlockIDByHash(dbTx, hash)
if err != nil {
return nil, err
}
return dbFetchTxsAcceptanceDataByID(dbTx, blockID)
}
func dbFetchTxsAcceptanceDataByID(dbTx database.Tx,
blockID uint64) (blockdag.MultiBlockTxsAcceptanceData, error) {
serializedBlockID := blockdag.SerializeBlockID(blockID)
bucket := dbTx.Metadata().Bucket(acceptanceIndexKey)
serializedTxsAcceptanceData := bucket.Get(serializedBlockID)
if serializedTxsAcceptanceData == nil {
return nil, errors.Errorf("no entry in the accpetance index for block id %d", blockID)
}
return deserializeMultiBlockTxsAcceptanceData(serializedTxsAcceptanceData)
}
type serializableTxAcceptanceData struct {
MsgTx wire.MsgTx
IsAccepted bool
}
type serializableBlockTxsAcceptanceData struct {
BlockHash daghash.Hash
TxAcceptanceData []serializableTxAcceptanceData
}
type serializableMultiBlockTxsAcceptanceData []serializableBlockTxsAcceptanceData
func serializeMultiBlockTxsAcceptanceData(
multiBlockTxsAcceptanceData blockdag.MultiBlockTxsAcceptanceData) ([]byte, error) {
// Convert MultiBlockTxsAcceptanceData to a serializable format
serializableData := make(serializableMultiBlockTxsAcceptanceData, len(multiBlockTxsAcceptanceData))
for i, blockTxsAcceptanceData := range multiBlockTxsAcceptanceData {
serializableBlockData := serializableBlockTxsAcceptanceData{
BlockHash: blockTxsAcceptanceData.BlockHash,
TxAcceptanceData: make([]serializableTxAcceptanceData, len(blockTxsAcceptanceData.TxAcceptanceData)),
}
for i, txAcceptanceData := range blockTxsAcceptanceData.TxAcceptanceData {
serializableBlockData.TxAcceptanceData[i] = serializableTxAcceptanceData{
MsgTx: *txAcceptanceData.Tx.MsgTx(),
IsAccepted: txAcceptanceData.IsAccepted,
}
}
serializableData[i] = serializableBlockData
}
// Serialize
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(serializableData)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func deserializeMultiBlockTxsAcceptanceData(
serializedTxsAcceptanceData []byte) (blockdag.MultiBlockTxsAcceptanceData, error) {
// Deserialize
buffer := bytes.NewBuffer(serializedTxsAcceptanceData)
decoder := gob.NewDecoder(buffer)
var serializedData serializableMultiBlockTxsAcceptanceData
err := decoder.Decode(&serializedData)
if err != nil {
return nil, err
}
// Convert serializable format to MultiBlockTxsAcceptanceData
multiBlockTxsAcceptanceData := make(blockdag.MultiBlockTxsAcceptanceData, len(serializedData))
for i, serializableBlockData := range serializedData {
blockTxsAcceptanceData := blockdag.BlockTxsAcceptanceData{
BlockHash: serializableBlockData.BlockHash,
TxAcceptanceData: make([]blockdag.TxAcceptanceData, len(serializableBlockData.TxAcceptanceData)),
}
for i, txData := range serializableBlockData.TxAcceptanceData {
msgTx := txData.MsgTx
blockTxsAcceptanceData.TxAcceptanceData[i] = blockdag.TxAcceptanceData{
Tx: util.NewTx(&msgTx),
IsAccepted: txData.IsAccepted,
}
}
multiBlockTxsAcceptanceData[i] = blockTxsAcceptanceData
}
return multiBlockTxsAcceptanceData, nil
}

View File

@@ -0,0 +1,340 @@
package indexers
import (
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
"github.com/pkg/errors"
"io"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"syscall"
"testing"
)
func TestAcceptanceIndexSerializationAndDeserialization(t *testing.T) {
// Create test data
hash, _ := daghash.NewHashFromStr("1111111111111111111111111111111111111111111111111111111111111111")
txIn1 := &wire.TxIn{SignatureScript: []byte{1}, PreviousOutpoint: wire.Outpoint{Index: 1}, Sequence: 0}
txIn2 := &wire.TxIn{SignatureScript: []byte{2}, PreviousOutpoint: wire.Outpoint{Index: 2}, Sequence: 0}
txOut1 := &wire.TxOut{ScriptPubKey: []byte{1}, Value: 10}
txOut2 := &wire.TxOut{ScriptPubKey: []byte{2}, Value: 20}
blockTxsAcceptanceData := blockdag.BlockTxsAcceptanceData{
BlockHash: *hash,
TxAcceptanceData: []blockdag.TxAcceptanceData{
{
Tx: util.NewTx(wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn1}, []*wire.TxOut{txOut1})),
IsAccepted: true,
},
{
Tx: util.NewTx(wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn2}, []*wire.TxOut{txOut2})),
IsAccepted: false,
},
},
}
multiBlockTxsAcceptanceData := blockdag.MultiBlockTxsAcceptanceData{blockTxsAcceptanceData}
// Serialize
serializedTxsAcceptanceData, err := serializeMultiBlockTxsAcceptanceData(multiBlockTxsAcceptanceData)
if err != nil {
t.Fatalf("TestAcceptanceIndexSerializationAndDeserialization: serialization failed: %s", err)
}
// Deserialize
deserializedTxsAcceptanceData, err := deserializeMultiBlockTxsAcceptanceData(serializedTxsAcceptanceData)
if err != nil {
t.Fatalf("TestAcceptanceIndexSerializationAndDeserialization: deserialization failed: %s", err)
}
// Check that they're the same
if !reflect.DeepEqual(multiBlockTxsAcceptanceData, deserializedTxsAcceptanceData) {
t.Fatalf("TestAcceptanceIndexSerializationAndDeserialization: original data and deseralize data aren't equal")
}
}
// TestAcceptanceIndexRecover tests the recoverability of the
// acceptance index.
// It does it by following these steps:
// * It creates a DAG with enabled acceptance index (let's call it dag1) and
// make it process some blocks.
// * It creates a copy of dag1 (let's call it dag2), and disables the acceptance
// index in it.
// * It processes two more blocks in both dag1 and dag2.
// * A copy of dag2 is created (let's call it dag3) with enabled
// acceptance index
// * It checks that the two missing blocks are added to dag3 acceptance index by
// comparing dag1's last block acceptance data and dag3's last block acceptance
// data.
func TestAcceptanceIndexRecover(t *testing.T) {
params := &dagconfig.SimNetParams
params.BlockCoinbaseMaturity = 0
testFiles := []string{
"blk_0_to_4.dat",
"blk_3B.dat",
}
var blocks []*util.Block
for _, file := range testFiles {
blockTmp, err := blockdag.LoadBlocks(filepath.Join("../testdata/", file))
if err != nil {
t.Fatalf("Error loading file: %v\n", err)
}
blocks = append(blocks, blockTmp...)
}
db1AcceptanceIndex := NewAcceptanceIndex()
db1IndexManager := NewManager([]Indexer{db1AcceptanceIndex})
db1Path, err := ioutil.TempDir("", "TestAcceptanceIndexRecover1")
if err != nil {
t.Fatalf("Error creating temporary directory: %s", err)
}
defer os.RemoveAll(db1Path)
db1, err := database.Create("ffldb", db1Path, params.Net)
if err != nil {
t.Fatalf("error creating db: %s", err)
}
db1Config := blockdag.Config{
IndexManager: db1IndexManager,
DAGParams: params,
DB: db1,
}
db1DAG, teardown, err := blockdag.DAGSetup("", db1Config)
if err != nil {
t.Fatalf("TestAcceptanceIndexRecover: Failed to setup DAG instance: %v", err)
}
if teardown != nil {
defer teardown()
}
for i := 1; i < len(blocks)-2; i++ {
isOrphan, delay, err := db1DAG.ProcessBlock(blocks[i], blockdag.BFNone)
if err != nil {
t.Fatalf("ProcessBlock fail on block %v: %v\n", i, err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: block %d "+
"is too far in the future", i)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block %v "+
"is an orphan\n", i)
}
}
err = db1.FlushCache()
if err != nil {
t.Fatalf("Error flushing database to disk: %s", err)
}
db2Path, err := ioutil.TempDir("", "TestAcceptanceIndexRecover2")
if err != nil {
t.Fatalf("Error creating temporary directory: %s", err)
}
defer os.RemoveAll(db2Path)
err = copyDirectory(db1Path, db2Path)
if err != nil {
t.Fatalf("copyDirectory: %s", err)
}
for i := len(blocks) - 2; i < len(blocks); i++ {
isOrphan, delay, err := db1DAG.ProcessBlock(blocks[i], blockdag.BFNone)
if err != nil {
t.Fatalf("ProcessBlock fail on block %v: %v\n", i, err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: block %d "+
"is too far in the future", i)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block %v "+
"is an orphan\n", i)
}
}
db1LastBlockAcceptanceData, err := db1AcceptanceIndex.TxsAcceptanceData(blocks[len(blocks)-1].Hash())
if err != nil {
t.Fatalf("Error fetching acceptance data: %s", err)
}
db2, err := database.Open("ffldb", db2Path, params.Net)
if err != nil {
t.Fatalf("Error opening database: %s", err)
}
db2Config := blockdag.Config{
DAGParams: params,
DB: db2,
}
db2DAG, teardown, err := blockdag.DAGSetup("", db2Config)
if err != nil {
t.Fatalf("TestAcceptanceIndexRecover: Failed to setup DAG instance: %v", err)
}
if teardown != nil {
defer teardown()
}
for i := len(blocks) - 2; i < len(blocks); i++ {
isOrphan, delay, err := db2DAG.ProcessBlock(blocks[i], blockdag.BFNone)
if err != nil {
t.Fatalf("ProcessBlock fail on block %v: %v\n", i, err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: block %d "+
"is too far in the future", i)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block %v "+
"is an orphan\n", i)
}
}
err = db2.FlushCache()
if err != nil {
t.Fatalf("Error flushing database to disk: %s", err)
}
db3Path, err := ioutil.TempDir("", "TestAcceptanceIndexRecover3")
if err != nil {
t.Fatalf("Error creating temporary directory: %s", err)
}
defer os.RemoveAll(db3Path)
err = copyDirectory(db2Path, db3Path)
if err != nil {
t.Fatalf("copyDirectory: %s", err)
}
db3, err := database.Open("ffldb", db3Path, params.Net)
if err != nil {
t.Fatalf("Error opening database: %s", err)
}
db3AcceptanceIndex := NewAcceptanceIndex()
db3IndexManager := NewManager([]Indexer{db3AcceptanceIndex})
db3Config := blockdag.Config{
IndexManager: db3IndexManager,
DAGParams: params,
DB: db3,
}
_, teardown, err = blockdag.DAGSetup("", db3Config)
if err != nil {
t.Fatalf("TestAcceptanceIndexRecover: Failed to setup DAG instance: %v", err)
}
if teardown != nil {
defer teardown()
}
db3LastBlockAcceptanceData, err := db3AcceptanceIndex.TxsAcceptanceData(blocks[len(blocks)-1].Hash())
if err != nil {
t.Fatalf("Error fetching acceptance data: %s", err)
}
if !reflect.DeepEqual(db1LastBlockAcceptanceData, db3LastBlockAcceptanceData) {
t.Fatalf("recovery failed")
}
}
// This function is copied and modified from this stackoverflow answer: https://stackoverflow.com/a/56314145/2413761
func copyDirectory(scrDir, dest string) error {
entries, err := ioutil.ReadDir(scrDir)
if err != nil {
return err
}
for _, entry := range entries {
sourcePath := filepath.Join(scrDir, entry.Name())
destPath := filepath.Join(dest, entry.Name())
fileInfo, err := os.Stat(sourcePath)
if err != nil {
return err
}
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
if !ok {
return errors.Errorf("failed to get raw syscall.Stat_t data for '%s'", sourcePath)
}
switch fileInfo.Mode() & os.ModeType {
case os.ModeDir:
if err := createIfNotExists(destPath, 0755); err != nil {
return err
}
if err := copyDirectory(sourcePath, destPath); err != nil {
return err
}
case os.ModeSymlink:
if err := copySymLink(sourcePath, destPath); err != nil {
return err
}
default:
if err := copyFile(sourcePath, destPath); err != nil {
return err
}
}
if err := os.Lchown(destPath, int(stat.Uid), int(stat.Gid)); err != nil {
return err
}
isSymlink := entry.Mode()&os.ModeSymlink != 0
if !isSymlink {
if err := os.Chmod(destPath, entry.Mode()); err != nil {
return err
}
}
}
return nil
}
// This function is copied and modified from this stackoverflow answer: https://stackoverflow.com/a/56314145/2413761
func copyFile(srcFile, dstFile string) error {
out, err := os.Create(dstFile)
defer out.Close()
if err != nil {
return err
}
in, err := os.Open(srcFile)
defer in.Close()
if err != nil {
return err
}
_, err = io.Copy(out, in)
if err != nil {
return err
}
return nil
}
// This function is copied and modified from this stackoverflow answer: https://stackoverflow.com/a/56314145/2413761
func createIfNotExists(dir string, perm os.FileMode) error {
if blockdag.FileExists(dir) {
return nil
}
if err := os.MkdirAll(dir, perm); err != nil {
return errors.Errorf("failed to create directory: '%s', error: '%s'", dir, err.Error())
}
return nil
}
// This function is copied and modified from this stackoverflow answer: https://stackoverflow.com/a/56314145/2413761
func copySymLink(source, dest string) error {
link, err := os.Readlink(source)
if err != nil {
return err
}
return os.Symlink(link, dest)
}

View File

@@ -0,0 +1,924 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package indexers
import (
"fmt"
"github.com/pkg/errors"
"sync"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
)
const (
// addrIndexName is the human-readable name for the index.
addrIndexName = "address index"
// level0MaxEntries is the maximum number of transactions that are
// stored in level 0 of an address index entry. Subsequent levels store
// 2^n * level0MaxEntries entries, or in words, double the maximum of
// the previous level.
level0MaxEntries = 8
// addrKeySize is the number of bytes an address key consumes in the
// index. It consists of 1 byte address type + 20 bytes hash160.
addrKeySize = 1 + 20
// levelKeySize is the number of bytes a level key in the address index
// consumes. It consists of the address key + 1 byte for the level.
levelKeySize = addrKeySize + 1
// levelOffset is the offset in the level key which identifes the level.
levelOffset = levelKeySize - 1
// addrKeyTypePubKeyHash is the address type in an address key which
// represents both a pay-to-pubkey-hash and a pay-to-pubkey address.
// This is done because both are identical for the purposes of the
// address index.
addrKeyTypePubKeyHash = 0
// addrKeyTypeScriptHash is the address type in an address key which
// represents a pay-to-script-hash address. This is necessary because
// the hash of a pubkey address might be the same as that of a script
// hash.
addrKeyTypeScriptHash = 1
// Size of a transaction entry. It consists of 8 bytes block id + 4
// bytes offset + 4 bytes length.
txEntrySize = 8 + 4 + 4
)
var (
// addrIndexKey is the key of the address index and the db bucket used
// to house it.
addrIndexKey = []byte("txbyaddridx")
// errUnsupportedAddressType is an error that is used to signal an
// unsupported address type has been used.
errUnsupportedAddressType = errors.New("address type is not supported " +
"by the address index")
)
// -----------------------------------------------------------------------------
// The address index maps addresses referenced in the blockchain to a list of
// all the transactions involving that address. Transactions are stored
// according to their order of appearance in the blockchain. That is to say
// first by block height and then by offset inside the block. It is also
// important to note that this implementation requires the transaction index
// since it is needed in order to catch up old blocks due to the fact the spent
// outputs will already be pruned from the utxo set.
//
// The approach used to store the index is similar to a log-structured merge
// tree (LSM tree) and is thus similar to how leveldb works internally.
//
// Every address consists of one or more entries identified by a level starting
// from 0 where each level holds a maximum number of entries such that each
// subsequent level holds double the maximum of the previous one. In equation
// form, the number of entries each level holds is 2^n * firstLevelMaxSize.
//
// New transactions are appended to level 0 until it becomes full at which point
// the entire level 0 entry is appended to the level 1 entry and level 0 is
// cleared. This process continues until level 1 becomes full at which point it
// will be appended to level 2 and cleared and so on.
//
// The result of this is the lower levels contain newer transactions and the
// transactions within each level are ordered from oldest to newest.
//
// The intent of this approach is to provide a balance between space efficiency
// and indexing cost. Storing one entry per transaction would have the lowest
// indexing cost, but would waste a lot of space because the same address hash
// would be duplicated for every transaction key. On the other hand, storing a
// single entry with all transactions would be the most space efficient, but
// would cause indexing cost to grow quadratically with the number of
// transactions involving the same address. The approach used here provides
// logarithmic insertion and retrieval.
//
// The serialized key format is:
//
// <addr type><addr hash><level>
//
// Field Type Size
// addr type uint8 1 byte
// addr hash hash160 20 bytes
// level uint8 1 byte
// -----
// Total: 22 bytes
//
// The serialized value format is:
//
// [<block id><start offset><tx length>,...]
//
// Field Type Size
// block id uint64 8 bytes
// start offset uint32 4 bytes
// tx length uint32 4 bytes
// -----
// Total: 16 bytes per indexed tx
// -----------------------------------------------------------------------------
// fetchBlockHashFunc defines a callback function to use in order to convert a
// serialized block ID to an associated block hash.
type fetchBlockHashFunc func(serializedID []byte) (*daghash.Hash, error)
// serializeAddrIndexEntry serializes the provided block id and transaction
// location according to the format described in detail above.
func serializeAddrIndexEntry(blockID uint64, txLoc wire.TxLoc) []byte {
// Serialize the entry.
serialized := make([]byte, 16)
byteOrder.PutUint64(serialized, blockID)
byteOrder.PutUint32(serialized[8:], uint32(txLoc.TxStart))
byteOrder.PutUint32(serialized[12:], uint32(txLoc.TxLen))
return serialized
}
// deserializeAddrIndexEntry decodes the passed serialized byte slice into the
// provided region struct according to the format described in detail above and
// uses the passed block hash fetching function in order to conver the block ID
// to the associated block hash.
func deserializeAddrIndexEntry(serialized []byte, region *database.BlockRegion, fetchBlockHash fetchBlockHashFunc) error {
// Ensure there are enough bytes to decode.
if len(serialized) < txEntrySize {
return errDeserialize("unexpected end of data")
}
hash, err := fetchBlockHash(serialized[0:8])
if err != nil {
return err
}
region.Hash = hash
region.Offset = byteOrder.Uint32(serialized[8:12])
region.Len = byteOrder.Uint32(serialized[12:16])
return nil
}
// keyForLevel returns the key for a specific address and level in the address
// index entry.
func keyForLevel(addrKey [addrKeySize]byte, level uint8) [levelKeySize]byte {
var key [levelKeySize]byte
copy(key[:], addrKey[:])
key[levelOffset] = level
return key
}
// dbPutAddrIndexEntry updates the address index to include the provided entry
// according to the level-based scheme described in detail above.
func dbPutAddrIndexEntry(bucket internalBucket, addrKey [addrKeySize]byte, blockID uint64, txLoc wire.TxLoc) error {
// Start with level 0 and its initial max number of entries.
curLevel := uint8(0)
maxLevelBytes := level0MaxEntries * txEntrySize
// Simply append the new entry to level 0 and return now when it will
// fit. This is the most common path.
newData := serializeAddrIndexEntry(blockID, txLoc)
level0Key := keyForLevel(addrKey, 0)
level0Data := bucket.Get(level0Key[:])
if len(level0Data)+len(newData) <= maxLevelBytes {
mergedData := newData
if len(level0Data) > 0 {
mergedData = make([]byte, len(level0Data)+len(newData))
copy(mergedData, level0Data)
copy(mergedData[len(level0Data):], newData)
}
return bucket.Put(level0Key[:], mergedData)
}
// At this point, level 0 is full, so merge each level into higher
// levels as many times as needed to free up level 0.
prevLevelData := level0Data
for {
// Each new level holds twice as much as the previous one.
curLevel++
maxLevelBytes *= 2
// Move to the next level as long as the current level is full.
curLevelKey := keyForLevel(addrKey, curLevel)
curLevelData := bucket.Get(curLevelKey[:])
if len(curLevelData) == maxLevelBytes {
prevLevelData = curLevelData
continue
}
// The current level has room for the data in the previous one,
// so merge the data from previous level into it.
mergedData := prevLevelData
if len(curLevelData) > 0 {
mergedData = make([]byte, len(curLevelData)+
len(prevLevelData))
copy(mergedData, curLevelData)
copy(mergedData[len(curLevelData):], prevLevelData)
}
err := bucket.Put(curLevelKey[:], mergedData)
if err != nil {
return err
}
// Move all of the levels before the previous one up a level.
for mergeLevel := curLevel - 1; mergeLevel > 0; mergeLevel-- {
mergeLevelKey := keyForLevel(addrKey, mergeLevel)
prevLevelKey := keyForLevel(addrKey, mergeLevel-1)
prevData := bucket.Get(prevLevelKey[:])
err := bucket.Put(mergeLevelKey[:], prevData)
if err != nil {
return err
}
}
break
}
// Finally, insert the new entry into level 0 now that it is empty.
return bucket.Put(level0Key[:], newData)
}
// dbFetchAddrIndexEntries returns block regions for transactions referenced by
// the given address key and the number of entries skipped since it could have
// been less in the case where there are less total entries than the requested
// number of entries to skip.
func dbFetchAddrIndexEntries(bucket internalBucket, addrKey [addrKeySize]byte, numToSkip, numRequested uint32, reverse bool, fetchBlockHash fetchBlockHashFunc) ([]database.BlockRegion, uint32, error) {
// When the reverse flag is not set, all levels need to be fetched
// because numToSkip and numRequested are counted from the oldest
// transactions (highest level) and thus the total count is needed.
// However, when the reverse flag is set, only enough records to satisfy
// the requested amount are needed.
var level uint8
var serialized []byte
for !reverse || len(serialized) < int(numToSkip+numRequested)*txEntrySize {
curLevelKey := keyForLevel(addrKey, level)
levelData := bucket.Get(curLevelKey[:])
if levelData == nil {
// Stop when there are no more levels.
break
}
// Higher levels contain older transactions, so prepend them.
prepended := make([]byte, len(serialized)+len(levelData))
copy(prepended, levelData)
copy(prepended[len(levelData):], serialized)
serialized = prepended
level++
}
// When the requested number of entries to skip is larger than the
// number available, skip them all and return now with the actual number
// skipped.
numEntries := uint32(len(serialized) / txEntrySize)
if numToSkip >= numEntries {
return nil, numEntries, nil
}
// Nothing more to do when there are no requested entries.
if numRequested == 0 {
return nil, numToSkip, nil
}
// Limit the number to load based on the number of available entries,
// the number to skip, and the number requested.
numToLoad := numEntries - numToSkip
if numToLoad > numRequested {
numToLoad = numRequested
}
// Start the offset after all skipped entries and load the calculated
// number.
results := make([]database.BlockRegion, numToLoad)
for i := uint32(0); i < numToLoad; i++ {
// Calculate the read offset according to the reverse flag.
var offset uint32
if reverse {
offset = (numEntries - numToSkip - i - 1) * txEntrySize
} else {
offset = (numToSkip + i) * txEntrySize
}
// Deserialize and populate the result.
err := deserializeAddrIndexEntry(serialized[offset:],
&results[i], fetchBlockHash)
if err != nil {
// Ensure any deserialization errors are returned as
// database corruption errors.
if isDeserializeErr(err) {
err = database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("failed to "+
"deserialized address index "+
"for key %x: %s", addrKey, err),
}
}
return nil, 0, err
}
}
return results, numToSkip, nil
}
// minEntriesToReachLevel returns the minimum number of entries that are
// required to reach the given address index level.
func minEntriesToReachLevel(level uint8) int {
maxEntriesForLevel := level0MaxEntries
minRequired := 1
for l := uint8(1); l <= level; l++ {
minRequired += maxEntriesForLevel
maxEntriesForLevel *= 2
}
return minRequired
}
// maxEntriesForLevel returns the maximum number of entries allowed for the
// given address index level.
func maxEntriesForLevel(level uint8) int {
numEntries := level0MaxEntries
for l := level; l > 0; l-- {
numEntries *= 2
}
return numEntries
}
// dbRemoveAddrIndexEntries removes the specified number of entries from from
// the address index for the provided key. An assertion error will be returned
// if the count exceeds the total number of entries in the index.
func dbRemoveAddrIndexEntries(bucket internalBucket, addrKey [addrKeySize]byte, count int) error {
// Nothing to do if no entries are being deleted.
if count <= 0 {
return nil
}
// Make use of a local map to track pending updates and define a closure
// to apply it to the database. This is done in order to reduce the
// number of database reads and because there is more than one exit
// path that needs to apply the updates.
pendingUpdates := make(map[uint8][]byte)
applyPending := func() error {
for level, data := range pendingUpdates {
curLevelKey := keyForLevel(addrKey, level)
if len(data) == 0 {
err := bucket.Delete(curLevelKey[:])
if err != nil {
return err
}
continue
}
err := bucket.Put(curLevelKey[:], data)
if err != nil {
return err
}
}
return nil
}
// Loop forwards through the levels while removing entries until the
// specified number has been removed. This will potentially result in
// entirely empty lower levels which will be backfilled below.
var highestLoadedLevel uint8
numRemaining := count
for level := uint8(0); numRemaining > 0; level++ {
// Load the data for the level from the database.
curLevelKey := keyForLevel(addrKey, level)
curLevelData := bucket.Get(curLevelKey[:])
if len(curLevelData) == 0 && numRemaining > 0 {
return AssertError(fmt.Sprintf("dbRemoveAddrIndexEntries "+
"not enough entries for address key %x to "+
"delete %d entries", addrKey, count))
}
pendingUpdates[level] = curLevelData
highestLoadedLevel = level
// Delete the entire level as needed.
numEntries := len(curLevelData) / txEntrySize
if numRemaining >= numEntries {
pendingUpdates[level] = nil
numRemaining -= numEntries
continue
}
// Remove remaining entries to delete from the level.
offsetEnd := len(curLevelData) - (numRemaining * txEntrySize)
pendingUpdates[level] = curLevelData[:offsetEnd]
break
}
// When all elements in level 0 were not removed there is nothing left
// to do other than updating the database.
if len(pendingUpdates[0]) != 0 {
return applyPending()
}
// At this point there are one or more empty levels before the current
// level which need to be backfilled and the current level might have
// had some entries deleted from it as well. Since all levels after
// level 0 are required to either be empty, half full, or completely
// full, the current level must be adjusted accordingly by backfilling
// each previous levels in a way which satisfies the requirements. Any
// entries that are left are assigned to level 0 after the loop as they
// are guaranteed to fit by the logic in the loop. In other words, this
// effectively squashes all remaining entries in the current level into
// the lowest possible levels while following the level rules.
//
// Note that the level after the current level might also have entries
// and gaps are not allowed, so this also keeps track of the lowest
// empty level so the code below knows how far to backfill in case it is
// required.
lowestEmptyLevel := uint8(255)
curLevelData := pendingUpdates[highestLoadedLevel]
curLevelMaxEntries := maxEntriesForLevel(highestLoadedLevel)
for level := highestLoadedLevel; level > 0; level-- {
// When there are not enough entries left in the current level
// for the number that would be required to reach it, clear the
// the current level which effectively moves them all up to the
// previous level on the next iteration. Otherwise, there are
// are sufficient entries, so update the current level to
// contain as many entries as possible while still leaving
// enough remaining entries required to reach the level.
numEntries := len(curLevelData) / txEntrySize
prevLevelMaxEntries := curLevelMaxEntries / 2
minPrevRequired := minEntriesToReachLevel(level - 1)
if numEntries < prevLevelMaxEntries+minPrevRequired {
lowestEmptyLevel = level
pendingUpdates[level] = nil
} else {
// This level can only be completely full or half full,
// so choose the appropriate offset to ensure enough
// entries remain to reach the level.
var offset int
if numEntries-curLevelMaxEntries >= minPrevRequired {
offset = curLevelMaxEntries * txEntrySize
} else {
offset = prevLevelMaxEntries * txEntrySize
}
pendingUpdates[level] = curLevelData[:offset]
curLevelData = curLevelData[offset:]
}
curLevelMaxEntries = prevLevelMaxEntries
}
pendingUpdates[0] = curLevelData
if len(curLevelData) == 0 {
lowestEmptyLevel = 0
}
// When the highest loaded level is empty, it's possible the level after
// it still has data and thus that data needs to be backfilled as well.
for len(pendingUpdates[highestLoadedLevel]) == 0 {
// When the next level is empty too, the is no data left to
// continue backfilling, so there is nothing left to do.
// Otherwise, populate the pending updates map with the newly
// loaded data and update the highest loaded level accordingly.
level := highestLoadedLevel + 1
curLevelKey := keyForLevel(addrKey, level)
levelData := bucket.Get(curLevelKey[:])
if len(levelData) == 0 {
break
}
pendingUpdates[level] = levelData
highestLoadedLevel = level
// At this point the highest level is not empty, but it might
// be half full. When that is the case, move it up a level to
// simplify the code below which backfills all lower levels that
// are still empty. This also means the current level will be
// empty, so the loop will perform another another iteration to
// potentially backfill this level with data from the next one.
curLevelMaxEntries := maxEntriesForLevel(level)
if len(levelData)/txEntrySize != curLevelMaxEntries {
pendingUpdates[level] = nil
pendingUpdates[level-1] = levelData
level--
curLevelMaxEntries /= 2
}
// Backfill all lower levels that are still empty by iteratively
// halfing the data until the lowest empty level is filled.
for level > lowestEmptyLevel {
offset := (curLevelMaxEntries / 2) * txEntrySize
pendingUpdates[level] = levelData[:offset]
levelData = levelData[offset:]
pendingUpdates[level-1] = levelData
level--
curLevelMaxEntries /= 2
}
// The lowest possible empty level is now the highest loaded
// level.
lowestEmptyLevel = highestLoadedLevel
}
// Apply the pending updates.
return applyPending()
}
// addrToKey converts known address types to an addrindex key. An error is
// returned for unsupported types.
func addrToKey(addr util.Address) ([addrKeySize]byte, error) {
switch addr := addr.(type) {
case *util.AddressPubKeyHash:
var result [addrKeySize]byte
result[0] = addrKeyTypePubKeyHash
copy(result[1:], addr.Hash160()[:])
return result, nil
case *util.AddressScriptHash:
var result [addrKeySize]byte
result[0] = addrKeyTypeScriptHash
copy(result[1:], addr.Hash160()[:])
return result, nil
}
return [addrKeySize]byte{}, errUnsupportedAddressType
}
// AddrIndex implements a transaction by address index. That is to say, it
// supports querying all transactions that reference a given address because
// they are either crediting or debiting the address. The returned transactions
// are ordered according to their order of appearance in the blockchain. In
// other words, first by block height and then by offset inside the block.
//
// In addition, support is provided for a memory-only index of unconfirmed
// transactions such as those which are kept in the memory pool before inclusion
// in a block.
type AddrIndex struct {
// The following fields are set when the instance is created and can't
// be changed afterwards, so there is no need to protect them with a
// separate mutex.
db database.DB
dagParams *dagconfig.Params
// The following fields are used to quickly link transactions and
// addresses that have not been included into a block yet when an
// address index is being maintained. The are protected by the
// unconfirmedLock field.
//
// The txnsByAddr field is used to keep an index of all transactions
// which either create an output to a given address or spend from a
// previous output to it keyed by the address.
//
// The addrsByTx field is essentially the reverse and is used to
// keep an index of all addresses which a given transaction involves.
// This allows fairly efficient updates when transactions are removed
// once they are included into a block.
unconfirmedLock sync.RWMutex
txnsByAddr map[[addrKeySize]byte]map[daghash.TxID]*util.Tx
addrsByTx map[daghash.TxID]map[[addrKeySize]byte]struct{}
}
// Ensure the AddrIndex type implements the Indexer interface.
var _ Indexer = (*AddrIndex)(nil)
// Ensure the AddrIndex type implements the NeedsInputser interface.
var _ NeedsInputser = (*AddrIndex)(nil)
// NeedsInputs signals that the index requires the referenced inputs in order
// to properly create the index.
//
// This implements the NeedsInputser interface.
func (idx *AddrIndex) NeedsInputs() bool {
return true
}
// Init is only provided to satisfy the Indexer interface as there is nothing to
// initialize for this index.
//
// This is part of the Indexer interface.
func (idx *AddrIndex) Init(db database.DB, _ *blockdag.BlockDAG) error {
idx.db = db
return nil
}
// Key returns the database key to use for the index as a byte slice.
//
// This is part of the Indexer interface.
func (idx *AddrIndex) Key() []byte {
return addrIndexKey
}
// Name returns the human-readable name of the index.
//
// This is part of the Indexer interface.
func (idx *AddrIndex) Name() string {
return addrIndexName
}
// Create is invoked when the indexer manager determines the index needs
// to be created for the first time. It creates the bucket for the address
// index.
//
// This is part of the Indexer interface.
func (idx *AddrIndex) Create(dbTx database.Tx) error {
_, err := dbTx.Metadata().CreateBucket(addrIndexKey)
return err
}
// writeIndexData represents the address index data to be written for one block.
// It consists of the address mapped to an ordered list of the transactions
// that involve the address in block. It is ordered so the transactions can be
// stored in the order they appear in the block.
type writeIndexData map[[addrKeySize]byte][]int
// indexScriptPubKey extracts all standard addresses from the passed public key
// script and maps each of them to the associated transaction using the passed
// map.
func (idx *AddrIndex) indexScriptPubKey(data writeIndexData, scriptPubKey []byte, txIdx int) {
// Nothing to index if the script is non-standard or otherwise doesn't
// contain any addresses.
_, addr, err := txscript.ExtractScriptPubKeyAddress(scriptPubKey,
idx.dagParams)
if err != nil || addr == nil {
return
}
addrKey, err := addrToKey(addr)
if err != nil {
// Ignore unsupported address types.
return
}
// Avoid inserting the transaction more than once. Since the
// transactions are indexed serially any duplicates will be
// indexed in a row, so checking the most recent entry for the
// address is enough to detect duplicates.
indexedTxns := data[addrKey]
numTxns := len(indexedTxns)
if numTxns > 0 && indexedTxns[numTxns-1] == txIdx {
return
}
indexedTxns = append(indexedTxns, txIdx)
data[addrKey] = indexedTxns
}
// indexBlock extract all of the standard addresses from all of the transactions
// in the passed block and maps each of them to the associated transaction using
// the passed map.
func (idx *AddrIndex) indexBlock(data writeIndexData, block *util.Block, dag *blockdag.BlockDAG) {
for txIdx, tx := range block.Transactions() {
// Coinbases do not reference any inputs. Since the block is
// required to have already gone through full validation, it has
// already been proven on the first transaction in the block is
// a coinbase.
if txIdx > util.CoinbaseTransactionIndex {
for _, txIn := range tx.MsgTx().TxIn {
// The UTXO should always have the input since
// the index contract requires it, however, be
// safe and simply ignore any missing entries.
entry, ok := dag.GetUTXOEntry(txIn.PreviousOutpoint)
if !ok {
continue
}
idx.indexScriptPubKey(data, entry.ScriptPubKey(), txIdx)
}
}
for _, txOut := range tx.MsgTx().TxOut {
idx.indexScriptPubKey(data, txOut.ScriptPubKey, txIdx)
}
}
}
// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain. This indexer adds a mapping for each address
// the transactions in the block involve.
//
// This is part of the Indexer interface.
func (idx *AddrIndex) ConnectBlock(dbTx database.Tx, block *util.Block, blockID uint64, dag *blockdag.BlockDAG,
_ blockdag.MultiBlockTxsAcceptanceData, _ blockdag.MultiBlockTxsAcceptanceData) error {
// The offset and length of the transactions within the serialized
// block.
txLocs, err := block.TxLoc()
if err != nil {
return err
}
// Build all of the address to transaction mappings in a local map.
addrsToTxns := make(writeIndexData)
idx.indexBlock(addrsToTxns, block, dag)
// Add all of the index entries for each address.
addrIdxBucket := dbTx.Metadata().Bucket(addrIndexKey)
for addrKey, txIdxs := range addrsToTxns {
for _, txIdx := range txIdxs {
err := dbPutAddrIndexEntry(addrIdxBucket, addrKey,
blockID, txLocs[txIdx])
if err != nil {
return err
}
}
}
return nil
}
// DisconnectBlock is invoked by the index manager when a block has been
// disconnected from the main chain. This indexer removes the address mappings
// each transaction in the block involve.
//
// This is part of the Indexer interface.
func (idx *AddrIndex) DisconnectBlock(dbTx database.Tx, block *util.Block, dag *blockdag.BlockDAG) error {
// Build all of the address to transaction mappings in a local map.
addrsToTxns := make(writeIndexData)
idx.indexBlock(addrsToTxns, block, dag)
// Remove all of the index entries for each address.
bucket := dbTx.Metadata().Bucket(addrIndexKey)
for addrKey, txIdxs := range addrsToTxns {
err := dbRemoveAddrIndexEntries(bucket, addrKey, len(txIdxs))
if err != nil {
return err
}
}
return nil
}
// TxRegionsForAddress returns a slice of block regions which identify each
// transaction that involves the passed address according to the specified
// number to skip, number requested, and whether or not the results should be
// reversed. It also returns the number actually skipped since it could be less
// in the case where there are not enough entries.
//
// NOTE: These results only include transactions confirmed in blocks. See the
// UnconfirmedTxnsForAddress method for obtaining unconfirmed transactions
// that involve a given address.
//
// This function is safe for concurrent access.
func (idx *AddrIndex) TxRegionsForAddress(dbTx database.Tx, addr util.Address, numToSkip, numRequested uint32, reverse bool) ([]database.BlockRegion, uint32, error) {
addrKey, err := addrToKey(addr)
if err != nil {
return nil, 0, err
}
var regions []database.BlockRegion
var skipped uint32
err = idx.db.View(func(dbTx database.Tx) error {
// Create closure to lookup the block hash given the ID using
// the database transaction.
fetchBlockHash := func(id []byte) (*daghash.Hash, error) {
// Deserialize and populate the result.
return blockdag.DBFetchBlockHashBySerializedID(dbTx, id)
}
var err error
addrIdxBucket := dbTx.Metadata().Bucket(addrIndexKey)
regions, skipped, err = dbFetchAddrIndexEntries(addrIdxBucket,
addrKey, numToSkip, numRequested, reverse,
fetchBlockHash)
return err
})
return regions, skipped, err
}
// indexUnconfirmedAddresses modifies the unconfirmed (memory-only) address
// index to include mappings for the addresses encoded by the passed public key
// script to the transaction.
//
// This function is safe for concurrent access.
func (idx *AddrIndex) indexUnconfirmedAddresses(scriptPubKey []byte, tx *util.Tx) {
// The error is ignored here since the only reason it can fail is if the
// script fails to parse and it was already validated before being
// admitted to the mempool.
_, addr, _ := txscript.ExtractScriptPubKeyAddress(scriptPubKey,
idx.dagParams)
// Ignore unsupported address types.
addrKey, err := addrToKey(addr)
if err != nil {
return
}
// Add a mapping from the address to the transaction.
idx.unconfirmedLock.Lock()
addrIndexEntry := idx.txnsByAddr[addrKey]
if addrIndexEntry == nil {
addrIndexEntry = make(map[daghash.TxID]*util.Tx)
idx.txnsByAddr[addrKey] = addrIndexEntry
}
addrIndexEntry[*tx.ID()] = tx
// Add a mapping from the transaction to the address.
addrsByTxEntry := idx.addrsByTx[*tx.ID()]
if addrsByTxEntry == nil {
addrsByTxEntry = make(map[[addrKeySize]byte]struct{})
idx.addrsByTx[*tx.ID()] = addrsByTxEntry
}
addrsByTxEntry[addrKey] = struct{}{}
idx.unconfirmedLock.Unlock()
}
// AddUnconfirmedTx adds all addresses related to the transaction to the
// unconfirmed (memory-only) address index.
//
// NOTE: This transaction MUST have already been validated by the memory pool
// before calling this function with it and have all of the inputs available in
// the provided utxo view. Failure to do so could result in some or all
// addresses not being indexed.
//
// This function is safe for concurrent access.
func (idx *AddrIndex) AddUnconfirmedTx(tx *util.Tx, utxoSet blockdag.UTXOSet) {
// Index addresses of all referenced previous transaction outputs.
//
// The existence checks are elided since this is only called after the
// transaction has already been validated and thus all inputs are
// already known to exist.
for _, txIn := range tx.MsgTx().TxIn {
entry, ok := utxoSet.Get(txIn.PreviousOutpoint)
if !ok {
// Ignore missing entries. This should never happen
// in practice since the function comments specifically
// call out all inputs must be available.
continue
}
idx.indexUnconfirmedAddresses(entry.ScriptPubKey(), tx)
}
// Index addresses of all created outputs.
for _, txOut := range tx.MsgTx().TxOut {
idx.indexUnconfirmedAddresses(txOut.ScriptPubKey, tx)
}
}
// RemoveUnconfirmedTx removes the passed transaction from the unconfirmed
// (memory-only) address index.
//
// This function is safe for concurrent access.
func (idx *AddrIndex) RemoveUnconfirmedTx(txID *daghash.TxID) {
idx.unconfirmedLock.Lock()
defer idx.unconfirmedLock.Unlock()
// Remove all address references to the transaction from the address
// index and remove the entry for the address altogether if it no longer
// references any transactions.
for addrKey := range idx.addrsByTx[*txID] {
delete(idx.txnsByAddr[addrKey], *txID)
if len(idx.txnsByAddr[addrKey]) == 0 {
delete(idx.txnsByAddr, addrKey)
}
}
// Remove the entry from the transaction to address lookup map as well.
delete(idx.addrsByTx, *txID)
}
// UnconfirmedTxnsForAddress returns all transactions currently in the
// unconfirmed (memory-only) address index that involve the passed address.
// Unsupported address types are ignored and will result in no results.
//
// This function is safe for concurrent access.
func (idx *AddrIndex) UnconfirmedTxnsForAddress(addr util.Address) []*util.Tx {
// Ignore unsupported address types.
addrKey, err := addrToKey(addr)
if err != nil {
return nil
}
// Protect concurrent access.
idx.unconfirmedLock.RLock()
defer idx.unconfirmedLock.RUnlock()
// Return a new slice with the results if there are any. This ensures
// safe concurrency.
if txns, exists := idx.txnsByAddr[addrKey]; exists {
addressTxns := make([]*util.Tx, 0, len(txns))
for _, tx := range txns {
addressTxns = append(addressTxns, tx)
}
return addressTxns
}
return nil
}
// Recover is invoked when the indexer wasn't turned on for several blocks
// and the indexer needs to close the gaps.
//
// This is part of the Indexer interface.
func (idx *AddrIndex) Recover(dbTx database.Tx, currentBlockID, lastKnownBlockID uint64) error {
return errors.Errorf("addrindex was turned off for %d blocks and can't be recovered."+
" To resume working drop the addrindex with --dropaddrindex", lastKnownBlockID-currentBlockID)
}
// NewAddrIndex returns a new instance of an indexer that is used to create a
// mapping of all addresses in the blockchain to the respective transactions
// that involve them.
//
// It implements the Indexer interface which plugs into the IndexManager that in
// turn is used by the blockchain package. This allows the index to be
// seamlessly maintained along with the chain.
func NewAddrIndex(dagParams *dagconfig.Params) *AddrIndex {
return &AddrIndex{
dagParams: dagParams,
txnsByAddr: make(map[[addrKeySize]byte]map[daghash.TxID]*util.Tx),
addrsByTx: make(map[daghash.TxID]map[[addrKeySize]byte]struct{}),
}
}
// DropAddrIndex drops the address index from the provided database if it
// exists.
func DropAddrIndex(db database.DB, interrupt <-chan struct{}) error {
return dropIndex(db, addrIndexKey, addrIndexName, interrupt)
}

View File

@@ -0,0 +1,277 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package indexers
import (
"bytes"
"fmt"
"github.com/pkg/errors"
"testing"
"github.com/kaspanet/kaspad/wire"
)
// addrIndexBucket provides a mock address index database bucket by implementing
// the internalBucket interface.
type addrIndexBucket struct {
levels map[[levelKeySize]byte][]byte
}
// Clone returns a deep copy of the mock address index bucket.
func (b *addrIndexBucket) Clone() *addrIndexBucket {
levels := make(map[[levelKeySize]byte][]byte)
for k, v := range b.levels {
vCopy := make([]byte, len(v))
copy(vCopy, v)
levels[k] = vCopy
}
return &addrIndexBucket{levels: levels}
}
// Get returns the value associated with the key from the mock address index
// bucket.
//
// This is part of the internalBucket interface.
func (b *addrIndexBucket) Get(key []byte) []byte {
var levelKey [levelKeySize]byte
copy(levelKey[:], key)
return b.levels[levelKey]
}
// Put stores the provided key/value pair to the mock address index bucket.
//
// This is part of the internalBucket interface.
func (b *addrIndexBucket) Put(key []byte, value []byte) error {
var levelKey [levelKeySize]byte
copy(levelKey[:], key)
b.levels[levelKey] = value
return nil
}
// Delete removes the provided key from the mock address index bucket.
//
// This is part of the internalBucket interface.
func (b *addrIndexBucket) Delete(key []byte) error {
var levelKey [levelKeySize]byte
copy(levelKey[:], key)
delete(b.levels, levelKey)
return nil
}
// printLevels returns a string with a visual representation of the provided
// address key taking into account the max size of each level. It is useful
// when creating and debugging test cases.
func (b *addrIndexBucket) printLevels(addrKey [addrKeySize]byte) string {
highestLevel := uint8(0)
for k := range b.levels {
if !bytes.Equal(k[:levelOffset], addrKey[:]) {
continue
}
level := uint8(k[levelOffset])
if level > highestLevel {
highestLevel = level
}
}
var levelBuf bytes.Buffer
_, _ = levelBuf.WriteString("\n")
maxEntries := level0MaxEntries
for level := uint8(0); level <= highestLevel; level++ {
data := b.levels[keyForLevel(addrKey, level)]
numEntries := len(data) / txEntrySize
for i := 0; i < numEntries; i++ {
start := i * txEntrySize
num := byteOrder.Uint32(data[start:])
_, _ = levelBuf.WriteString(fmt.Sprintf("%02d ", num))
}
for i := numEntries; i < maxEntries; i++ {
_, _ = levelBuf.WriteString("_ ")
}
_, _ = levelBuf.WriteString("\n")
maxEntries *= 2
}
return levelBuf.String()
}
// sanityCheck ensures that all data stored in the bucket for the given address
// adheres to the level-based rules described by the address index
// documentation.
func (b *addrIndexBucket) sanityCheck(addrKey [addrKeySize]byte, expectedTotal int) error {
// Find the highest level for the key.
highestLevel := uint8(0)
for k := range b.levels {
if !bytes.Equal(k[:levelOffset], addrKey[:]) {
continue
}
level := uint8(k[levelOffset])
if level > highestLevel {
highestLevel = level
}
}
// Ensure the expected total number of entries are present and that
// all levels adhere to the rules described in the address index
// documentation.
var totalEntries int
maxEntries := level0MaxEntries
for level := uint8(0); level <= highestLevel; level++ {
// Level 0 can'have more entries than the max allowed if the
// levels after it have data and it can't be empty. All other
// levels must either be half full or full.
data := b.levels[keyForLevel(addrKey, level)]
numEntries := len(data) / txEntrySize
totalEntries += numEntries
if level == 0 {
if (highestLevel != 0 && numEntries == 0) ||
numEntries > maxEntries {
return errors.Errorf("level %d has %d entries",
level, numEntries)
}
} else if numEntries != maxEntries && numEntries != maxEntries/2 {
return errors.Errorf("level %d has %d entries", level,
numEntries)
}
maxEntries *= 2
}
if totalEntries != expectedTotal {
return errors.Errorf("expected %d entries - got %d", expectedTotal,
totalEntries)
}
// Ensure all of the numbers are in order starting from the highest
// level moving to the lowest level.
expectedNum := uint32(0)
for level := highestLevel + 1; level > 0; level-- {
data := b.levels[keyForLevel(addrKey, level)]
numEntries := len(data) / txEntrySize
for i := 0; i < numEntries; i++ {
start := i * txEntrySize
num := byteOrder.Uint32(data[start:])
if num != expectedNum {
return errors.Errorf("level %d offset %d does "+
"not contain the expected number of "+
"%d - got %d", level, i, num,
expectedNum)
}
expectedNum++
}
}
return nil
}
// TestAddrIndexLevels ensures that adding and deleting entries to the address
// index creates multiple levels as described by the address index
// documentation.
func TestAddrIndexLevels(t *testing.T) {
t.Parallel()
tests := []struct {
name string
key [addrKeySize]byte
numInsert int
printLevels bool // Set to help debug a specific test.
}{
{
name: "level 0 not full",
numInsert: level0MaxEntries - 1,
},
{
name: "level 1 half",
numInsert: level0MaxEntries + 1,
},
{
name: "level 1 full",
numInsert: level0MaxEntries*2 + 1,
},
{
name: "level 2 half, level 1 half",
numInsert: level0MaxEntries*3 + 1,
},
{
name: "level 2 half, level 1 full",
numInsert: level0MaxEntries*4 + 1,
},
{
name: "level 2 full, level 1 half",
numInsert: level0MaxEntries*5 + 1,
},
{
name: "level 2 full, level 1 full",
numInsert: level0MaxEntries*6 + 1,
},
{
name: "level 3 half, level 2 half, level 1 half",
numInsert: level0MaxEntries*7 + 1,
},
{
name: "level 3 full, level 2 half, level 1 full",
numInsert: level0MaxEntries*12 + 1,
},
}
nextTest:
for testNum, test := range tests {
// Insert entries in order.
populatedBucket := &addrIndexBucket{
levels: make(map[[levelKeySize]byte][]byte),
}
for i := 0; i < test.numInsert; i++ {
txLoc := wire.TxLoc{TxStart: i * 2}
err := dbPutAddrIndexEntry(populatedBucket, test.key,
uint64(i), txLoc)
if err != nil {
t.Errorf("dbPutAddrIndexEntry #%d (%s) - "+
"unexpected error: %v", testNum,
test.name, err)
continue nextTest
}
}
if test.printLevels {
t.Log(populatedBucket.printLevels(test.key))
}
// Delete entries from the populated bucket until all entries
// have been deleted. The bucket is reset to the fully
// populated bucket on each iteration so every combination is
// tested. Notice the upper limit purposes exceeds the number
// of entries to ensure attempting to delete more entries than
// there are works correctly.
for numDelete := 0; numDelete <= test.numInsert+1; numDelete++ {
// Clone populated bucket to run each delete against.
bucket := populatedBucket.Clone()
// Remove the number of entries for this iteration.
err := dbRemoveAddrIndexEntries(bucket, test.key,
numDelete)
if err != nil {
if numDelete <= test.numInsert {
t.Errorf("dbRemoveAddrIndexEntries (%s) "+
" delete %d - unexpected error: "+
"%v", test.name, numDelete, err)
continue nextTest
}
}
if test.printLevels {
t.Log(bucket.printLevels(test.key))
}
// Sanity check the levels to ensure the adhere to all
// rules.
numExpected := test.numInsert
if numDelete <= test.numInsert {
numExpected -= numDelete
}
err = bucket.sanityCheck(test.key, numExpected)
if err != nil {
t.Errorf("sanity check fail (%s) delete %d: %v",
test.name, numDelete, err)
continue nextTest
}
}
}
}

112
blockdag/indexers/common.go Normal file
View File

@@ -0,0 +1,112 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
/*
Package indexers implements optional block chain indexes.
*/
package indexers
import (
"encoding/binary"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
)
var (
// byteOrder is the preferred byte order used for serializing numeric
// fields for storage in the database.
byteOrder = binary.LittleEndian
// errInterruptRequested indicates that an operation was cancelled due
// to a user-requested interrupt.
errInterruptRequested = errors.New("interrupt requested")
)
// NeedsInputser provides a generic interface for an indexer to specify the it
// requires the ability to look up inputs for a transaction.
type NeedsInputser interface {
NeedsInputs() bool
}
// Indexer provides a generic interface for an indexer that is managed by an
// index manager such as the Manager type provided by this package.
type Indexer interface {
// Key returns the key of the index as a byte slice.
Key() []byte
// Name returns the human-readable name of the index.
Name() string
// Create is invoked when the indexer manager determines the index needs
// to be created for the first time.
Create(dbTx database.Tx) error
// Init is invoked when the index manager is first initializing the
// index. This differs from the Create method in that it is called on
// every load, including the case the index was just created.
Init(db database.DB, dag *blockdag.BlockDAG) error
// ConnectBlock is invoked when the index manager is notified that a new
// block has been connected to the DAG.
ConnectBlock(dbTx database.Tx,
block *util.Block,
blockID uint64,
dag *blockdag.BlockDAG,
acceptedTxsData blockdag.MultiBlockTxsAcceptanceData,
virtualTxsAcceptanceData blockdag.MultiBlockTxsAcceptanceData) error
// Recover is invoked when the indexer wasn't turned on for several blocks
// and the indexer needs to close the gaps.
Recover(dbTx database.Tx, currentBlockID, lastKnownBlockID uint64) error
}
// AssertError identifies an error that indicates an internal code consistency
// issue and should be treated as a critical and unrecoverable error.
type AssertError string
// Error returns the assertion error as a huma-readable string and satisfies
// the error interface.
func (e AssertError) Error() string {
return "assertion failed: " + string(e)
}
// errDeserialize signifies that a problem was encountered when deserializing
// data.
type errDeserialize string
// Error implements the error interface.
func (e errDeserialize) Error() string {
return string(e)
}
// isDeserializeErr returns whether or not the passed error is an errDeserialize
// error.
func isDeserializeErr(err error) bool {
_, ok := err.(errDeserialize)
return ok
}
// internalBucket is an abstraction over a database bucket. It is used to make
// the code easier to test since it allows mock objects in the tests to only
// implement these functions instead of everything a database.Bucket supports.
type internalBucket interface {
Get(key []byte) []byte
Put(key []byte, value []byte) error
Delete(key []byte) error
}
// interruptRequested returns true when the provided channel has been closed.
// This simplifies early shutdown slightly since the caller can just use an if
// statement instead of a select.
func interruptRequested(interrupted <-chan struct{}) bool {
select {
case <-interrupted:
return true
default:
}
return false
}

13
blockdag/indexers/log.go Normal file
View File

@@ -0,0 +1,13 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package indexers
import (
"github.com/kaspanet/kaspad/logger"
"github.com/kaspanet/kaspad/util/panics"
)
var log, _ = logger.Get(logger.SubsystemTags.INDX)
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -0,0 +1,389 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package indexers
import (
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
var (
// indexTipsBucketName is the name of the db bucket used to house the
// current tip of each index.
indexTipsBucketName = []byte("idxtips")
indexCurrentBlockIDBucketName = []byte("idxcurrentblockid")
)
// Manager defines an index manager that manages multiple optional indexes and
// implements the blockchain.IndexManager interface so it can be seamlessly
// plugged into normal chain processing.
type Manager struct {
db database.DB
enabledIndexes []Indexer
}
// Ensure the Manager type implements the blockchain.IndexManager interface.
var _ blockdag.IndexManager = (*Manager)(nil)
// indexDropKey returns the key for an index which indicates it is in the
// process of being dropped.
func indexDropKey(idxKey []byte) []byte {
dropKey := make([]byte, len(idxKey)+1)
dropKey[0] = 'd'
copy(dropKey[1:], idxKey)
return dropKey
}
// maybeFinishDrops determines if each of the enabled indexes are in the middle
// of being dropped and finishes dropping them when the are. This is necessary
// because dropping and index has to be done in several atomic steps rather than
// one big atomic step due to the massive number of entries.
func (m *Manager) maybeFinishDrops(interrupt <-chan struct{}) error {
indexNeedsDrop := make([]bool, len(m.enabledIndexes))
err := m.db.View(func(dbTx database.Tx) error {
// None of the indexes needs to be dropped if the index tips
// bucket hasn't been created yet.
indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName)
if indexesBucket == nil {
return nil
}
// Mark the indexer as requiring a drop if one is already in
// progress.
for i, indexer := range m.enabledIndexes {
dropKey := indexDropKey(indexer.Key())
if indexesBucket.Get(dropKey) != nil {
indexNeedsDrop[i] = true
}
}
return nil
})
if err != nil {
return err
}
if interruptRequested(interrupt) {
return errInterruptRequested
}
// Finish dropping any of the enabled indexes that are already in the
// middle of being dropped.
for i, indexer := range m.enabledIndexes {
if !indexNeedsDrop[i] {
continue
}
log.Infof("Resuming %s drop", indexer.Name())
err := dropIndex(m.db, indexer.Key(), indexer.Name(), interrupt)
if err != nil {
return err
}
}
return nil
}
// maybeCreateIndexes determines if each of the enabled indexes have already
// been created and creates them if not.
func (m *Manager) maybeCreateIndexes(dbTx database.Tx) error {
indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName)
for _, indexer := range m.enabledIndexes {
// Nothing to do if the index tip already exists.
idxKey := indexer.Key()
if indexesBucket.Get(idxKey) != nil {
continue
}
// The tip for the index does not exist, so create it and
// invoke the create callback for the index so it can perform
// any one-time initialization it requires.
if err := indexer.Create(dbTx); err != nil {
return err
}
// TODO (Mike): this is temporary solution to prevent node from not starting
// because it thinks indexers are not initialized.
// Indexers, however, do not work properly, and a general solution to their work operation is required
indexesBucket.Put(idxKey, []byte{0})
}
return nil
}
// Init initializes the enabled indexes. This is called during chain
// initialization and primarily consists of catching up all indexes to the
// current best chain tip. This is necessary since each index can be disabled
// and re-enabled at any time and attempting to catch-up indexes at the same
// time new blocks are being downloaded would lead to an overall longer time to
// catch up due to the I/O contention.
//
// This is part of the blockchain.IndexManager interface.
func (m *Manager) Init(db database.DB, blockDAG *blockdag.BlockDAG, interrupt <-chan struct{}) error {
// Nothing to do when no indexes are enabled.
if len(m.enabledIndexes) == 0 {
return nil
}
if interruptRequested(interrupt) {
return errInterruptRequested
}
m.db = db
// Finish and drops that were previously interrupted.
if err := m.maybeFinishDrops(interrupt); err != nil {
return err
}
// Create the initial state for the indexes as needed.
err := m.db.Update(func(dbTx database.Tx) error {
// Create the bucket for the current tips as needed.
meta := dbTx.Metadata()
_, err := meta.CreateBucketIfNotExists(indexTipsBucketName)
if err != nil {
return err
}
if _, err := meta.CreateBucketIfNotExists(indexCurrentBlockIDBucketName); err != nil {
return err
}
return m.maybeCreateIndexes(dbTx)
})
if err != nil {
return err
}
// Initialize each of the enabled indexes.
for _, indexer := range m.enabledIndexes {
if err := indexer.Init(db, blockDAG); err != nil {
return err
}
}
return m.recoverIfNeeded()
}
// recoverIfNeeded checks if the node worked for some time
// without one of the current enabled indexes, and if it's
// the case, recovers the missing blocks from the index.
func (m *Manager) recoverIfNeeded() error {
return m.db.Update(func(dbTx database.Tx) error {
lastKnownBlockID := blockdag.DBFetchCurrentBlockID(dbTx)
for _, indexer := range m.enabledIndexes {
serializedCurrentIdxBlockID := dbTx.Metadata().Bucket(indexCurrentBlockIDBucketName).Get(indexer.Key())
currentIdxBlockID := uint64(0)
if serializedCurrentIdxBlockID != nil {
currentIdxBlockID = blockdag.DeserializeBlockID(serializedCurrentIdxBlockID)
}
if lastKnownBlockID > currentIdxBlockID {
err := indexer.Recover(dbTx, currentIdxBlockID, lastKnownBlockID)
if err != nil {
return err
}
}
}
return nil
})
}
// ConnectBlock must be invoked when a block is extending the main chain. It
// keeps track of the state of each index it is managing, performs some sanity
// checks, and invokes each indexer.
//
// This is part of the blockchain.IndexManager interface.
func (m *Manager) ConnectBlock(dbTx database.Tx, block *util.Block, blockID uint64, dag *blockdag.BlockDAG,
txsAcceptanceData blockdag.MultiBlockTxsAcceptanceData, virtualTxsAcceptanceData blockdag.MultiBlockTxsAcceptanceData) error {
// Call each of the currently active optional indexes with the block
// being connected so they can update accordingly.
for _, index := range m.enabledIndexes {
// Notify the indexer with the connected block so it can index it.
if err := index.ConnectBlock(dbTx, block, blockID, dag, txsAcceptanceData, virtualTxsAcceptanceData); err != nil {
return err
}
}
// Add the new block ID index entry for the block being connected and
// update the current internal block ID accordingly.
err := m.updateIndexersWithCurrentBlockID(dbTx, block.Hash(), blockID)
if err != nil {
return err
}
return nil
}
func (m *Manager) updateIndexersWithCurrentBlockID(dbTx database.Tx, blockHash *daghash.Hash, blockID uint64) error {
serializedBlockID := blockdag.SerializeBlockID(blockID)
for _, index := range m.enabledIndexes {
err := dbTx.Metadata().Bucket(indexCurrentBlockIDBucketName).Put(index.Key(), serializedBlockID)
if err != nil {
return err
}
}
return nil
}
// NewManager returns a new index manager with the provided indexes enabled.
//
// The manager returned satisfies the blockchain.IndexManager interface and thus
// cleanly plugs into the normal blockchain processing path.
func NewManager(enabledIndexes []Indexer) *Manager {
return &Manager{
enabledIndexes: enabledIndexes,
}
}
// dropIndex drops the passed index from the database. Since indexes can be
// massive, it deletes the index in multiple database transactions in order to
// keep memory usage to reasonable levels. It also marks the drop in progress
// so the drop can be resumed if it is stopped before it is done before the
// index can be used again.
func dropIndex(db database.DB, idxKey []byte, idxName string, interrupt <-chan struct{}) error {
// Nothing to do if the index doesn't already exist.
var needsDelete bool
err := db.View(func(dbTx database.Tx) error {
indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName)
if indexesBucket != nil && indexesBucket.Get(idxKey) != nil {
needsDelete = true
}
return nil
})
if err != nil {
return err
}
if !needsDelete {
log.Infof("Not dropping %s because it does not exist", idxName)
return nil
}
// Mark that the index is in the process of being dropped so that it
// can be resumed on the next start if interrupted before the process is
// complete.
log.Infof("Dropping all %s entries. This might take a while...",
idxName)
err = db.Update(func(dbTx database.Tx) error {
indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName)
return indexesBucket.Put(indexDropKey(idxKey), idxKey)
})
if err != nil {
return err
}
// Since the indexes can be so large, attempting to simply delete
// the bucket in a single database transaction would result in massive
// memory usage and likely crash many systems due to ulimits. In order
// to avoid this, use a cursor to delete a maximum number of entries out
// of the bucket at a time. Recurse buckets depth-first to delete any
// sub-buckets.
const maxDeletions = 2000000
var totalDeleted uint64
// Recurse through all buckets in the index, cataloging each for
// later deletion.
var subBuckets [][][]byte
var subBucketClosure func(database.Tx, []byte, [][]byte) error
subBucketClosure = func(dbTx database.Tx,
subBucket []byte, tlBucket [][]byte) error {
// Get full bucket name and append to subBuckets for later
// deletion.
var bucketName [][]byte
if (tlBucket == nil) || (len(tlBucket) == 0) {
bucketName = append(bucketName, subBucket)
} else {
bucketName = append(tlBucket, subBucket)
}
subBuckets = append(subBuckets, bucketName)
// Recurse sub-buckets to append to subBuckets slice.
bucket := dbTx.Metadata()
for _, subBucketName := range bucketName {
bucket = bucket.Bucket(subBucketName)
}
return bucket.ForEachBucket(func(k []byte) error {
return subBucketClosure(dbTx, k, bucketName)
})
}
// Call subBucketClosure with top-level bucket.
err = db.View(func(dbTx database.Tx) error {
return subBucketClosure(dbTx, idxKey, nil)
})
if err != nil {
return nil
}
// Iterate through each sub-bucket in reverse, deepest-first, deleting
// all keys inside them and then dropping the buckets themselves.
for i := range subBuckets {
bucketName := subBuckets[len(subBuckets)-1-i]
// Delete maxDeletions key/value pairs at a time.
for numDeleted := maxDeletions; numDeleted == maxDeletions; {
numDeleted = 0
err := db.Update(func(dbTx database.Tx) error {
subBucket := dbTx.Metadata()
for _, subBucketName := range bucketName {
subBucket = subBucket.Bucket(subBucketName)
}
cursor := subBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() &&
numDeleted < maxDeletions {
if err := cursor.Delete(); err != nil {
return err
}
numDeleted++
}
return nil
})
if err != nil {
return err
}
if numDeleted > 0 {
totalDeleted += uint64(numDeleted)
log.Infof("Deleted %d keys (%d total) from %s",
numDeleted, totalDeleted, idxName)
}
}
if interruptRequested(interrupt) {
return errInterruptRequested
}
// Drop the bucket itself.
err = db.Update(func(dbTx database.Tx) error {
bucket := dbTx.Metadata()
for j := 0; j < len(bucketName)-1; j++ {
bucket = bucket.Bucket(bucketName[j])
}
return bucket.DeleteBucket(bucketName[len(bucketName)-1])
})
}
// Remove the index tip, index bucket, and in-progress drop flag now
// that all index entries have been removed.
err = db.Update(func(dbTx database.Tx) error {
meta := dbTx.Metadata()
indexesBucket := meta.Bucket(indexTipsBucketName)
if err := indexesBucket.Delete(idxKey); err != nil {
return err
}
if err := meta.Bucket(indexCurrentBlockIDBucketName).Delete(idxKey); err != nil {
return err
}
return indexesBucket.Delete(indexDropKey(idxKey))
})
if err != nil {
return err
}
log.Infof("Dropped %s", idxName)
return nil
}

View File

@@ -0,0 +1,431 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package indexers
import (
"fmt"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/database"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
"github.com/pkg/errors"
)
const (
// txIndexName is the human-readable name for the index.
txIndexName = "transaction index"
includingBlocksIndexKeyEntrySize = 8 // 4 bytes for offset + 4 bytes for transaction length
)
var (
includingBlocksIndexKey = []byte("includingblocksidx")
acceptingBlocksIndexKey = []byte("acceptingblocksidx")
)
// txsAcceptedByVirtual is the in-memory index of txIDs that were accepted
// by the current virtual
var txsAcceptedByVirtual map[daghash.TxID]bool
// -----------------------------------------------------------------------------
// The transaction index consists of an entry for every transaction in the DAG.
//
// There are two buckets used in total. The first bucket maps the hash of
// each transaction to its location in each block it's included in. The second bucket
// contains all of the blocks that from their viewpoint the transaction has been
// accepted (i.e. the transaction is found in their blue set without double spends),
// and their blue block (or themselves) that included the transaction.
//
// NOTE: Although it is technically possible for multiple transactions to have
// the same hash as long as the previous transaction with the same hash is fully
// spent, this code only stores the most recent one because doing otherwise
// would add a non-trivial amount of space and overhead for something that will
// realistically never happen per the probability and even if it did, the old
// one must be fully spent and so the most likely transaction a caller would
// want for a given hash is the most recent one anyways.
//
// The including blocks index contains a sub bucket for each transaction hash (32 byte each), that its serialized format is:
//
// <block id> = <start offset><tx length>
//
// Field Type Size
// block id uint64 8 bytes
// start offset uint32 4 bytes
// tx length uint32 4 bytes
// -----
// Total: 16 bytes
//
// The accepting blocks index contains a sub bucket for each transaction hash (32 byte each), that its serialized format is:
//
// <accepting block id> = <including block id>
//
// Field Type Size
// accepting block id uint64 8 bytes
// including block id uint64 8 bytes
// -----
// Total: 16 bytes
//
// -----------------------------------------------------------------------------
func putIncludingBlocksEntry(target []byte, txLoc wire.TxLoc) {
byteOrder.PutUint32(target, uint32(txLoc.TxStart))
byteOrder.PutUint32(target[4:], uint32(txLoc.TxLen))
}
func dbPutIncludingBlocksEntry(dbTx database.Tx, txID *daghash.TxID, blockID uint64, serializedData []byte) error {
bucket, err := dbTx.Metadata().Bucket(includingBlocksIndexKey).CreateBucketIfNotExists(txID[:])
if err != nil {
return err
}
return bucket.Put(blockdag.SerializeBlockID(blockID), serializedData)
}
func dbPutAcceptingBlocksEntry(dbTx database.Tx, txID *daghash.TxID, blockID uint64, serializedData []byte) error {
bucket, err := dbTx.Metadata().Bucket(acceptingBlocksIndexKey).CreateBucketIfNotExists(txID[:])
if err != nil {
return err
}
return bucket.Put(blockdag.SerializeBlockID(blockID), serializedData)
}
// dbFetchFirstTxRegion uses an existing database transaction to fetch the block
// region for the provided transaction hash from the transaction index. When
// there is no entry for the provided hash, nil will be returned for the both
// the region and the error.
//
// P.S Because the transaction can be found in multiple blocks, this function arbitarily
// returns the first block region that is stored in the txindex.
func dbFetchFirstTxRegion(dbTx database.Tx, txID *daghash.TxID) (*database.BlockRegion, error) {
// Load the record from the database and return now if it doesn't exist.
txBucket := dbTx.Metadata().Bucket(includingBlocksIndexKey).Bucket(txID[:])
if txBucket == nil {
return nil, database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("No block region "+
"was found for %s", txID),
}
}
cursor := txBucket.Cursor()
if ok := cursor.First(); !ok {
return nil, database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("No block region "+
"was found for %s", txID),
}
}
serializedBlockID := cursor.Key()
serializedData := cursor.Value()
if len(serializedData) == 0 {
return nil, nil
}
// Ensure the serialized data has enough bytes to properly deserialize.
if len(serializedData) < includingBlocksIndexKeyEntrySize {
return nil, database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("corrupt transaction index "+
"entry for %s", txID),
}
}
// Load the block hash associated with the block ID.
hash, err := blockdag.DBFetchBlockHashBySerializedID(dbTx, serializedBlockID)
if err != nil {
return nil, database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("corrupt transaction index "+
"entry for %s: %s", txID, err),
}
}
// Deserialize the final entry.
region := database.BlockRegion{Hash: &daghash.Hash{}}
copy(region.Hash[:], hash[:])
region.Offset = byteOrder.Uint32(serializedData[:4])
region.Len = byteOrder.Uint32(serializedData[4:])
return &region, nil
}
// dbAddTxIndexEntries uses an existing database transaction to add a
// transaction index entry for every transaction in the passed block.
func dbAddTxIndexEntries(dbTx database.Tx, block *util.Block, blockID uint64, multiBlockTxsAcceptanceData blockdag.MultiBlockTxsAcceptanceData) error {
// The offset and length of the transactions within the serialized
// block.
txLocs, err := block.TxLoc()
if err != nil {
return err
}
// As an optimization, allocate a single slice big enough to hold all
// of the serialized transaction index entries for the block and
// serialize them directly into the slice. Then, pass the appropriate
// subslice to the database to be written. This approach significantly
// cuts down on the number of required allocations.
includingBlocksOffset := 0
serializedIncludingBlocksValues := make([]byte, len(block.Transactions())*includingBlocksIndexKeyEntrySize)
for i, tx := range block.Transactions() {
putIncludingBlocksEntry(serializedIncludingBlocksValues[includingBlocksOffset:], txLocs[i])
endOffset := includingBlocksOffset + includingBlocksIndexKeyEntrySize
err := dbPutIncludingBlocksEntry(dbTx, tx.ID(), blockID,
serializedIncludingBlocksValues[includingBlocksOffset:endOffset:endOffset])
if err != nil {
return err
}
includingBlocksOffset += includingBlocksIndexKeyEntrySize
}
for _, blockTxsAcceptanceData := range multiBlockTxsAcceptanceData {
var includingBlockID uint64
if blockTxsAcceptanceData.BlockHash.IsEqual(block.Hash()) {
includingBlockID = blockID
} else {
includingBlockID, err = blockdag.DBFetchBlockIDByHash(dbTx, &blockTxsAcceptanceData.BlockHash)
if err != nil {
return err
}
}
serializedIncludingBlockID := blockdag.SerializeBlockID(includingBlockID)
for _, txAcceptanceData := range blockTxsAcceptanceData.TxAcceptanceData {
err = dbPutAcceptingBlocksEntry(dbTx, txAcceptanceData.Tx.ID(), blockID, serializedIncludingBlockID)
if err != nil {
return err
}
}
}
return nil
}
func updateTxsAcceptedByVirtual(virtualTxsAcceptanceData blockdag.MultiBlockTxsAcceptanceData) error {
// Initialize a new txsAcceptedByVirtual
entries := 0
for _, blockTxsAcceptanceData := range virtualTxsAcceptanceData {
entries += len(blockTxsAcceptanceData.TxAcceptanceData)
}
txsAcceptedByVirtual = make(map[daghash.TxID]bool, entries)
// Copy virtualTxsAcceptanceData to txsAcceptedByVirtual
for _, blockTxsAcceptanceData := range virtualTxsAcceptanceData {
for _, txAcceptanceData := range blockTxsAcceptanceData.TxAcceptanceData {
txsAcceptedByVirtual[*txAcceptanceData.Tx.ID()] = true
}
}
return nil
}
// TxIndex implements a transaction by hash index. That is to say, it supports
// querying all transactions by their hash.
type TxIndex struct {
db database.DB
}
// Ensure the TxIndex type implements the Indexer interface.
var _ Indexer = (*TxIndex)(nil)
// Init initializes the hash-based transaction index. In particular, it finds
// the highest used block ID and stores it for later use when connecting or
// disconnecting blocks.
//
// This is part of the Indexer interface.
func (idx *TxIndex) Init(db database.DB, dag *blockdag.BlockDAG) error {
idx.db = db
// Initialize the txsAcceptedByVirtual index
virtualTxsAcceptanceData, err := dag.TxsAcceptedByVirtual()
if err != nil {
return err
}
err = updateTxsAcceptedByVirtual(virtualTxsAcceptanceData)
if err != nil {
return err
}
return nil
}
// Key returns the database key to use for the index as a byte slice.
//
// This is part of the Indexer interface.
func (idx *TxIndex) Key() []byte {
return includingBlocksIndexKey
}
// Name returns the human-readable name of the index.
//
// This is part of the Indexer interface.
func (idx *TxIndex) Name() string {
return txIndexName
}
// Create is invoked when the indexer manager determines the index needs
// to be created for the first time. It creates the buckets for the hash-based
// transaction index and the internal block ID indexes.
//
// This is part of the Indexer interface.
func (idx *TxIndex) Create(dbTx database.Tx) error {
meta := dbTx.Metadata()
if _, err := meta.CreateBucket(includingBlocksIndexKey); err != nil {
return err
}
_, err := meta.CreateBucket(acceptingBlocksIndexKey)
return err
}
// ConnectBlock is invoked by the index manager when a new block has been
// connected to the DAG. This indexer adds a hash-to-transaction mapping
// for every transaction in the passed block.
//
// This is part of the Indexer interface.
func (idx *TxIndex) ConnectBlock(dbTx database.Tx, block *util.Block, blockID uint64, dag *blockdag.BlockDAG,
acceptedTxsData blockdag.MultiBlockTxsAcceptanceData, virtualTxsAcceptanceData blockdag.MultiBlockTxsAcceptanceData) error {
if err := dbAddTxIndexEntries(dbTx, block, blockID, acceptedTxsData); err != nil {
return err
}
err := updateTxsAcceptedByVirtual(virtualTxsAcceptanceData)
if err != nil {
return err
}
return nil
}
// TxFirstBlockRegion returns the first block region for the provided transaction hash
// from the transaction index. The block region can in turn be used to load the
// raw transaction bytes. When there is no entry for the provided hash, nil
// will be returned for the both the entry and the error.
//
// This function is safe for concurrent access.
func (idx *TxIndex) TxFirstBlockRegion(txID *daghash.TxID) (*database.BlockRegion, error) {
var region *database.BlockRegion
err := idx.db.View(func(dbTx database.Tx) error {
var err error
region, err = dbFetchFirstTxRegion(dbTx, txID)
return err
})
return region, err
}
// TxBlocks returns the hashes of the blocks where the transaction exists
func (idx *TxIndex) TxBlocks(txHash *daghash.Hash) ([]*daghash.Hash, error) {
blockHashes := make([]*daghash.Hash, 0)
err := idx.db.View(func(dbTx database.Tx) error {
var err error
blockHashes, err = dbFetchTxBlocks(dbTx, txHash)
if err != nil {
return err
}
return nil
})
return blockHashes, err
}
func dbFetchTxBlocks(dbTx database.Tx, txHash *daghash.Hash) ([]*daghash.Hash, error) {
blockHashes := make([]*daghash.Hash, 0)
bucket := dbTx.Metadata().Bucket(includingBlocksIndexKey).Bucket(txHash[:])
if bucket == nil {
return nil, database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("No including blocks "+
"were found for %s", txHash),
}
}
err := bucket.ForEach(func(serializedBlockID, _ []byte) error {
blockHash, err := blockdag.DBFetchBlockHashBySerializedID(dbTx, serializedBlockID)
if err != nil {
return err
}
blockHashes = append(blockHashes, blockHash)
return nil
})
if err != nil {
return nil, err
}
return blockHashes, nil
}
// BlockThatAcceptedTx returns the hash of the block where the transaction got accepted (from the virtual block point of view)
func (idx *TxIndex) BlockThatAcceptedTx(dag *blockdag.BlockDAG, txID *daghash.TxID) (*daghash.Hash, error) {
var acceptingBlock *daghash.Hash
err := idx.db.View(func(dbTx database.Tx) error {
var err error
acceptingBlock, err = dbFetchTxAcceptingBlock(dbTx, txID, dag)
return err
})
return acceptingBlock, err
}
func dbFetchTxAcceptingBlock(dbTx database.Tx, txID *daghash.TxID, dag *blockdag.BlockDAG) (*daghash.Hash, error) {
// If the transaction was accepted by the current virtual,
// return the zeroHash immediately
if _, ok := txsAcceptedByVirtual[*txID]; ok {
return &daghash.ZeroHash, nil
}
bucket := dbTx.Metadata().Bucket(acceptingBlocksIndexKey).Bucket(txID[:])
if bucket == nil {
return nil, nil
}
cursor := bucket.Cursor()
if !cursor.First() {
return nil, database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("Accepting blocks bucket is "+
"empty for %s", txID),
}
}
for ; cursor.Key() != nil; cursor.Next() {
blockHash, err := blockdag.DBFetchBlockHashBySerializedID(dbTx, cursor.Key())
if err != nil {
return nil, err
}
if dag.IsInSelectedParentChain(blockHash) {
return blockHash, nil
}
}
return nil, nil
}
// NewTxIndex returns a new instance of an indexer that is used to create a
// mapping of the hashes of all transactions in the blockchain to the respective
// block, location within the block, and size of the transaction.
//
// It implements the Indexer interface which plugs into the IndexManager that in
// turn is used by the blockchain package. This allows the index to be
// seamlessly maintained along with the chain.
func NewTxIndex() *TxIndex {
return &TxIndex{}
}
// DropTxIndex drops the transaction index from the provided database if it
// exists. Since the address index relies on it, the address index will also be
// dropped when it exists.
func DropTxIndex(db database.DB, interrupt <-chan struct{}) error {
err := dropIndex(db, addrIndexKey, addrIndexName, interrupt)
if err != nil {
return err
}
err = dropIndex(db, includingBlocksIndexKey, addrIndexName, interrupt)
if err != nil {
return err
}
return dropIndex(db, acceptingBlocksIndexKey, txIndexName, interrupt)
}
// Recover is invoked when the indexer wasn't turned on for several blocks
// and the indexer needs to close the gaps.
//
// This is part of the Indexer interface.
func (idx *TxIndex) Recover(dbTx database.Tx, currentBlockID, lastKnownBlockID uint64) error {
return errors.Errorf("txindex was turned off for %d blocks and can't be recovered."+
" To resume working drop the txindex with --droptxindex", lastKnownBlockID-currentBlockID)
}

View File

@@ -0,0 +1,144 @@
package indexers
import (
"bytes"
"reflect"
"testing"
"github.com/kaspanet/kaspad/blockdag"
"github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/mining"
"github.com/kaspanet/kaspad/txscript"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/wire"
)
func createTransaction(t *testing.T, value uint64, originTx *wire.MsgTx, outputIndex uint32) *wire.MsgTx {
signatureScript, err := txscript.PayToScriptHashSignatureScript(blockdag.OpTrueScript, nil)
if err != nil {
t.Fatalf("Error creating signature script: %s", err)
}
txIn := &wire.TxIn{
PreviousOutpoint: wire.Outpoint{
TxID: *originTx.TxID(),
Index: outputIndex,
},
Sequence: wire.MaxTxInSequenceNum,
SignatureScript: signatureScript,
}
txOut := wire.NewTxOut(value, blockdag.OpTrueScript)
tx := wire.NewNativeMsgTx(wire.TxVersion, []*wire.TxIn{txIn}, []*wire.TxOut{txOut})
return tx
}
func TestTxIndexConnectBlock(t *testing.T) {
blocks := make(map[daghash.Hash]*util.Block)
txIndex := NewTxIndex()
indexManager := NewManager([]Indexer{txIndex})
params := dagconfig.SimNetParams
params.BlockCoinbaseMaturity = 0
params.K = 1
config := blockdag.Config{
IndexManager: indexManager,
DAGParams: &params,
}
dag, teardown, err := blockdag.DAGSetup("TestTxIndexConnectBlock", config)
if err != nil {
t.Fatalf("TestTxIndexConnectBlock: Failed to setup DAG instance: %v", err)
}
if teardown != nil {
defer teardown()
}
prepareAndProcessBlock := func(parentHashes []*daghash.Hash, transactions []*wire.MsgTx, blockName string) *wire.MsgBlock {
block, err := mining.PrepareBlockForTest(dag, &params, parentHashes, transactions, false)
if err != nil {
t.Fatalf("TestTxIndexConnectBlock: block %v got unexpected error from PrepareBlockForTest: %v", blockName, err)
}
utilBlock := util.NewBlock(block)
blocks[*block.BlockHash()] = utilBlock
isOrphan, delay, err := dag.ProcessBlock(utilBlock, blockdag.BFNoPoWCheck)
if err != nil {
t.Fatalf("TestTxIndexConnectBlock: dag.ProcessBlock got unexpected error for block %v: %v", blockName, err)
}
if delay != 0 {
t.Fatalf("TestTxIndexConnectBlock: block %s "+
"is too far in the future", blockName)
}
if isOrphan {
t.Fatalf("TestTxIndexConnectBlock: block %v was unexpectedly orphan", blockName)
}
return block
}
block1 := prepareAndProcessBlock([]*daghash.Hash{params.GenesisHash}, nil, "1")
block2Tx := createTransaction(t, block1.Transactions[0].TxOut[0].Value, block1.Transactions[0], 0)
block2 := prepareAndProcessBlock([]*daghash.Hash{block1.BlockHash()}, []*wire.MsgTx{block2Tx}, "2")
block3Tx := createTransaction(t, block2.Transactions[0].TxOut[0].Value, block2.Transactions[0], 0)
block3 := prepareAndProcessBlock([]*daghash.Hash{block2.BlockHash()}, []*wire.MsgTx{block3Tx}, "3")
block2TxID := block2Tx.TxID()
block2TxNewAcceptedBlock, err := txIndex.BlockThatAcceptedTx(dag, block2TxID)
if err != nil {
t.Errorf("TestTxIndexConnectBlock: TxAcceptedInBlock: %v", err)
}
block3Hash := block3.BlockHash()
if !block2TxNewAcceptedBlock.IsEqual(block3Hash) {
t.Errorf("TestTxIndexConnectBlock: block2Tx should've "+
"been accepted in block %v but instead got accepted in block %v", block3Hash, block2TxNewAcceptedBlock)
}
block3TxID := block3Tx.TxID()
block3TxNewAcceptedBlock, err := txIndex.BlockThatAcceptedTx(dag, block3TxID)
if err != nil {
t.Errorf("TestTxIndexConnectBlock: TxAcceptedInBlock: %v", err)
}
if !block3TxNewAcceptedBlock.IsEqual(&daghash.ZeroHash) {
t.Errorf("TestTxIndexConnectBlock: block3Tx should've "+
"been accepted by the virtual block but instead got accepted in block %v", block3TxNewAcceptedBlock)
}
block3A := prepareAndProcessBlock([]*daghash.Hash{block2.BlockHash()}, []*wire.MsgTx{block3Tx}, "3A")
block4 := prepareAndProcessBlock([]*daghash.Hash{block3.BlockHash()}, nil, "4")
prepareAndProcessBlock([]*daghash.Hash{block3A.BlockHash(), block4.BlockHash()}, nil, "5")
block2TxAcceptedBlock, err := txIndex.BlockThatAcceptedTx(dag, block2TxID)
if err != nil {
t.Errorf("TestTxIndexConnectBlock: TxAcceptedInBlock: %v", err)
}
block3AHash := block3A.BlockHash()
if !block2TxAcceptedBlock.IsEqual(block3AHash) {
t.Errorf("TestTxIndexConnectBlock: block2Tx should've "+
"been accepted in block %v but instead got accepted in block %v", block3AHash, block2TxAcceptedBlock)
}
region, err := txIndex.TxFirstBlockRegion(block3TxID)
if err != nil {
t.Fatalf("TestTxIndexConnectBlock: no block region was found for block3Tx")
}
regionBlock, ok := blocks[*region.Hash]
if !ok {
t.Fatalf("TestTxIndexConnectBlock: couldn't find block with hash %v", region.Hash)
}
regionBlockBytes, err := regionBlock.Bytes()
if err != nil {
t.Fatalf("TestTxIndexConnectBlock: Couldn't serialize block to bytes")
}
block3TxInBlock := regionBlockBytes[region.Offset : region.Offset+region.Len]
block3TxBuf := bytes.NewBuffer(make([]byte, 0, block3Tx.SerializeSize()))
block3Tx.BtcEncode(block3TxBuf, 0)
blockTxBytes := block3TxBuf.Bytes()
if !reflect.DeepEqual(blockTxBytes, block3TxInBlock) {
t.Errorf("TestTxIndexConnectBlock: the block region that was in the bucket doesn't match block3Tx")
}
}

13
blockdag/log.go Normal file
View File

@@ -0,0 +1,13 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"github.com/kaspanet/kaspad/logger"
"github.com/kaspanet/kaspad/util/panics"
)
var log, _ = logger.Get(logger.SubsystemTags.BDAG)
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
package blockdag
import (
"math"
@@ -134,7 +134,7 @@ func (m *medianTime) AddTimeSample(sourceID string, timeVal time.Time) {
sort.Sort(int64Sorter(sortedOffsets))
offsetDuration := time.Duration(offsetSecs) * time.Second
log.Debugf("Added time sample of %v (total: %v)", offsetDuration,
log.Debugf("Added time sample of %s (total: %d)", offsetDuration,
numOffsets)
// NOTE: The following code intentionally has a bug to mirror the
@@ -190,7 +190,7 @@ func (m *medianTime) AddTimeSample(sourceID string, timeVal time.Time) {
}
medianDuration := time.Duration(m.offsetSecs) * time.Second
log.Debugf("New time offset: %v", medianDuration)
log.Debugf("New time offset: %d", medianDuration)
}
// Offset returns the number of seconds to adjust the local clock based upon the

View File

@@ -1,15 +1,13 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
package blockdag
import (
"strconv"
"testing"
"time"
"github.com/btcsuite/btcd/blockchain"
)
// TestMedianTime tests the medianTime implementation.
@@ -47,7 +45,7 @@ func TestMedianTime(t *testing.T) {
// be ignored.
{in: []int64{-4201, 4202, -4203, 4204, -4205}, wantOffset: 0},
// Excerise the condition where the median offset is greater
// Exercise the condition where the median offset is greater
// than the max allowed adjustment, but there is at least one
// sample that is close enough to the current time to avoid
// triggering a warning about an invalid local clock.
@@ -55,11 +53,11 @@ func TestMedianTime(t *testing.T) {
}
// Modify the max number of allowed median time entries for these tests.
blockchain.TstSetMaxMedianTimeEntries(10)
defer blockchain.TstSetMaxMedianTimeEntries(200)
maxMedianTimeEntries = 10
defer func() { maxMedianTimeEntries = 200 }()
for i, test := range tests {
filter := blockchain.NewMedianTime()
filter := NewMedianTime()
for j, offset := range test.in {
id := strconv.Itoa(j)
now := time.Unix(time.Now().Unix(), 0)

View File

@@ -1,16 +1,24 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
package blockdag
import (
"math"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
// MerkleTree holds the hashes of a merkle tree
type MerkleTree []*daghash.Hash
// Root returns the root of the merkle tree
func (mt MerkleTree) Root() *daghash.Hash {
return mt[len(mt)-1]
}
// nextPowerOfTwo returns the next highest power of two from a given number if
// it is not already a power of two. This is a helper function used during the
// calculation of a merkle tree.
@@ -28,17 +36,37 @@ func nextPowerOfTwo(n int) int {
// HashMerkleBranches takes two hashes, treated as the left and right tree
// nodes, and returns the hash of their concatenation. This is a helper
// function used to aid in the generation of a merkle tree.
func HashMerkleBranches(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash {
func HashMerkleBranches(left *daghash.Hash, right *daghash.Hash) *daghash.Hash {
// Concatenate the left and right nodes.
var sha [wire.HashSize * 2]byte
copy(sha[:wire.HashSize], left[:])
copy(sha[wire.HashSize:], right[:])
var hash [daghash.HashSize * 2]byte
copy(hash[:daghash.HashSize], left[:])
copy(hash[daghash.HashSize:], right[:])
newSha := wire.DoubleSha256SH(sha[:])
return &newSha
newHash := daghash.DoubleHashH(hash[:])
return &newHash
}
// BuildMerkleTreeStore creates a merkle tree from a slice of transactions,
// BuildHashMerkleTreeStore creates a merkle tree from a slice of transactions, based
// on their hash. See `buildMerkleTreeStore` for more info.
func BuildHashMerkleTreeStore(transactions []*util.Tx) MerkleTree {
txHashes := make([]*daghash.Hash, len(transactions))
for i, tx := range transactions {
txHashes[i] = tx.Hash()
}
return buildMerkleTreeStore(txHashes)
}
// BuildIDMerkleTreeStore creates a merkle tree from a slice of transactions, based
// on their ID. See `buildMerkleTreeStore` for more info.
func BuildIDMerkleTreeStore(transactions []*util.Tx) MerkleTree {
txIDs := make([]*daghash.Hash, len(transactions))
for i, tx := range transactions {
txIDs[i] = (*daghash.Hash)(tx.ID())
}
return buildMerkleTreeStore(txIDs)
}
// buildMerkleTreeStore creates a merkle tree from a slice of hashes,
// stores it using a linear array, and returns a slice of the backing array. A
// linear array was chosen as opposed to an actual tree structure since it uses
// about half as much memory. The following describes a merkle tree and how it
@@ -66,16 +94,16 @@ func HashMerkleBranches(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash {
// are calculated by concatenating the left node with itself before hashing.
// Since this function uses nodes that are pointers to the hashes, empty nodes
// will be nil.
func BuildMerkleTreeStore(transactions []*btcutil.Tx) []*wire.ShaHash {
func buildMerkleTreeStore(hashes []*daghash.Hash) MerkleTree {
// Calculate how many entries are required to hold the binary merkle
// tree as a linear array and create an array of that size.
nextPoT := nextPowerOfTwo(len(transactions))
nextPoT := nextPowerOfTwo(len(hashes))
arraySize := nextPoT*2 - 1
merkles := make([]*wire.ShaHash, arraySize)
merkles := make(MerkleTree, arraySize)
// Create the base transaction shas and populate the array with them.
for i, tx := range transactions {
merkles[i] = tx.Sha()
// Create the base transaction hashes and populate the array with them.
for i, hash := range hashes {
merkles[i] = hash
}
// Start the array offset after the last transaction and adjusted to the
@@ -90,14 +118,14 @@ func BuildMerkleTreeStore(transactions []*btcutil.Tx) []*wire.ShaHash {
// When there is no right child, the parent is generated by
// hashing the concatenation of the left child with itself.
case merkles[i+1] == nil:
newSha := HashMerkleBranches(merkles[i], merkles[i])
merkles[offset] = newSha
newHash := HashMerkleBranches(merkles[i], merkles[i])
merkles[offset] = newHash
// The normal case sets the parent node to the double sha256
// of the concatentation of the left and right children.
default:
newSha := HashMerkleBranches(merkles[i], merkles[i+1])
merkles[offset] = newSha
newHash := HashMerkleBranches(merkles[i], merkles[i+1])
merkles[offset] = newHash
}
offset++
}

36
blockdag/merkle_test.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"github.com/kaspanet/kaspad/util/daghash"
"testing"
"github.com/kaspanet/kaspad/util"
)
// TestMerkle tests the BuildHashMerkleTreeStore API.
func TestMerkle(t *testing.T) {
block := util.NewBlock(&Block100000)
hashMerkleTree := BuildHashMerkleTreeStore(block.Transactions())
calculatedHashMerkleRoot := hashMerkleTree.Root()
wantHashMerkleRoot := Block100000.Header.HashMerkleRoot
if !wantHashMerkleRoot.IsEqual(calculatedHashMerkleRoot) {
t.Errorf("BuildHashMerkleTreeStore: hash merkle root mismatch - "+
"got %v, want %v", calculatedHashMerkleRoot, wantHashMerkleRoot)
}
idMerkleTree := BuildIDMerkleTreeStore(block.Transactions())
calculatedIDMerkleRoot := idMerkleTree.Root()
wantIDMerkleRoot, err := daghash.NewHashFromStr("3f69feb7edf5d0d67930afc990c8ec931e3428d7c7a65d7af6b81079319eb110")
if err != nil {
t.Errorf("BuildIDMerkleTreeStore: unexpected error: %s", err)
}
if !calculatedIDMerkleRoot.IsEqual(wantIDMerkleRoot) {
t.Errorf("BuildIDMerkleTreeStore: ID merkle root mismatch - "+
"got %v, want %v", calculatedIDMerkleRoot, wantIDMerkleRoot)
}
}

89
blockdag/notifications.go Normal file
View File

@@ -0,0 +1,89 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"fmt"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
// NotificationType represents the type of a notification message.
type NotificationType int
// NotificationCallback is used for a caller to provide a callback for
// notifications about various chain events.
type NotificationCallback func(*Notification)
// Constants for the type of a notification message.
const (
// NTBlockAdded indicates the associated block was added into
// the blockDAG.
NTBlockAdded NotificationType = iota
// NTChainChanged indicates that selected parent
// chain had changed.
NTChainChanged
)
// notificationTypeStrings is a map of notification types back to their constant
// names for pretty printing.
var notificationTypeStrings = map[NotificationType]string{
NTBlockAdded: "NTBlockAdded",
NTChainChanged: "NTChainChanged",
}
// String returns the NotificationType in human-readable form.
func (n NotificationType) String() string {
if s, ok := notificationTypeStrings[n]; ok {
return s
}
return fmt.Sprintf("Unknown Notification Type (%d)", int(n))
}
// Notification defines notification that is sent to the caller via the callback
// function provided during the call to New and consists of a notification type
// as well as associated data that depends on the type as follows:
// - Added: *util.Block
type Notification struct {
Type NotificationType
Data interface{}
}
// Subscribe to block chain notifications. Registers a callback to be executed
// when various events take place. See the documentation on Notification and
// NotificationType for details on the types and contents of notifications.
func (dag *BlockDAG) Subscribe(callback NotificationCallback) {
dag.notificationsLock.Lock()
dag.notifications = append(dag.notifications, callback)
dag.notificationsLock.Unlock()
}
// sendNotification sends a notification with the passed type and data if the
// caller requested notifications by providing a callback function in the call
// to New.
func (dag *BlockDAG) sendNotification(typ NotificationType, data interface{}) {
// Generate and send the notification.
n := Notification{Type: typ, Data: data}
dag.notificationsLock.RLock()
for _, callback := range dag.notifications {
callback(&n)
}
dag.notificationsLock.RUnlock()
}
// BlockAddedNotificationData defines data to be sent along with a BlockAdded
// notification
type BlockAddedNotificationData struct {
Block *util.Block
WasUnorphaned bool
}
// ChainChangedNotificationData defines data to be sent along with a ChainChanged
// notification
type ChainChangedNotificationData struct {
RemovedChainBlockHashes []*daghash.Hash
AddedChainBlockHashes []*daghash.Hash
}

View File

@@ -0,0 +1,61 @@
// Copyright (c) 2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"path/filepath"
"testing"
"github.com/kaspanet/kaspad/dagconfig"
)
// TestNotifications ensures that notification callbacks are fired on events.
func TestNotifications(t *testing.T) {
blocks, err := LoadBlocks(filepath.Join("testdata/blk_0_to_4.dat"))
if err != nil {
t.Fatalf("Error loading file: %v\n", err)
}
// Create a new database and dag instance to run tests against.
dag, teardownFunc, err := DAGSetup("notifications", Config{
DAGParams: &dagconfig.SimNetParams,
})
if err != nil {
t.Fatalf("Failed to setup dag instance: %v", err)
}
defer teardownFunc()
notificationCount := 0
callback := func(notification *Notification) {
if notification.Type == NTBlockAdded {
notificationCount++
}
}
// Register callback multiple times then assert it is called that many
// times.
const numSubscribers = 3
for i := 0; i < numSubscribers; i++ {
dag.Subscribe(callback)
}
isOrphan, delay, err := dag.ProcessBlock(blocks[1], BFNone)
if err != nil {
t.Fatalf("ProcessBlock fail on block 1: %v\n", err)
}
if delay != 0 {
t.Fatalf("ProcessBlock: block 1 " +
"is too far in the future")
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block " +
"is an orphan\n")
}
if notificationCount != numSubscribers {
t.Fatalf("Expected notification callback to be executed %d "+
"times, found %d", numSubscribers, notificationCount)
}
}

105
blockdag/phantom.go Normal file
View File

@@ -0,0 +1,105 @@
package blockdag
import (
"github.com/kaspanet/kaspad/util/daghash"
)
// phantom calculates and returns the block's blue set, selected parent and blue score.
// Chain start is determined by going down the DAG through the selected path
// (follow the selected parent of each block) k + 1 steps.
// The blue set of a block are all blue blocks in its past.
// To optimize memory usage, for each block we are storing only the blue blocks in
// its selected parent's anticone that are in the future of the chain start
// as well as the selected parent itself - the rest of the
// blue set can be restored by traversing the selected parent chain and combining
// the .blues of all blocks in it.
// The blue score is the total number of blocks in this block's blue set
// of the selected parent. (the blue score of the genesis block is defined as 0)
// The selected parent is chosen by determining which block's parent will give this block the highest blue score.
func phantom(block *blockNode, k uint32) (blues []*blockNode, selectedParent *blockNode, score uint64) {
bestScore := uint64(0)
var bestParent *blockNode
var bestBlues []*blockNode
var bestHash *daghash.Hash
for _, parent := range block.parents {
chainStart := digToChainStart(parent, k)
candidates := blueCandidates(chainStart)
blues := traverseCandidates(block, candidates, parent)
score := uint64(len(blues)) + parent.blueScore
if score > bestScore || (score == bestScore && (bestHash == nil || daghash.Less(parent.hash, bestHash))) {
bestScore = score
bestBlues = blues
bestParent = parent
bestHash = parent.hash
}
}
return bestBlues, bestParent, bestScore
}
// digToChainStart digs through the selected path and returns the block in depth k+1
func digToChainStart(parent *blockNode, k uint32) *blockNode {
current := parent
for i := uint32(0); i < k; i++ {
if current.isGenesis() {
break
}
current = current.selectedParent
}
return current
}
func blueCandidates(chainStart *blockNode) blockSet {
candidates := newSet()
candidates.add(chainStart)
queue := []*blockNode{chainStart}
for len(queue) > 0 {
var current *blockNode
current, queue = queue[0], queue[1:]
children := current.children
for _, child := range children {
if !candidates.contains(child) {
candidates.add(child)
queue = append(queue, child)
}
}
}
return candidates
}
//traverseCandidates returns all the blocks that are in the future of the chain start and in the anticone of the selected parent
func traverseCandidates(newBlock *blockNode, candidates blockSet, selectedParent *blockNode) []*blockNode {
blues := []*blockNode{}
selectedParentPast := newSet()
queue := newDownHeap()
visited := newSet()
for _, parent := range newBlock.parents {
queue.Push(parent)
}
for queue.Len() > 0 {
current := queue.pop()
if candidates.contains(current) {
if current == selectedParent || selectedParentPast.anyChildInSet(current) {
selectedParentPast.add(current)
} else {
blues = append(blues, current)
}
for _, parent := range current.parents {
if !visited.contains(parent) {
visited.add(parent)
queue.Push(parent)
}
}
}
}
return append(blues, selectedParent)
}

892
blockdag/phantom_test.go Normal file
View File

@@ -0,0 +1,892 @@
package blockdag
import (
"fmt"
"reflect"
"sort"
"testing"
"time"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/dagconfig"
)
type testBlockData struct {
parents []string
id string //id is a virtual entity that is used only for tests so we can define relations between blocks without knowing their hash
expectedScore uint64
expectedSelectedParent string
expectedBlues []string
}
//TestPhantom iterate over several dag simulations, and checks
//that the blue score, blue set and selected parent of each
//block calculated as expected
func TestPhantom(t *testing.T) {
netParams := dagconfig.SimNetParams
blockVersion := int32(0x10000000)
tests := []struct {
k uint32
dagData []*testBlockData
virtualBlockID string
expectedReds []string
}{
{
//Block hash order:AKJIHGFEDCB
k: 1,
virtualBlockID: "K",
expectedReds: []string{"D"},
dagData: []*testBlockData{
{
parents: []string{"A"},
id: "B",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"A"},
id: "C",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"B"},
id: "D",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"B"},
id: "E",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"C"},
id: "F",
expectedScore: 2,
expectedSelectedParent: "C",
expectedBlues: []string{"C"},
},
{
parents: []string{"C", "D"},
id: "G",
expectedScore: 4,
expectedSelectedParent: "C",
expectedBlues: []string{"D", "B", "C"},
},
{
parents: []string{"C", "E"},
id: "H",
expectedScore: 4,
expectedSelectedParent: "C",
expectedBlues: []string{"E", "B", "C"},
},
{
parents: []string{"E", "G"},
id: "I",
expectedScore: 5,
expectedSelectedParent: "E",
expectedBlues: []string{"G", "D", "E"},
},
{
parents: []string{"F"},
id: "J",
expectedScore: 3,
expectedSelectedParent: "F",
expectedBlues: []string{"F"},
},
{
parents: []string{"H", "I", "J"},
id: "K",
expectedScore: 9,
expectedSelectedParent: "H",
expectedBlues: []string{"I", "G", "J", "F", "H"},
},
},
},
{
//block hash order:AVUTSRQPONMLKJIHGFEDCB
k: 2,
virtualBlockID: "V",
expectedReds: []string{"D", "J", "P"},
dagData: []*testBlockData{
{
parents: []string{"A"},
id: "B",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"A"},
id: "C",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"B"},
id: "D",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"B"},
id: "E",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"C"},
id: "F",
expectedScore: 2,
expectedSelectedParent: "C",
expectedBlues: []string{"C"},
},
{
parents: []string{"C"},
id: "G",
expectedScore: 2,
expectedSelectedParent: "C",
expectedBlues: []string{"C"},
},
{
parents: []string{"G"},
id: "H",
expectedScore: 3,
expectedSelectedParent: "G",
expectedBlues: []string{"G"},
},
{
parents: []string{"E"},
id: "I",
expectedScore: 3,
expectedSelectedParent: "E",
expectedBlues: []string{"E"},
},
{
parents: []string{"E"},
id: "J",
expectedScore: 3,
expectedSelectedParent: "E",
expectedBlues: []string{"E"},
},
{
parents: []string{"I"},
id: "K",
expectedScore: 4,
expectedSelectedParent: "I",
expectedBlues: []string{"I"},
},
{
parents: []string{"K", "H"},
id: "L",
expectedScore: 5,
expectedSelectedParent: "K",
expectedBlues: []string{"K"},
},
{
parents: []string{"F", "L"},
id: "M",
expectedScore: 10,
expectedSelectedParent: "F",
expectedBlues: []string{"L", "K", "I", "H", "G", "E", "B", "F"},
},
{
parents: []string{"G", "K"},
id: "N",
expectedScore: 7,
expectedSelectedParent: "G",
expectedBlues: []string{"K", "I", "E", "B", "G"},
},
{
parents: []string{"J", "N"},
id: "O",
expectedScore: 8,
expectedSelectedParent: "N",
expectedBlues: []string{"N"},
},
{
parents: []string{"D"},
id: "P",
expectedScore: 3,
expectedSelectedParent: "D",
expectedBlues: []string{"D"},
},
{
parents: []string{"O", "P"},
id: "Q",
expectedScore: 10,
expectedSelectedParent: "P",
expectedBlues: []string{"O", "N", "K", "J", "I", "E", "P"},
},
{
parents: []string{"L", "Q"},
id: "R",
expectedScore: 11,
expectedSelectedParent: "Q",
expectedBlues: []string{"Q"},
},
{
parents: []string{"M", "R"},
id: "S",
expectedScore: 15,
expectedSelectedParent: "M",
expectedBlues: []string{"R", "Q", "O", "N", "M"},
},
{
parents: []string{"H", "F"},
id: "T",
expectedScore: 5,
expectedSelectedParent: "F",
expectedBlues: []string{"H", "G", "F"},
},
{
parents: []string{"M", "T"},
id: "U",
expectedScore: 12,
expectedSelectedParent: "M",
expectedBlues: []string{"T", "M"},
},
{
parents: []string{"S", "U"},
id: "V",
expectedScore: 18,
expectedSelectedParent: "S",
expectedBlues: []string{"U", "T", "S"},
},
},
},
{
//Block hash order:AXWVUTSRQPONMLKJIHGFEDCB
k: 1,
virtualBlockID: "X",
expectedReds: []string{"D", "F", "G", "H", "J", "K", "L", "N", "O", "Q", "R", "S", "U", "V"},
dagData: []*testBlockData{
{
parents: []string{"A"},
id: "B",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"A"},
id: "C",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"A"},
id: "D",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"A"},
id: "E",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"B"},
id: "F",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"B"},
id: "G",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"C"},
id: "H",
expectedScore: 2,
expectedSelectedParent: "C",
expectedBlues: []string{"C"},
},
{
parents: []string{"C"},
id: "I",
expectedScore: 2,
expectedSelectedParent: "C",
expectedBlues: []string{"C"},
},
{
parents: []string{"B"},
id: "J",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"D"},
id: "K",
expectedScore: 2,
expectedSelectedParent: "D",
expectedBlues: []string{"D"},
},
{
parents: []string{"D"},
id: "L",
expectedScore: 2,
expectedSelectedParent: "D",
expectedBlues: []string{"D"},
},
{
parents: []string{"E"},
id: "M",
expectedScore: 2,
expectedSelectedParent: "E",
expectedBlues: []string{"E"},
},
{
parents: []string{"E"},
id: "N",
expectedScore: 2,
expectedSelectedParent: "E",
expectedBlues: []string{"E"},
},
{
parents: []string{"F", "G", "J"},
id: "O",
expectedScore: 5,
expectedSelectedParent: "F",
expectedBlues: []string{"J", "G", "F"},
},
{
parents: []string{"B", "M", "I"},
id: "P",
expectedScore: 6,
expectedSelectedParent: "B",
expectedBlues: []string{"M", "I", "E", "C", "B"},
},
{
parents: []string{"K", "E"},
id: "Q",
expectedScore: 4,
expectedSelectedParent: "E",
expectedBlues: []string{"K", "D", "E"},
},
{
parents: []string{"L", "N"},
id: "R",
expectedScore: 3,
expectedSelectedParent: "L",
expectedBlues: []string{"L"},
},
{
parents: []string{"I", "Q"},
id: "S",
expectedScore: 5,
expectedSelectedParent: "Q",
expectedBlues: []string{"Q"},
},
{
parents: []string{"K", "P"},
id: "T",
expectedScore: 7,
expectedSelectedParent: "P",
expectedBlues: []string{"P"},
},
{
parents: []string{"K", "L"},
id: "U",
expectedScore: 4,
expectedSelectedParent: "K",
expectedBlues: []string{"L", "K"},
},
{
parents: []string{"U", "R"},
id: "V",
expectedScore: 5,
expectedSelectedParent: "R",
expectedBlues: []string{"U", "R"},
},
{
parents: []string{"S", "U", "T"},
id: "W",
expectedScore: 8,
expectedSelectedParent: "T",
expectedBlues: []string{"T"},
},
{
parents: []string{"V", "W", "H"},
id: "X",
expectedScore: 9,
expectedSelectedParent: "W",
expectedBlues: []string{"W"},
},
},
},
{
//Secret mining attack: The attacker is mining
//blocks B,C,D,E,F,G,T in secret without propagating
//them, so all blocks except T should be red, because
//they don't follow the rules of PHANTOM that require
//you to point to all the parents that you know, and
//propagate your block as soon as it's mined
//Block hash order:AYXWVUTSRQPONMLKJIHGFEDCB
k: 1,
virtualBlockID: "Y",
expectedReds: []string{"B", "C", "D", "E", "F", "G", "L"},
dagData: []*testBlockData{
{
parents: []string{"A"},
id: "B",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"B"},
id: "C",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"C"},
id: "D",
expectedScore: 3,
expectedSelectedParent: "C",
expectedBlues: []string{"C"},
},
{
parents: []string{"D"},
id: "E",
expectedScore: 4,
expectedSelectedParent: "D",
expectedBlues: []string{"D"},
},
{
parents: []string{"E"},
id: "F",
expectedScore: 5,
expectedSelectedParent: "E",
expectedBlues: []string{"E"},
},
{
parents: []string{"F"},
id: "G",
expectedScore: 6,
expectedSelectedParent: "F",
expectedBlues: []string{"F"},
},
{
parents: []string{"A"},
id: "H",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"A"},
id: "I",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"H", "I"},
id: "J",
expectedScore: 3,
expectedSelectedParent: "H",
expectedBlues: []string{"I", "H"},
},
{
parents: []string{"H", "I"},
id: "K",
expectedScore: 3,
expectedSelectedParent: "H",
expectedBlues: []string{"I", "H"},
},
{
parents: []string{"I"},
id: "L",
expectedScore: 2,
expectedSelectedParent: "I",
expectedBlues: []string{"I"},
},
{
parents: []string{"J", "K", "L"},
id: "M",
expectedScore: 5,
expectedSelectedParent: "J",
expectedBlues: []string{"K", "J"},
},
{
parents: []string{"J", "K", "L"},
id: "N",
expectedScore: 5,
expectedSelectedParent: "J",
expectedBlues: []string{"K", "J"},
},
{
parents: []string{"N", "M"},
id: "O",
expectedScore: 7,
expectedSelectedParent: "M",
expectedBlues: []string{"N", "M"},
},
{
parents: []string{"N", "M"},
id: "P",
expectedScore: 7,
expectedSelectedParent: "M",
expectedBlues: []string{"N", "M"},
},
{
parents: []string{"N", "M"},
id: "Q",
expectedScore: 7,
expectedSelectedParent: "M",
expectedBlues: []string{"N", "M"},
},
{
parents: []string{"O", "P", "Q"},
id: "R",
expectedScore: 10,
expectedSelectedParent: "O",
expectedBlues: []string{"Q", "P", "O"},
},
{
parents: []string{"O", "P", "Q"},
id: "S",
expectedScore: 10,
expectedSelectedParent: "O",
expectedBlues: []string{"Q", "P", "O"},
},
{
parents: []string{"G", "S", "R"},
id: "T",
expectedScore: 12,
expectedSelectedParent: "R",
expectedBlues: []string{"S", "R"},
},
{
parents: []string{"S", "R"},
id: "U",
expectedScore: 12,
expectedSelectedParent: "R",
expectedBlues: []string{"S", "R"},
},
{
parents: []string{"T", "U"},
id: "V",
expectedScore: 14,
expectedSelectedParent: "T",
expectedBlues: []string{"U", "T"},
},
{
parents: []string{"T", "U"},
id: "W",
expectedScore: 14,
expectedSelectedParent: "T",
expectedBlues: []string{"U", "T"},
},
{
parents: []string{"U", "T"},
id: "X",
expectedScore: 14,
expectedSelectedParent: "T",
expectedBlues: []string{"U", "T"},
},
{
parents: []string{"V", "W", "X"},
id: "Y",
expectedScore: 17,
expectedSelectedParent: "V",
expectedBlues: []string{"X", "W", "V"},
},
},
},
{
//Censorship mining attack: The attacker is mining blocks B,C,D,E,F,G in secret without propagating them,
//so all blocks except B and C should be red, because they don't follow the rules of
//PHANTOM that require you to point to all the parents that you know
//Block hash order:AYXWVUTSRQPONMLKJIHGFEDCB
k: 1,
virtualBlockID: "Y",
expectedReds: []string{"D", "E", "F", "G", "L"},
dagData: []*testBlockData{
{
parents: []string{"A"},
id: "B",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"B"},
id: "C",
expectedScore: 2,
expectedSelectedParent: "B",
expectedBlues: []string{"B"},
},
{
parents: []string{"C"},
id: "D",
expectedScore: 3,
expectedSelectedParent: "C",
expectedBlues: []string{"C"},
},
{
parents: []string{"D"},
id: "E",
expectedScore: 4,
expectedSelectedParent: "D",
expectedBlues: []string{"D"},
},
{
parents: []string{"E"},
id: "F",
expectedScore: 5,
expectedSelectedParent: "E",
expectedBlues: []string{"E"},
},
{
parents: []string{"F"},
id: "G",
expectedScore: 6,
expectedSelectedParent: "F",
expectedBlues: []string{"F"},
},
{
parents: []string{"A"},
id: "H",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"A"},
id: "I",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"H", "I", "B"},
id: "J",
expectedScore: 4,
expectedSelectedParent: "B",
expectedBlues: []string{"I", "H", "B"},
},
{
parents: []string{"H", "I", "B"},
id: "K",
expectedScore: 4,
expectedSelectedParent: "B",
expectedBlues: []string{"I", "H", "B"},
},
{
parents: []string{"I"},
id: "L",
expectedScore: 2,
expectedSelectedParent: "I",
expectedBlues: []string{"I"},
},
{
parents: []string{"J", "K", "L", "C"},
id: "M",
expectedScore: 7,
expectedSelectedParent: "J",
expectedBlues: []string{"K", "C", "J"},
},
{
parents: []string{"J", "K", "L", "C"},
id: "N",
expectedScore: 7,
expectedSelectedParent: "J",
expectedBlues: []string{"K", "C", "J"},
},
{
parents: []string{"N", "M", "D"},
id: "O",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"N", "M"},
},
{
parents: []string{"N", "M", "D"},
id: "P",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"N", "M"},
},
{
parents: []string{"N", "M", "D"},
id: "Q",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"N", "M"},
},
{
parents: []string{"O", "P", "Q", "E"},
id: "R",
expectedScore: 12,
expectedSelectedParent: "O",
expectedBlues: []string{"Q", "P", "O"},
},
{
parents: []string{"O", "P", "Q", "E"},
id: "S",
expectedScore: 12,
expectedSelectedParent: "O",
expectedBlues: []string{"Q", "P", "O"},
},
{
parents: []string{"G", "S", "R"},
id: "T",
expectedScore: 14,
expectedSelectedParent: "R",
expectedBlues: []string{"S", "R"},
},
{
parents: []string{"S", "R", "F"},
id: "U",
expectedScore: 14,
expectedSelectedParent: "R",
expectedBlues: []string{"S", "R"},
},
{
parents: []string{"T", "U"},
id: "V",
expectedScore: 16,
expectedSelectedParent: "T",
expectedBlues: []string{"U", "T"},
},
{
parents: []string{"T", "U"},
id: "W",
expectedScore: 16,
expectedSelectedParent: "T",
expectedBlues: []string{"U", "T"},
},
{
parents: []string{"T", "U"},
id: "X",
expectedScore: 16,
expectedSelectedParent: "T",
expectedBlues: []string{"U", "T"},
},
{
parents: []string{"V", "W", "X"},
id: "Y",
expectedScore: 19,
expectedSelectedParent: "V",
expectedBlues: []string{"X", "W", "V"},
},
},
},
}
for i, test := range tests {
netParams.K = test.k
// Generate enough synthetic blocks for the rest of the test
blockDAG := newTestDAG(&netParams)
genesisNode := blockDAG.genesis
blockTime := genesisNode.Header().Timestamp
blockByIDMap := make(map[string]*blockNode)
idByBlockMap := make(map[*blockNode]string)
blockByIDMap["A"] = genesisNode
idByBlockMap[genesisNode] = "A"
for _, blockData := range test.dagData {
blockTime = blockTime.Add(time.Second)
parents := blockSet{}
for _, parentID := range blockData.parents {
parent := blockByIDMap[parentID]
parents.add(parent)
}
node := newTestNode(parents, blockVersion, 0, blockTime, test.k)
node.hash = &daghash.Hash{} //It helps to predict hash order
for i, char := range blockData.id {
node.hash[i] = byte(char)
}
blockDAG.index.AddNode(node)
addNodeAsChildToParents(node)
blockByIDMap[blockData.id] = node
idByBlockMap[node] = blockData.id
bluesIDs := make([]string, 0, len(node.blues))
for _, blue := range node.blues {
bluesIDs = append(bluesIDs, idByBlockMap[blue])
}
selectedParentID := idByBlockMap[node.selectedParent]
fullDataStr := fmt.Sprintf("blues: %v, selectedParent: %v, score: %v",
bluesIDs, selectedParentID, node.blueScore)
if blockData.expectedScore != node.blueScore {
t.Errorf("Test %d: Block %v expected to have score %v but got %v (fulldata: %v)",
i, blockData.id, blockData.expectedScore, node.blueScore, fullDataStr)
}
if blockData.expectedSelectedParent != selectedParentID {
t.Errorf("Test %d: Block %v expected to have selected parent %v but got %v (fulldata: %v)",
i, blockData.id, blockData.expectedSelectedParent, selectedParentID, fullDataStr)
}
if !reflect.DeepEqual(blockData.expectedBlues, bluesIDs) {
t.Errorf("Test %d: Block %v expected to have blues %v but got %v (fulldata: %v)",
i, blockData.id, blockData.expectedBlues, bluesIDs, fullDataStr)
}
}
reds := make(map[string]bool)
for id := range blockByIDMap {
reds[id] = true
}
for tip := blockByIDMap[test.virtualBlockID]; tip.selectedParent != nil; tip = tip.selectedParent {
tipID := idByBlockMap[tip]
delete(reds, tipID)
for _, blue := range tip.blues {
blueID := idByBlockMap[blue]
delete(reds, blueID)
}
}
if !checkReds(test.expectedReds, reds) {
redsIDs := make([]string, 0, len(reds))
for id := range reds {
redsIDs = append(redsIDs, id)
}
sort.Strings(redsIDs)
sort.Strings(test.expectedReds)
t.Errorf("Test %d: Expected reds %v but got %v", i, test.expectedReds, redsIDs)
}
}
}
func checkReds(expectedReds []string, reds map[string]bool) bool {
if len(expectedReds) != len(reds) {
return false
}
for _, redID := range expectedReds {
if !reds[redID] {
return false
}
}
return true
}

219
blockdag/process.go Normal file
View File

@@ -0,0 +1,219 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"fmt"
"time"
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
)
// BehaviorFlags is a bitmask defining tweaks to the normal behavior when
// performing DAG processing and consensus rules checks.
type BehaviorFlags uint32
const (
// BFFastAdd may be set to indicate that several checks can be avoided
// for the block since it is already known to fit into the chain due to
// already proving it correct links into the chain up to a known
// checkpoint. This is primarily used for headers-first mode.
BFFastAdd BehaviorFlags = 1 << iota
// BFNoPoWCheck may be set to indicate the proof of work check which
// ensures a block hashes to a value less than the required target will
// not be performed.
BFNoPoWCheck
// BFWasUnorphaned may be set to indicate that a block was just now
// unorphaned
BFWasUnorphaned
// BFAfterDelay may be set to indicate that a block had timestamp too far
// in the future, just finished the delay
BFAfterDelay
// BFIsSync may be set to indicate that the block was sent as part of the
// netsync process
BFIsSync
// BFWasStored is set to indicate that the block was previously stored
// in the block index but was never fully processed
BFWasStored
// BFNone is a convenience value to specifically indicate no flags.
BFNone BehaviorFlags = 0
)
// BlockExists determines whether a block with the given hash exists in
// the DAG.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) BlockExists(hash *daghash.Hash) bool {
return dag.index.HaveBlock(hash)
}
// processOrphans determines if there are any orphans which depend on the passed
// block hash (they are no longer orphans if true) and potentially accepts them.
// It repeats the process for the newly accepted blocks (to detect further
// orphans which may no longer be orphans) until there are no more.
//
// The flags do not modify the behavior of this function directly, however they
// are needed to pass along to maybeAcceptBlock.
//
// This function MUST be called with the chain state lock held (for writes).
func (dag *BlockDAG) processOrphans(hash *daghash.Hash, flags BehaviorFlags) error {
// Start with processing at least the passed hash. Leave a little room
// for additional orphan blocks that need to be processed without
// needing to grow the array in the common case.
processHashes := make([]*daghash.Hash, 0, 10)
processHashes = append(processHashes, hash)
for len(processHashes) > 0 {
// Pop the first hash to process from the slice.
processHash := processHashes[0]
processHashes[0] = nil // Prevent GC leak.
processHashes = processHashes[1:]
// Look up all orphans that are parented by the block we just
// accepted. This will typically only be one, but it could
// be multiple if multiple blocks are mined and broadcast
// around the same time. The one with the most proof of work
// will eventually win out. An indexing for loop is
// intentionally used over a range here as range does not
// reevaluate the slice on each iteration nor does it adjust the
// index for the modified slice.
for i := 0; i < len(dag.prevOrphans[*processHash]); i++ {
orphan := dag.prevOrphans[*processHash][i]
if orphan == nil {
log.Warnf("Found a nil entry at index %d in the "+
"orphan dependency list for block %s", i,
processHash)
continue
}
// Skip this orphan if one or more of its parents are
// still missing.
_, err := lookupParentNodes(orphan.block, dag)
if err != nil {
if ruleErr, ok := err.(RuleError); ok && ruleErr.ErrorCode == ErrParentBlockUnknown {
continue
}
return err
}
// Remove the orphan from the orphan pool.
orphanHash := orphan.block.Hash()
dag.removeOrphanBlock(orphan)
i--
// Potentially accept the block into the block DAG.
err = dag.maybeAcceptBlock(orphan.block, flags|BFWasUnorphaned)
if err != nil {
// Since we don't want to reject the original block because of
// a bad unorphaned child, only return an error if it's not a RuleError.
if _, ok := err.(RuleError); !ok {
return err
}
log.Warnf("Verification failed for orphan block %s: %s", orphanHash, err)
}
// Add this block to the list of blocks to process so
// any orphan blocks that depend on this block are
// handled too.
processHashes = append(processHashes, orphanHash)
}
}
return nil
}
// ProcessBlock is the main workhorse for handling insertion of new blocks into
// the block chain. It includes functionality such as rejecting duplicate
// blocks, ensuring blocks follow all rules, orphan handling, and insertion into
// the block DAG.
//
// When no errors occurred during processing, the first return value indicates
// whether or not the block is an orphan.
//
// This function is safe for concurrent access.
func (dag *BlockDAG) ProcessBlock(block *util.Block, flags BehaviorFlags) (isOrphan bool, delay time.Duration, err error) {
dag.dagLock.Lock()
defer dag.dagLock.Unlock()
isDelayedBlock := flags&BFAfterDelay == BFAfterDelay
wasBlockStored := flags&BFWasStored == BFWasStored
blockHash := block.Hash()
log.Tracef("Processing block %s", blockHash)
// The block must not already exist in the DAG.
if dag.BlockExists(blockHash) && !wasBlockStored {
str := fmt.Sprintf("already have block %s", blockHash)
return false, 0, ruleError(ErrDuplicateBlock, str)
}
// The block must not already exist as an orphan.
if _, exists := dag.orphans[*blockHash]; exists {
str := fmt.Sprintf("already have block (orphan) %s", blockHash)
return false, 0, ruleError(ErrDuplicateBlock, str)
}
if !isDelayedBlock {
// Perform preliminary sanity checks on the block and its transactions.
delay, err := dag.checkBlockSanity(block, flags)
if err != nil {
return false, 0, err
}
if delay != 0 {
return false, delay, err
}
}
// Handle orphan blocks.
allParentsExist := true
for _, parentHash := range block.MsgBlock().Header.ParentHashes {
if !dag.BlockExists(parentHash) {
allParentsExist = false
}
}
if !allParentsExist {
// Some orphans during netsync are a normal part of the process, since the anticone
// of the chain-split is never explicitly requested.
// Therefore, if we are during netsync - don't report orphans to default logs.
//
// The number K*2 was chosen since in peace times anticone is limited to K blocks,
// while some red block can make it a bit bigger, but much more than that indicates
// there might be some problem with the netsync process.
if flags&BFIsSync == BFIsSync && uint32(len(dag.orphans)) < dag.dagParams.K*2 {
log.Debugf("Adding orphan block %s. This is normal part of netsync process", blockHash)
} else {
log.Infof("Adding orphan block %s", blockHash)
}
dag.addOrphanBlock(block)
return true, 0, nil
}
// The block has passed all context independent checks and appears sane
// enough to potentially accept it into the block DAG.
err = dag.maybeAcceptBlock(block, flags)
if err != nil {
return false, 0, err
}
// Accept any orphan blocks that depend on this block (they are
// no longer orphans) and repeat for those accepted blocks until
// there are no more.
err = dag.processOrphans(blockHash, flags)
if err != nil {
return false, 0, err
}
log.Debugf("Accepted block %s", blockHash)
return false, 0, nil
}

Some files were not shown because too many files have changed in this diff Show More