Sentientd API
Sentient network uses semantic versioning.
API calls return either JSON or no content. Success is indicated by 2xx HTTP status codes, while errors are indicated by 4xx and 5xx HTTP status codes. If an endpoint does not specify its expected status code refer to #standard-responses.
There may be functional API calls which are not documented. These are not guaranteed to be supported in the current release, and should be used at your own risk.
Notes:
- Requests must set their User-Agent string to contain the substring "Sentient-Agent".
- By default, sentientd listens on "localhost:9910". This can be changed using the
--api-addr
flag when running sentientd, as well as using--addr
flag when connecting to it viasentientc
- Do not bind or expose the API to a non-loopback address unless you are aware of the possible dangers.
Example GET curl call:
curl -A "Sentient-Agent" "localhost:9910/wallet/transactions?startheight=1&endheight=250"
Example POST curl call:
curl -A "Sentient-Agent" --data "amount=123&destination=abcd" "localhost:9910/wallet/sen"
Standard responses
Success
The standard response indicating the request was successfully processed is HTTP
status code 204 No Content
. If the request was successfully processed and the
server responded with JSON the HTTP status code is 200 OK
. Specific endpoints
may specify other 2xx status codes on success.
Error
The standard error response indicating the request failed for any reason, is a 4xx or 5xx HTTP status code with an error JSON object describing the error.
{
"message": String
// There may be additional fields depending on the specific error.
}
Authentication
API authentication can be enabled with the --authenticate-api
flag when launching sentientd
.
Authentication is HTTP Basic Authentication as described in
RFC 2617, however, the username is the
empty string. The flag does not enforce authentication on all API endpoints.
Only endpoints that expose sensitive information or modify state require
authentication. See https://github.com/consensus-ai/sen-node/blob/master/node/api/routes.go
for more details on which endpoins require a password.
For example, if the API password is "foobar" the request header should include
Authorization: Basic OmZvb2Jhcg==
Units
Unless otherwise specified, all parameters should be specified in their smallest possible unit. For example, size should always be specified in bytes and Sen should be specified in Hastings. JSON values returned by the API will also use the smallest possible unit, unless otherwise specified.
If a numbers is returned as a string in JSON, it should be treated as an arbitrary-precision number (bignum), and it should be parsed with your language's corresponding bignum library. Currency values are the most common example where this is necessary.
Table of contents
Daemon
Route | HTTP verb |
---|---|
/daemon/constants | GET |
/daemon/stop | GET |
/daemon/version | GET |
/daemon/constants [GET]
returns the set of constants in use.
JSON Response
{
"blockfrequency": 600, // seconds per block
"blocksizelimit": 2000000, // bytes
"extremefuturethreshold": 10800, // seconds
"futurethreshold": 10800, // seconds
"genesistimestamp": 1257894000, // Unix time
"maturitydelay": 144, // blocks
"mediantimestampwindow": 11, // blocks
"targetwindow": 1000, // blocks
"initialcoinbase": 300000, // Sen
"minimumcoinbase": 30000, // Sen
"roottarget": [0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"rootdepth": [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255],
"senprecision": "1000000000000000000000000" // hastings per sen
}
/daemon/stop [GET]
cleanly shuts down the daemon. May take a few seconds.
Response
standard success or error response. See #standard-responses.
/daemon/version [GET]
returns information about the version of sentientd
currently running.
JSON Response
{
"version": "0.0.1",
"gitrevision":"805782f",
"buildtime":"Fri Dec 27 18:21:47 GMT 2019",
"apiversion":"1.0.0",
"tipblockversion":"0.0"
}
Consensus
Route | HTTP verb |
---|---|
/consensus | GET |
/consensus/blocks | GET |
/consensus/validate/transactionset | POST |
/consensus [GET]
returns information about the consensus set, such as the current block height.
JSON Response
{
"synced": true,
"height": 62248,
"currentblock": "00000000000008a84884ba827bdc868a17ba9c14011de33ff763bd95779a9cf1",
"target": [0,0,0,0,0,0,11,48,125,79,116,89,136,74,42,27,5,14,10,31,23,53,226,238,202,219,5,204,38,32,59,165],
"difficulty": "1234"
}
/consensus/blocks [GET]
Returns the block for a given id or height.
Query String Parameters
One of the following parameters can be specified.
// BlockID of the requested block.
id
// BlockHeight of the requested block.
height
JSON Response
e.g.
{
"parentid": "77cbc9fadf16cae799da5a1104e73f336ac19b293edc11c219e7e739c4c3bf76",
"nonce": [48,21,0,0,0,0,0,0],
"timestamp": 1527536185,
"minerpayouts": [{
"value": "299000000000000000000000000",
"unlockhash": "2c14262c4d6ff634007f52a88d9a01008dd85506f5d93c37d55775bf63009114a601112a211a"
}],
"transactions": [
{
"seninputs": [],
"senoutputs": [],
"minerfees": [],
"arbitrarydata": [
"Tm9uU2lhAAAAAAAAAAAAADdYH5bfNtLb+vFn02P/V7Y="
],
"transactionsignatures": []
}
]
}
/consensus/validate/transactionset [POST]
validates a set of transactions using the current utxo set.
Request Body Bytes
Since transactions may be large, the transaction set is supplied in the POST body, encoded in JSON format.
Response
standard success or error response. See #standard-responses.
Gateway
Route | HTTP verb |
---|---|
/gateway | GET |
/gateway/connect/:netaddress | POST |
/gateway/disconnect/:netaddress | POST |
/gateway [GET]
returns information about the gateway, including the list of connected peers.
JSON Response
e.g:
{
"netaddress": "76.9.206.134:9911",
"peers": [{
"inbound": false,
"local": false,
"netaddress": "52.14.115.136:9911",
"version": "0.0.1"
}]
}
/gateway/connect/:netaddress [POST]
connects the gateway to a peer. The peer is added to the node list if it is not already present. The node list is the list of all nodes the gateway knows about, but is not necessarily connected to.
Path Parameters
:netaddress
Response
standard success or error response. See #standard-responses.
/gateway/disconnect/:netaddress [POST]
disconnects the gateway from a peer. The peer remains in the node list.
Path Parameters
:netaddress
Response
standard success or error response. See #standard-responses.
Miner
Route | HTTP verb |
---|---|
/miner | GET |
/miner/start | GET |
/miner/stop | GET |
/miner/header | GET |
/miner/header | POST |
/miner [GET]
returns the status of the CPU miner.
JSON Response
{
"blocksmined": 9001,
"cpuhashrate": 1337,
"cpumining": false,
"staleblocksmined": 0,
}
/miner/start [GET]
starts a single threaded cpu miner. Does nothing if the cpu miner is already running.
Response
standard success or error response. See #standard-responses.
/miner/stop [GET]
stops the cpu miner. Does nothing if the cpu miner is not running.
Response
standard success or error response. See #standard-responses.
/miner/header [GET]
provides a block header that is ready to be grinded on for work.
Byte Response
For efficiency the header for work is returned as a raw byte encoding of the header, rather than encoded to JSON.
Blocks are mined by repeatedly changing the nonce of the header, hashing the
header's bytes, and comparing the resulting hash to the target. The block with
that nonce is valid if the hash is less than the target. If none of the 2^64
possible nonces result in a header with a hash less than the target, call
/miner/header [GET]
again to get a new block header with a different merkle
root. The above process can then be repeated for the new block header.
The other fields can generally be ignored. The parent block ID field is the hash of the parent block's header. Modifying this field will result in an orphan block. The timestamp is the time at which the block was mined and is set by the Sentient Daemon. Modifying this field can result in invalid block. The merkle root is the merkle root of a merkle tree consisting of the timestamp, the miner outputs (one leaf per payout), and the transactions (one leaf per transaction). Modifying this field will result in an invalid block.
Field | Byte range within response | Byte range within header |
---|---|---|
target | [0-32) | |
header | [32-112) | |
parent block ID | [32-64) | [0-32) |
nonce | [64-72) | [32-40) |
timestamp | [72-80) | [40-48) |
merkle root | [80-112) | [48-80) |
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (returned bytes)
tttttttttttttttttttttttttttttttt (target)
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh (header)
pppppppppppppppppppppppppppppppp (parent block ID)
nnnnnnnn (nonce)
ssssssss (timestamp)
mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm (merkle root)
/miner/header [POST]
submits a header that has passed the POW.
Request Body Bytes
For efficiency headers are submitted as raw byte encodings of the header in the
body of the request, rather than as a query string parameter or path parameter.
The request body should contain only the 80 bytes of the encoded header. The
encoding is the same encoding used in /miner/header [GET]
endpoint. Refer to
Miner.md#byte-response for a detailed
description of the byte encoding.
Response
standard success or error response. See #standard-responses.
Transaction Pool
Route | HTTP verb |
---|---|
/tpool/confirmed/:id | GET |
/tpool/fee | GET |
/tpool/raw/:id | GET |
/tpool/raw | POST |
/tpool/confirmed/:id [GET]
returns whether the requested transaction has been seen on the blockchain. Note, however, that the block containing the transaction may later be invalidated by a reorg.
JSON Response
{
"confirmed": true
}
/tpool/fee [GET]
returns the minimum and maximum estimated fees expected by the transaction pool.
JSON Response
{
"minimum": "1234", // hastings / byte
"maximum": "5678" // hastings / byte
}
/tpool/raw/:id [GET]
returns the ID for the requested transaction and its raw encoded parents and transaction data.
JSON Response
{
// id of the transaction
"id": "124302d30a219d52f368ecd94bae1bfb922a3e45b6c32dd7fb5891b863808788",
// raw, base64 encoded transaction data
"transaction": "AQAAAAAAAADBM1ca/FyURfizmSukoUQ2S0GwXMit1iNSeYgrnhXOPAAAAAAAAAAAAQAAAAAAAABlZDI1NTE5AAAAAAAAAAAAIAAAAAAAAACdfzoaJ1MBY7L0fwm7O+BoQlFkkbcab5YtULa6B9aecgEAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAM7Ljyf0IA86AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAACgAAAAAAAACe0ZTbGbI4wAAAAAAAAAAAAAABAAAAAAAAAMEzVxr8XJRF+LOZK6ShRDZLQbBcyK3WI1J5iCueFc48AAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA+z4P1wc98IqKxykTSJxiVT+BVbWezIBnIBO1gRRlLq2x/A+jIc6G7/BA5YNJRbdnqPHrzsZvkCv4TKYd/XzwBA==",
"parents": "AQAAAAAAAAABAAAAAAAAAJYYmFUdXXfLQ2p6EpF+tcqM9M4Pw5SLSFHdYwjMDFCjAAAAAAAAAAABAAAAAAAAAGVkMjU1MTkAAAAAAAAAAAAgAAAAAAAAAAHONvdzzjHfHBx6psAN8Z1rEVgqKPZ+K6Bsqp3FbrfjAQAAAAAAAAACAAAAAAAAAAwAAAAAAAAAAzvNDjSrme8gwAAA4w8ODnW8DxbOV/JribivvTtjJ4iHVOug0SXJc31BdSINAAAAAAAAAAPGHY4699vggx5AAAC2qBhm5vwPaBsmwAVPho/1Pd8ecce/+BGv4UimnEPzPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAACWGJhVHV13y0NqehKRfrXKjPTOD8OUi0hR3WMIzAxQowAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAABnt64wN1qxym/CfiMgOx5fg/imVIEhY+4IiiM7gwvSx8qtqKniOx50ekrGv8B+gTKDXpmm2iJibWTI9QLZHWAY=",
}
/tpool/raw [POST]
submits a raw transaction to the transaction pool, broadcasting it to the transaction pool's peers.
Query String Parameters
parents string // raw base64 encoded transaction parents
transaction string // raw base64 encoded transaction
Response
standard success or error response. See #standard-responses.
Wallet
Route | HTTP verb |
---|---|
/wallet | GET |
/wallet/address | GET |
/wallet/addresses | GET |
/wallet/backup | GET |
/wallet/init | POST |
/wallet/init/seed | POST |
/wallet/lock | POST |
/wallet/seed | POST |
/wallet/seeds | GET |
/wallet/sen | POST |
/wallet/sweep/seed | POST |
/wallet/transaction/:id | GET |
/wallet/transactions | GET |
/wallet/transactions/:addr | GET |
/wallet/unlock | POST |
/wallet/verify/address/:addr | GET |
/wallet/changepassword | POST |
/wallet/transaction | POST |
/wallet [GET]
returns basic information about the wallet, such as whether the wallet is locked or unlocked.
JSON Response
{
"encrypted": true,
"unlocked": true,
"rescanning": false,
"confirmedsenbalance": "123456", // hastings, big int
"unconfirmedoutgoingsen": "0", // hastings, big int
"unconfirmedincomingsen": "789", // hastings, big int
"dustthreshold": "1234", // hastings / byte, big int
}
Query String Parameters
source
encryptionpassword
Response
standard success or error response. See #standard-responses.
/wallet/address [GET]
gets a new address from the wallet generated by the primary seed. An error will be returned if the wallet is locked.
JSON Response
{
"address": "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab"
}
/wallet/addresses [GET]
fetches the list of addresses from the wallet. If the wallet has not been created or unlocked, no addresses will be returned. After the wallet is unlocked, this call will continue to return its addresses even after the wallet is locked again.
JSON Response
{
"addresses": [
"1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
]
}
/wallet/backup [POST]
creates a backup of the wallet settings file. Though this can easily be done manually, the settings file is often in an unknown or difficult to find location. The /wallet/backup call can spare users the trouble of needing to find their wallet file.
Parameters
destination
Response
standard success or error response. See #standard-responses.
/wallet/init [POST]
initializes the wallet. After the wallet has been initialized once, it does not need to be initialized again, and future calls to /wallet/init will return an error. The encryption password is provided by the api call. If the password is blank, then the password will be set to the same as the seed.
Query String Parameters
encryptionpassword
dictionary // Optional, default is english.
force // Optional, when set to true it will destroy an existing wallet and reinitialize a new one.
JSON Response
{
"primaryseed": "hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello"
}
/wallet/init/seed [POST]
initializes the wallet using a preexisting seed. After the wallet has been initialized once, it does not need to be initialized again, and future calls to /wallet/init/seed will return an error. The encryption password is provided by the api call. If the password is blank, then the password will be set to the same as the seed. Note that loading a preexisting seed requires scanning the blockchain to determine how many keys have been generated from the seed. For this reason, /wallet/init/seed can only be called if the blockchain is synced.
Query String Parameters
encryptionpassword
dictionary // Optional, default is english.
seed
force // Optional, when set to true it will destroy an existing wallet and reinitialize a new one
// Additionally, when set to true, the blockchain sync state check is bypassed
Response
standard success or error response. See #standard-responses.
/wallet/seed [POST]
gives the wallet a seed to track when looking for incoming transactions. The wallet will be able to spend outputs related to addresses created by the seed. The seed is added as an auxiliary seed, and does not replace the primary seed. Only the primary seed will be used for generating new addresses.
Query String Parameters
encryptionpassword
dictionary
seed
Response
standard success or error response. See #standard-responses.
/wallet/seeds [GET]
returns the list of seeds in use by the wallet. The primary seed is the only seed that gets used to generate new addresses. This call is unavailable when the wallet is locked.
Query String Parameters
dictionary
JSON Response
{
"primaryseed": "hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello",
"addressesremaining": 2500,
"allseeds": [
"hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello",
"foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo",
]
}
/wallet/sen [POST]
sends sen to an address or set of addresses. The outputs are arbitrarily selected from addresses in the wallet. If 'outputs' is supplied, 'amount' and 'destination' must be empty.
Query String Parameters
amount // hastings
destination // address
outputs // JSON array of {unlockhash, value} pairs
JSON Response
{
"transactionids": [
"1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
]
}
/wallet/sweep/seed [POST]
Function: Scan the blockchain for outputs belonging to a seed and send them to an address owned by the wallet.
Query String Parameters
dictionary // Optional, default is english.
seed
JSON Response
{
"coins": "123456", // hastings, big int
}
/wallet/lock [POST]
locks the wallet, wiping all secret keys. After being locked, the keys are encrypted. Queries for the seed, to send sen, and related queries become unavailable. Queries concerning transaction history and balance are still available.
Response
standard success or error response. See #standard-responses.
/wallet/transaction/:id [GET]
gets the transaction associated with a specific transaction id.
Path Parameters
:id
JSON Response
{
"transaction": {
"transaction": {
// See types.Transaction in https://github.com/consensus-ai/sen-node/blob/master/types/transactions.go
},
"transactionid": "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"confirmationheight": 50000,
"confirmationtimestamp": 1257894000,
"inputs": [
{
"parentid": "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"fundtype": "sen input",
"walletaddress": false,
"relatedaddress": "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab",
"value": "1234", // hastings, depending on fundtype, big int
}
],
"outputs": [
{
"id": "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"fundtype": "sen output",
"maturityheight": 50000,
"walletaddress": false,
"relatedaddress": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"value": "1234", // hastings, big int
}
]
}
}
/wallet/transactions [GET]
returns a list of transactions related to the wallet in chronological order.
Query String Parameters
startheight // block height
endheight // block height
JSON Response
{
"confirmedtransactions": [
{
// See the documentation for '/wallet/transaction/:id' for more information.
}
],
"unconfirmedtransactions": [
{
// See the documentation for '/wallet/transaction/:id' for more information.
}
]
}
/wallet/transactions/:addr [GET]
returns all of the transactions related to a specific address.
Path Parameters
:addr
JSON Response
{
"transactions": [
{
// See the documentation for '/wallet/transaction/:id' for more information.
}
]
}
/wallet/unlock [POST]
unlocks the wallet. The wallet is capable of knowing whether the correct password was provided.
Query String Parameters
encryptionpassword
Response
standard success or error response. See #standard-responses.
/wallet/verify/address/:addr [GET]
takes the address specified by :addr and returns a JSON response indicating if the address is valid.
JSON Response
{
"valid": true
}
/wallet/changepassword [POST]
changes the wallet's encryption key.
Query String Parameters
encryptionpassword
newpassword
Response
standard success or error response. See #standard-responses.
/wallet/transaction [POST]
generate a signed transaction with given outputs that can later be submitted to the transaction pool
Request Body Bytes
Input is supplied in the POST body, encoded in JSON format. It encodes a map where keys are UnlockHash strings and values are integers. Unlock Hash Strings represent the destination address, and value integers represent the amount to be sent to the address, in Hastings.
e.g:
{
"741eb923440cee35e7181b98659e29ff55807bf96b59a19b10ca6a9fa17f97adf7d6c8a30a48": 30022500000000000000000000,
"e495f23593bd1252b39e1200b6c18efa03197fefda9ddedefb9f2251df7a827aefb766b5848a": 939932500000000000000000000
}
JSON Response
Response is a JSON encoded string, which includes the JSON encoded Parents and Transaction objects, as well as the RawParents and RawTransaction strings as part of the JSON struct, which are base64 encoded equivalents of the Parents and Transaction objects, respectively. The Raw fields can be passed in to POST [/tpool/raw] for submission into the transaction pool.
e.g:
{
"parents": [{
"seninputs": [{
"parentid": "71fee6ba3ab68cedc1cef8c13ddb440a910050028ac68e5938c0b0c668f63bd9",
"unlockconditions": {
"timelock": 0,
"publickeys": [{
"algorithm": "ed25519",
"key": "YmgGbcFT3B3ua8fHRQCqb9oF3AonFpKz6fT4WxwBLvs="
}],
"signaturesrequired": 1
}
}],
"senoutputs": [{
"value": "30022500000000000000000000",
"unlockhash": "741eb923440cee35e7181b98659e29ff55807bf96b59a19b10ca6a9fa17f97adf7d6c8a30a48"
}, {
"value": "939932500000000000000000000",
"unlockhash": "e495f23593bd1252b39e1200b6c18efa03197fefda9ddedefb9f2251df7a827aefb766b5848a"
}],
"transactionsignatures": [{
"parentid": "71fee6ba3ab68cedc1cef8c13ddb440a910050028ac68e5938c0b0c668f63bd9",
"publickeyindex": 0,
"timelock": 0,
"coveredfields": {
"wholetransaction": true
},
"signature": "hj955kvh1SlsXAU7dljEo5PIw9ctiMhEDxAaOjh8geMypOcOq6YsuDx0Eccll70Cb2HdiwfA+HjUwkF7duKuBQ=="
}]
}],
"transaction": {
"seninputs": [{
"parentid": "43f8fd13007c17409fc4c27a87ab5b9b78a88459f4f44cf4cb94a12e743917fa",
"unlockconditions": {
"timelock": 0,
"publickeys": [{
"algorithm": "ed25519",
"key": "odI1HiT81fkGajIUGgT/05TKwjNm0CpDR/u5M2N9G0o="
}],
"signaturesrequired": 1
}
}],
"senoutputs": [{
"value": "30000000000000000000000000",
"unlockhash": "7f2420ec55de48697999356185496cd4084329ab716d779614b1a3a2811faa999aa386327063"
}],
"minerfees": ["22500000000000000000000"],
"transactionsignatures": [{
"parentid": "43f8fd13007c17409fc4c27a87ab5b9b78a88459f4f44cf4cb94a12e743917fa",
"publickeyindex": 0,
"timelock": 0,
"coveredfields": {
"wholetransaction": true
},
"signature": "0chy4DLPS/YtPDvtYHaceG4RLNzFFhOUsg1MvYDpLQ+8g5zo4yV/aJNZFjaweThJ+fz0fRakpdkWvHMFW4MvCA=="
}]
},
"rawparents": "AQAAAAAAAAABAAAAAAAAAHH+5ro6toztwc74wT3bRAqRAFACisaOWTjAsMZo9jvZAAAAAAAAAAABAAAAAAAAAGVkMjU1MTkAAAAAAAAAAAAgAAAAAAAAAGJoBm3BU9wd7mvHx0UAqm/aBdwKJxaSs+n0+FscAS77AQAAAAAAAAACAAAAAAAAAAsAAAAAAAAAGNWC/HXJvO8QAAB0HrkjRAzuNecYG5hlnin/VYB7+WtZoZsQymqfoX+XrQwAAAAAAAAAAwl+cuyrJt740AAA5JXyNZO9ElKznhIAtsGO+gMZf+/and7e+58iUd96gnoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAcf7mujq2jO3BzvjBPdtECpEAUAKKxo5ZOMCwxmj2O9kAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACGP3nmS+HVKWxcBTt2WMSjk8jD1y2IyEQPEBo6OHyB4zKk5w6rpiy4PHQRxyWXvQJvYd2LB8D4eNTCQXt24q4F",
"rawtransaction": "AQAAAAAAAABD+P0TAHwXQJ/EwnqHq1ubeKiEWfT0TPTLlKEudDkX+gAAAAAAAAAAAQAAAAAAAABlZDI1NTE5AAAAAAAAAAAAIAAAAAAAAACh0jUeJPzV+QZqMhQaBP/TlMrCM2bQKkNH+7kzY30bSgEAAAAAAAAAAQAAAAAAAAALAAAAAAAAABjQv0I8A9jeAAAAfyQg7FXeSGl5mTVhhUls1AhDKatxbXeWFLGjooEfqpkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAKAAAAAAAAAATDujnF5BEQAAAAAAAAAAAAAAEAAAAAAAAAQ/j9EwB8F0CfxMJ6h6tbm3iohFn09Ez0y5ShLnQ5F/oAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADRyHLgMs9L9i08O+1gdpx4bhEs3MUWE5SyDUy9gOktD7yDnOjjJX9ok1kWNrB5OEn5/PR9FqSl2Ra8cwVbgy8I"
}
Explorer
Explorer routes are only available in the sen-explorer
executable. The explorer supports the following routes, as well as the
/consensus
and /gateway
routes. The /explorer/raw
routes
correspond to the routes previously available in v1.0.2 builds.
Route | HTTP verb |
---|---|
/explorer | GET |
/explorer/block/:heightorid | GET |
/explorer/blocks/:height | GET |
/explorer/blocks/:height/:count | GET |
/explorer/transaction/:txid | GET |
/explorer/address/:address | GET |
/explorer/address | POST |
/explorer/addresses | POST |
/explorer/difficulty/:timestamp | GET |
/explorer/raw/blocks/:height | GET |
/explorer/raw/hashes/:hash | GET |
/explorer [GET]
Returns a set of statistics about the blockchain as they appeared at the latest block height in the explorer's consensus set.
JSON Response
e.g:
{
"blockid": "000000000261c38c07668cad364e1876098c858331c7cac86a8578e13bf08330",
"difficulty": "222756861562",
"estimatedhashrate": "1835807982",
"height": 3498,
"maturitytimestamp": 1531424927,
"target": [0, 0, 0, 0, 4, 239, 152, 234, 248, 227, 19, 10, 14, 227, 65, 66, 180, 232, 244, 239, 99, 20, 76, 157, 200, 224, 81, 82, 186, 136, 135, 141],
"minerpayoutcount": 3498,
"transactioncount": 3697,
"seninputcount": 3544,
"senoutputcount": 202,
"minerfeecount": 99,
"arbitrarydatacount": 3498,
"transactionsignaturecount": 3544
}
/explorer/block/:heightorid [GET]
Returns information about a block that was mined at a given block height or with the provided BlockID.
JSON Response
e.g:
{
"height": 32768,
"id": "0000000000045e60b37ba08d352fd45a537bdf794bf06fb6bd086f53f0484fd1",
"minerpayouts": [
{
"address": "2ed69fbd957af70801f227886ef539c8fc32d26b4b6bd47c2fcd7baf453d37c3216de5c69231",
"amount": "999087500000000000000000000",
"blockid": "0000000000045e60b37ba08d352fd45a537bdf794bf06fb6bd086f53f0484fd1",
"matured": false
}
],
"parentid": "0000000000039023f9872a151a59ff7d093cc9a40f4a1d430cb973600fc816d8",
"supports": "0.0",
"timestamp": 1552983387,
"transactions": [
{
"blockid": "0000000000045e60b37ba08d352fd45a537bdf794bf06fb6bd086f53f0484fd1",
"fee": "0",
"id": "5550b145c4480feee0ee4a63a16da43224438cf53bf8a2835dfad243a50d4d0f",
"inputs": [
{
"address": "1cfa3c4691a3e6fb33ea1922518cbbf13079855c74c3339cc888e684789d9a703b213fb1f0d0",
"amount": "999000000000000000000000000"
},
{
"address": "2d370a097da7feea2cf738274171c24751b7b0d85a0f6d36128ef1a74c5c69e2cb3b2e94e355",
"amount": "999000000000000000000000000"
},
...
],
"outputs": [
{
"address": "e09b150f5aa2885e0a9325df21bb63c5d02dd6d263ce686d98344de9b1274992a19a80622ab7",
"amount": "34965000000000000000000000000"
}
],
"timestamp": 1552983387
},
{
"blockid": "0000000000045e60b37ba08d352fd45a537bdf794bf06fb6bd086f53f0484fd1",
"fee": "87500000000000000000000",
"id": "1babcfdb73108bec8750bb9e94ed16f0ba292ff82bec5dc2c1fb0c71f535bdd2",
"inputs": [
{
"address": "e09b150f5aa2885e0a9325df21bb63c5d02dd6d263ce686d98344de9b1274992a19a80622ab7",
"amount": "34965000000000000000000000000"
}
],
"outputs": [
{
"address": "319105eb12d2eb708cd507624977a220945766d81bf69ffa748663d20247322872e7e4258962",
"amount": "34964912500000000000000000000"
}
],
"timestamp": 1552983387
}
],
"version": "0.0"
}
/explorer/blocks/:height [GET]
Returns information about all blocks since the indicated
height. Blocks are in the same format as /explorer/block/:height
.
/explorer/blocks/:height/:count [GET]
Returns information about the count
blocks starting with the
indicated height. Blocks are in the same format as
/explorer/block/:height
.
/explorer/transaction/:txid [GET]
Returns information about the transaction with the indicated transaction ID.
JSON Response
e.g:
{
"blockid": "0000000000045e60b37ba08d352fd45a537bdf794bf06fb6bd086f53f0484fd1",
"fee": "87500000000000000000000",
"id": "1babcfdb73108bec8750bb9e94ed16f0ba292ff82bec5dc2c1fb0c71f535bdd2",
"inputs": [
{
"address": "e09b150f5aa2885e0a9325df21bb63c5d02dd6d263ce686d98344de9b1274992a19a80622ab7",
"amount": "34965000000000000000000000000"
}
],
"outputs": [
{
"address": "319105eb12d2eb708cd507624977a220945766d81bf69ffa748663d20247322872e7e4258962",
"amount": "34964912500000000000000000000"
}
],
"timestamp": 1552983387
}
/explorer/address/:address [GET]
Returns information about the indicated blockchain address, including the balance held by that address and all transactions involving that address.
JSON Response
e.g:
{
"addresses": [
"e09b150f5aa2885e0a9325df21bb63c5d02dd6d263ce686d98344de9b1274992a19a80622ab7"
],
"balance": "0",
"minerpayouts": null,
"transactions": [
{
"blockid": "0000000000045e60b37ba08d352fd45a537bdf794bf06fb6bd086f53f0484fd1",
"fee": "87500000000000000000000",
"id": "1babcfdb73108bec8750bb9e94ed16f0ba292ff82bec5dc2c1fb0c71f535bdd2",
"inputs": [
{
"address": "e09b150f5aa2885e0a9325df21bb63c5d02dd6d263ce686d98344de9b1274992a19a80622ab7",
"amount": "34965000000000000000000000000"
}
],
"outputs": [
{
"address": "319105eb12d2eb708cd507624977a220945766d81bf69ffa748663d20247322872e7e4258962",
"amount": "34964912500000000000000000000"
}
],
"timestamp": 1552983387
},
{
"blockid": "0000000000045e60b37ba08d352fd45a537bdf794bf06fb6bd086f53f0484fd1",
"fee": "0",
"id": "5550b145c4480feee0ee4a63a16da43224438cf53bf8a2835dfad243a50d4d0f",
"inputs": [
{
"address": "1cfa3c4691a3e6fb33ea1922518cbbf13079855c74c3339cc888e684789d9a703b213fb1f0d0",
"amount": "999000000000000000000000000"
},
{
"address": "2d370a097da7feea2cf738274171c24751b7b0d85a0f6d36128ef1a74c5c69e2cb3b2e94e355",
"amount": "999000000000000000000000000"
},
...
],
"outputs": [
{
"address": "e09b150f5aa2885e0a9325df21bb63c5d02dd6d263ce686d98344de9b1274992a19a80622ab7",
"amount": "34965000000000000000000000000"
}
],
"timestamp": 1552983387
}
]
}
/explorer/address [POST]
Same as /explorer/address
, but pass the requested address as the
JSON payload in the request body, for example:
e.g:
{
"address": "e09b150f5aa2885e0a9325df21bb63c5d02dd6d263ce686d98344de9b1274992a19a80622ab7"
}
/explorer/addresses [POST]
Same as /explorer/address
, but pass a list of requested addresses as
the JSON payload in the request body, for example:
e.g:
{
"addresses": [
"e09b150f5aa2885e0a9325df21bb63c5d02dd6d263ce686d98344de9b1274992a19a80622ab7",
"319105eb12d2eb708cd507624977a220945766d81bf69ffa748663d20247322872e7e4258962"
]
}
/explorer/difficulty/:timestamp [GET]
Generates the datapoints for a graph of blockchain difficulty over time. Pass in the requested timestamp, and datapoints will be returned for all blocks since the indicated (UTC) timestamp. Each datapoint includes the block height, difficulty, and timestamp.
JSON Response
e.g:
{
"points": [
{
"d": "71015803596781",
"h": 73471,
"t": 1577386693
},
{
"d": "71299866811168",
"h": 73472,
"t": 1577387431
},
{
"d": "71467433920200",
"h": 73473,
"t": 1577391706
},
...
]
}
/explorer/raw/blocks/:height [GET]
Returns information about a block that was mined at a given block height.
JSON Response
e.g:
{
"block": {
"arbitrarydatacount": 100,
"blockid": "0000000093d626a2b06ed3043107ab7ae299e871e927d2ec06519222681bef9f",
"difficulty": "5637715065",
"height": 100,
"maturitytimestamp": 1531009728,
"minerfeecount": 1,
"minerpayoutcount": 100,
"minerpayoutids": [
"360f9b7fc67815e182897294a9276bb2bc8d5d2b840ac75f444c95dbacc20f02"
],
"rawblock": {
"minerpayouts": [
{
"unlockhash": "9bbcdd07575b8332710c204660671163e24781da4bcd83f384b233f6192712c2c29fda28e27f",
"value": "40600000000000000000000000000"
}
],
"nonce": [209, 224, 22, 0, 0, 0, 0, 0],
"parentid": "000000000faa0549e52fff661ccd941983ce799aee643f8ab6ce07d8e838c7f9",
"timestamp": 1531009756,
"transactions": [
{
"arbitrarydata": [
"g8F7PJAWSfhCD1/GEMFGwgslmd4jPSOiWo3LtatzGt8="
]
}
]
},
"seninputcount": 36,
"senoutputcount": 4,
"target": [0, 0, 0, 0, 195, 7, 35, 68, 32, 60, 173, 120, 240, 190, 202, 246, 50, 166, 0, 28, 138, 207, 214, 253, 56, 141, 242, 246, 37, 248, 88, 74],
"transactioncount": 103,
"transactions": [
{
"height": 100,
"id": "24f8b3327559dfad7499ed1a5b78af571a1467a2dac58d06385b02d77a6d8f19",
"parent": "0000000093d626a2b06ed3043107ab7ae299e871e927d2ec06519222681bef9f",
"rawtransaction": {
"arbitrarydata": [
"g8F7PJAWSfhCD1/GEMFGwgslmd4jPSOiWo3LtatzGt8="
]
}
}
],
"transactionsignaturecount": 36
}
}
/explorer/raw/hashes/:hash [GET]
Scans the blockchain for appearances of this hash as one of: Block ID, Transaction ID, Sen Output ID, UnlockHash
JSON Response
e.g:
{
"hashtype": "transactionid",
"transaction": {
"id": "375e96de4c7eb1ae897d2601f21466515300b226d95a33399af25f75b72a512d",
"height": 204,
"parent": "0000000032cee8534c8f7bb1dd577d5e974d299ab17f61105ff3d47e2ccd210d",
"rawtransaction": {
"seninputs": [
{
"parentid": "e157550123786fcc8fdddd0ce3d6e5fa8da8af4079aafcd7d6241831b2fa006d",
"unlockconditions": {
"timelock": 0,
"publickeys": [
{
"algorithm": "ed25519",
"key": "bi4u8PQlxqTNi4H6chPIo1VPjCqVT5U4P46VlgBBBRg="
}
],
"signaturesrequired": 1
}
}
],
"senoutputs": [
{
"value": "1000000000000000000000000000000",
"unlockhash": "f8cd0e125b236f9b6960a6bb5822f60ef99411953068f258a794963478040d643c4e83a1ed9f"
}
],
"minerfees": [
"22500000000000000000000"
],
"transactionsignatures": [
{
"parentid": "e157550123786fcc8fdddd0ce3d6e5fa8da8af4079aafcd7d6241831b2fa006d",
"publickeyindex": 0,
"timelock": 0,
"coveredfields": {
"wholetransaction": true
},
"signature": "5KFz4X0t+Ka/cLN6XutyDqgDJGdzDH9UDXsbhUGiM0Pzx6hZ/shfOFZzXTG5d/EEMBNIGr7NZ/K/jXTPrwX+Aw=="
}
]
},
"seninputoutputs": [
{
"value": "1000000022500000000000000000000",
"unlockhash": "6aa4c43fc79728c2f85020980f298ece437b7b1772d6e48cef4af75db335599438e6ed12a7fe"
}
],
"senoutputids": [
"f0fa14b2430c771089c1b310c4b50dfde02dfe8a4181f34a4e43e923cd854718"
]
}
}
Developer Environment
Sentient Network is written in Go. To build and test it, you are going to need
a working go environment, including having both $GOROOT/bin
and $GOPATH/bin
in your $PATH
. For most Linux distributions, Go will be in the package manager,
though it may be an old version that is incompatible with Sen. Once you have a
working Go environment, you are set to build the project. If you plan on cross
compiling Sen, you may need to install Go from source. You can find information
on that here.
Sentient network has a development build, an automated testing build, a testnet
build, and a release build. The release build is the only one that can synchronize
to the main network. To get the release build, it is usually sufficient to run
go get -u github.com/consensus-ai/sen-node/...
. This will download
Sentient Network and its dependencies and install binaries in $GOPATH/bin
.
After downloading, you can find the sentient network source code in
$GOPATH/src/github.com/consensus-ai/sen-node
. To build the release binary, run
make release-std
from this directory. To build the release binary with a
(slow) race detector and an array of debugging asserts, run make release
. To
build the developer binary (which has a different genesis block, faster block
times, and a few other tweaks), just run make
.
If you intend to contribute to Sentient Network, you should start by forking the
project on GitHub, and then adding your fork as a "remote" in the sen-node
git repository via git remote add [fork name] [fork url]
. Now you can develop by
pulling changes from origin
, pushing your modifications to [fork name]
, and
then making a pull request on GitHub.
If you see an error like the one below, it means that you either forgot to run
make dependencies
, or you cloned the project into a path that the go tool
does not recognize (usually the wrong path, or symbolic links were somehow
involved).
consensus/fork.go:4:2: cannot find package "github.com/consensus-ai/sen-node/crypto" in any of:
/usr/lib/go/src/github.com/consensus-ai/sen-node/crypto (from $GOROOT)
/home/user/gopath/src/github.com/consensus-ai/sen-node/crypto (from $GOPATH)
Developer Conventions
This file is meant to help a developer navigate the codebase and develop clean, maintainable code. Knowing all of these conventions will also make it easier to read and code review the Sentient Network project.
The primary purpose of the conventions within Sentient Network is to keep the codebase
simple. Simpler constructions means easier code reviews, greater accessibility
to newcomers, and smaller potential for mistakes. It is also to keep things
uniform, much in the spirit of go fmt
. When everything looks the same,
everyone has an easier time reading and reviewing code they did not write
themselves.
Documentation
All structs, functions, and interfaces must have a docstring.
Anytime that something is left unfinished, place a comment containing the string 'TODO:'. This sends a clear message to other developers, and creates a greppable way to find unfinished parts of the codebase. 'TODO' statements are currently discouraged. As the codebase matures, 'TODO' statements will become increasingly frowned upon. 'TODO' statements should not document feature requests, but instead document incompleteness where the incompleteness causes disruption to user experience or causes a security vulnerability.
Documentation should give a sense of what each function does, but should also give a sense of the overall architecture of the code. Where useful, examples should be provided, and common pitfalls should be explained. Anything that breaks other conventions in any way needs to have a comment, even if it is obvious why the convention had to be broken.
The goal of the codebase is to be accessible to newbies. Anything more advanced than what you would expect to remember from an 'Intro to Data Structures' class should have an explanation about what the concept it is and why it was picked over other potential choices.
Code that exists purely to be compatible with previous versions of the
software should be tagged with a COMPATvX.X.X
comment. Examples below.
// Find and sort the outputs.
outputs := getOutputs()
// TODO: actually sort the outputs.
// Disallow unknown agents.
//
// COMPATv0.4.0: allow a blank agent to preserve compatibility with
// 'sentientc' v0.4.0, which did not set an agent.
if agent != "SenAgent" && agent != "" {
if errors.New("unrecognized agent!")
}
Naming
Names are used to give readers and reviewers a sense of what is happening in
the code. When naming variables, you should assume that the person reading your
code is unfamiliar with the codebase. Short names (like cs
instead of
consensusSet
) should only be used when the context is immediately obvious.
For example cs := new(ConsensusSet)
is immediately obvious context for cs
,
and so cs
is appropriate for the rest of the function.
Data structures should never have shortened names. Transaction.ts
is
confusing to anyone who has not used the data structure extensively. The code
should be accessible to people who are unfamiliar with the codebase. One
exception is for the variable called mu
, which is short for 'mutex'. This
exception is made because mu
appears in many data structures.
When calling functions with obscure parameters, named variables should be used
to indicate what the parameters do. For example, m := NewMiner(1)
is
confusing. Instead, use threads := 1; m := NewMiner(threads)
. The name gives
readers a sense of what the parameter within NewMiner
does even when they are
not familiar with the NewMiner
function. Where possible, functions with
obscure, untyped inputs should be avoided.
The most important thing to remember when choosing names is to cater to people
who are unfamiliar with the code. A reader should never have to ask 'What is
cs
?' on their first pass through the code, even though to experienced
developers it is obvious that cs
refers to a consensus.ConsensusSet
.
Function Prefixes
Sen uses special prefixes for certain functions to hint about their usage to the caller.
threaded
Prefix functions with threaded
(e.g., threadedMine
) to indicate that callers
should only call these functions within their own goroutine (e.g.,
go threadedMine()
). These functions must manage their own thread-safety.
managed
Prefix functions with managed
(e.g. managedUpdateWorkerPool
) if the function
acquires any locks in its body.
Control Flow
Where possible, control structures should be minimized or avoided. This includes avoiding nested if statements, and avoiding else statements where possible. Sometimes, complex control structures are necessary, but where possible use alternative code patterns and insert functions to break things up.
Example:
// Do not do this:
if err != nil {
if
} else {
forkBlockchain(node)
}
// Instead to this:
if err != nil {
if
}
forkBlockchain(node)
Mutexes
All exported functions from a package and/or object need to be thread safe.
Usually, this means that the first lines of the function contain a Lock(); defer Unlock()
. Simple locking schemes should be preferred over performant
locking schemes. As will everything else, anything unusual or convention
breaking should have a comment.
Non-exported functions should not do any locking unless they are named with the proper prefix (see Function Prefixes). The responsibility for thread-safety comes from the exported functions which call the non-exported functions. Maintaining this convention minimizes developer overhead when working with complex objects.
Error Handling
All errors need to be checked as soon as they are received, even if they are
known to not cause problems. The statement that checks the error needs to be
if err != nil
, and if there is a good reason to use an alternative statement
(such as err == nil
), it must be documented. The body of the if statement
should be at most 4 lines, but usually only one. Anything requiring more lines
needs to be its own function.
Example:
block, err := s.AcceptBlock()
if err != nil {
handleAcceptBlockErr(block, err)
if
}
Sanity Checks
Some functions make assumptions. For example, the addTransaction
function
assumes that the transaction being added is not in conflict with any other
transactions. Where possible, these explicit assumptions should be validated.
Example:
if build.DEBUG {
_, exists := tp.usedOutputs[input.OutputID]
if exists {
panic("incorrect use of addTransaction")
}
}
In the example, a panic is called for incorrect use of the function, but only in debug mode. This failure will be invisible in production code, but the code will have higher performance because the code should never fail anyway.
If the code is continually checking items that should be universally true, mistakes are easier to catch during testing, and side effects are less likely to go unnoticed.
Sanity checks and panics are purely to check for developer mistakes. A user should not be able to trigger a panic, and no set of network communications or real-world conditions should be able to trigger a panic.
Testing
The test suite code should be the same quality as the rest of the codebase. When writing new code in a pull request, the pull request should include test coverage for the code.
Most modules have a tester object, which can be created by calling
createXXXTester
. Module testers typically have a consensus set, a miner, a
wallet, and a few other relevant modules that can be used to build
transactions, mine blocks, etc.
In general, testing that uses exclusively exported functions to achieve full coverage is preferred. These types of tests seem to find more bugs and trigger more asserts.
Any testing provided by a third party which is both maintainable and reasonably quick will be accepted. There is little downside to more testing, even when the testing is largely redundant.
Genesis
The following are the seed values for their respective unlock hashes in the dev
and testing
genesis file:
[DEV]
seed:
oaks ultimate ouch sidekick greater tutor economics vastness apex slug asleep thumbs ozone orphans hairy negative moment pastry bakery uphill justice goblet hills nail lava guys bemused aces ablaze
unlock hash:
630e039889c20d1593398fbe589972e3ae36e1a65ce48d23aa8864e064fa72e11449ac5514b0
seed:
vinegar initiate ignore apart having names obtains hatchet vogue pawnshop sake soothe jargon oxidant eldest lilac saucepan token befit session poaching pliers lobster eating noted being gigantic torch abducts
unlock hash:
48d518a96cbacd1315471dd2f557196921bc2f2021c8b5d2022f8a7e0b3c92ed1fb5734183ff
[TESTING]
seed:
symptoms daft gauze huddle cell ammo nuance cohesive vastness faxed wanted memoir rover merger catch ivory after arises reheat zones stylishly tucks divers hire smidgen vibrate mechanic terminal abrasive
unlock hash:
7c04be6f34ab037575a195d7676d268ec4d1308d2c683eef3e18cce22896f0a394916da00529
seed:
illness archer afraid avatar gossip orbit basin calamity birth oasis trash federal upper drinks double hockey fuel glass cylinder deepest goodbye exotic anybody pimple getting nitrogen energy jellyfish adept
unlock hash:
24562aa6da74a73670a91f55051719cf4519f893eff89c61f116d43970c401107da64faf9e04
Contributing to Sen
Table of Contents
Get started with Go
Install Go
To install Go on your computer, follow the official installation guide.
You should install the latest official Go binary for your system (if not available, install from source). If you plan to cross compile Sentient Network, see Cross Compilation with Go 1.5 by Dave Cheney.
Learn Go
- To get familiar with the language, start with the official Tour of Go.
- Move onto How to Write Go Code to learn how to organize Go packages and use the go tool.
- Finish with the Effective Go guide.
Build Sentient Network
To build Sentient Network on your machine, enter the following on the command line:
# Download Sentient Network and its dependencies
# Binaries will be installed in $GOPATH/bin
$ go get -u github.com/consensus-ai/sen-node/...
# Switch to directory containing Sentient Network source code
$ cd $GOPATH/src/github.com/consensus-ai/sen-node
# You have four Sentient Network builds to choose from.
# To build the standard release binary:
$ make release
# Or to build the release binary with race detection and an array debugging
# asserts:
$ make release-race
# Or to build the developer binary (with a different genesis block, faster
# block times, and other changes):
$ make dev
# Or build the developer binary with race detection:
$ make dev-race
# Build the debugger binary:
$ make debug
# Or build debugger binary with race detection:
$ make debug-race
# Build the testnet binary:
$ make testnet
Contribute to the codebase
Set up git
Install git on your machine according to these instructions in the Pro Git book.
You will first need to set up global settings using the command line.
$ git config --global user.name "Your Name"
$ git config --global user.email you@somedomain.com
# Tell git to remember your login information for a certain amount of time.
# Default time is 15 minutes:
$ git config --global credential.helper cache
# Or you can choose a different amount of time:
$ git config --global credential.helper "cache --timeout=[seconds]"
Fork the Sentient Network repository
While logged into your Github account, navigate to the Sentient Network repository
and click the 'Fork' button in the upper righthand corner. Your account now
has a 'forked' copy of the original repo at
https://github.com/<your GitHub username>/sen-node
.
When you installed Sentient Network using go get
, the go tool put the Sentient Network
source code in $GOPATH/src/github.com/consensus-ai/sen-node. Change to that
directory and set up your fork as a git remote:
$ cd $GOPATH/src/github.com/consensus-ai/sen-node
# Add your fork as a remote. Name it whatever is convenient,
# e.g your GitHub username
$ git remote add <remote name> https://github.com/<username>/sen-node.git
# Or if you use an SSH key, create the remote with the following
$ git remote add <remote name> git@github.com:<username>/sen-node.git
Write some code
Right now your git local repository only has one branch (called 'master' by default). If you want to make changes, add a new branch and make your changes there. You should maintain master as an up-to-date copy of the consensus-ai/sen-node repository's master branch.
To create and checkout a new branch:
# If you're not already in the right directory:
$ cd $GOPATH/src/github.com/consensus-ai/sen-node
# Make sure you're on branch master
$ git checkout master
# Create and checkout a new branch
$ git checkout -b <branch>
Now write some code while the new branch is checked out.
Only implement one logical change per branch. If you're working on several
things at once, make multiple branches. To switch between branches you're
working on, you have to stash the changes in the branch you're switching from
by running git stash
, which tucks away all changes since the last
commit.
# Stash changes to current branch.
$ git stash
# Checkout other branch.
$ git checkout <branch>
...
# Make changes
...
# Return to first branch:
$ git checkout <branch 1>
# View a list of stashes and their corresponding hashes.
$ git stash list
# Reapply changes from the stash you want to recover and remove that stash from.
# the list
$ git stash pop <hash>
To learn more about branching, see Using the Fork-and-Branch Git Workflow and Pro Git - Branches in a Nutshell. For more on stashing, see Pro Git - Stashing and Cleaning.
Be sure to follow the conventions detailed in docs/Developers.md. We will reject pull requests that do not satisfy these best practices.
Once you've finished making changes, stage and commit your changes then update your fork on Github:
# Make sure the code is up to date with the original repo:
$ git checkout master
$ git pull origin master
# Checkout branch with changes.
$ git checkout <branch>
$ git rebase master
# Before every pull request, you should run `make test-long`
# to test your code and fix formatting and style problems.
$ make test-long
# If all goes well, proceed to staging your changed files:
$ git add <changed files>
# Use `git status` to see what files have been staged.
$ git status
# Commit your changes. If you just run `commit`, a text editor will pop up for
# you to enter a description of your changes.
$ git commit -m "Add new tests for CommitSync method"
# Push the changes to your fork on GitHub, which you should have set up as a
# remote already.
$ git push <fork remote> <branch>
Submit your code for review
Once you've tested your new code and pushed changes to your fork, navigate to
your fork at https://github.com/<username>/sen-node
in your browser.
Switch to the branch you've made changes on by selecting it from the list on
the upper left. Then click 'New pull request' on the upper right.
Once you have made the pull request, we will review your code. We will reject code that is unsafe, difficult to read, or otherwise violates the conventions outlined in docs/Developers.md.
If you want to tweak code for which you've already submitted a pull request,
push the updated code to your fork with git push -f <fork remote> <branch>
and
summarize the changes you've made in a comment on the pull request page on
GitHub.
Once we have accepted your changes and merged them into the original repo, you have some cleanup to do:
# Update local master branch to reflect changes in origin (the original
# repo).
$ git pull origin master
# Delete the branch you made the pull request from.
$ git branch -d <branch>
# Delete the remote branch on your fork.
$ git push <fork remote> :<branch>
# Update your fork.
$ git push <fork remote> master
More Git resources
Where to start
If you'd like to contribute to Sentient Network but don't have any specific ideas, writing tests is a good way to get your feet wet. See doc/Running and Writing Tests for Sentient Network.md to get started.
Contact us
Feel free to ask for help on the #core-dev channel on discord.