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 via sentientc
  • 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

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.