{"action":"created","comment":{"url":"https://api.github.com/repos/NebulousLabs/Sia/pulls/comments/49271460","id":49271460,"diff_hunk":"@@ -0,0 +1,264 @@\n+package host\n+\n+import (\n+\t\"os\"\n+\t\"sync\"\n+\n+\t\"github.com/NebulousLabs/Sia/build\"\n+\t\"github.com/NebulousLabs/Sia/types\"\n+)\n+\n+const (\n+\t// resubmissionTimeout is the number of blocks that the host will wait\n+\t// before trying to resubmit a transaction to the blockchain.\n+\tresubmissionTimeout = 2\n+\n+\t// confirmationRequirement is the number of blocks that the host is going\n+\t// to wait before assuming that a storage proof has successfully been\n+\t// confirmed by the blockchain.\n+\tconfirmationRequirement = 12\n+\n+\t// Booleans to indicate that a contract obligation has been successful or\n+\t// unsuccessful.\n+\tobligationSucceeded = true\n+\tobligationFailed    = false\n+)\n+\n+// A contractObligation tracks a file contract that the host is obligated to\n+// fulfill.\n+type contractObligation struct {\n+\t// Blockchain tracking. The storage proof transaction is not tracked\n+\t// because the same storage proof transaction is not always guaranteed to\n+\t// be valid. If the origin or the revision has not been confirmed, the host\n+\t// will need to resubmit them to the transaction pool.\n+\tID                types.FileContractID // The ID of the file contract.\n+\tOriginTxn         types.Transaction    // The transaction containing the original file contract.\n+\tRevisionTxn       types.Transaction    // The most recent revision to the contract.\n+\tOriginConfirmed   bool                 // whether the origin transaction has been confirmed.\n+\tRevisionConfirmed bool                 // whether the most recent revision has been confirmed.\n+\tProofConfirmed    bool                 // whether the storage proof has been confirmed.\n+\n+\t// Where on disk the file is stored.\n+\tPath string\n+\n+\t// The mutex ensures that revisions are happening in serial. The actual\n+\t// data under the obligations is being protected by the host's mutex.\n+\t// Grabbing 'mu' is not sufficient to guarantee modification safety of the\n+\t// struct, the host mutex must also be grabbed.\n+\tmu sync.Mutex\n+}\n+\n+// fileSize returns the size of the file that is held by the contract\n+// obligation.\n+func (co *contractObligation) fileSize() uint64 {\n+\tif co.hasRevision() {\n+\t\treturn co.RevisionTxn.FileContractRevisions[0].NewFileSize\n+\t}\n+\treturn co.OriginTxn.FileContracts[0].FileSize\n+}\n+\n+// hasRevision indiciates whether there is a file contract reivision contained\n+// within the contract obligation.\n+func (co *contractObligation) hasRevision() bool {\n+\treturn len(co.RevisionTxn.FileContractRevisions) == 1\n+}\n+\n+// missedProofUnlockHash returns the operating unlock hash for a successful\n+// file contract in the obligation.\n+func (co *contractObligation) missedProofUnlockHash() types.UnlockHash {\n+\tif co.hasRevision() {\n+\t\treturn co.RevisionTxn.FileContractRevisions[0].NewMissedProofOutputs[1].UnlockHash\n+\t}\n+\treturn co.OriginTxn.FileContracts[0].MissedProofOutputs[1].UnlockHash\n+}\n+\n+// payout returns the operating payout of the contract obligation.\n+func (co *contractObligation) payout() types.Currency {\n+\t// Function seems unnecessary because it is just a getter, but adding this\n+\t// function helps maintain consistency with the way that the other fields\n+\t// are accessed.\n+\treturn co.OriginTxn.FileContracts[0].Payout\n+}\n+\n+// proofConfirmed inidicates whether the storage proofs have been seen on the\n+// blockchain.\n+func (co *contractObligation) proofConfirmed() bool {\n+\t// Function seems unnecessary because it is just a getter, but adding this\n+\t// function helps maintain consistency with the way that the other fields\n+\t// are accessed.\n+\treturn co.ProofConfirmed\n+}\n+\n+// reset updates the contract obligation to reflect that the consensus set is\n+// being rescanned, which means all of the consensus indicators need to be\n+// reset, and the action items need to be filled out again.\n+func (co *contractObligation) reset() {\n+\tco.OriginConfirmed = false\n+\tco.ProofConfirmed = false\n+\n+\t// If there is no revision, then the final revision counts as being\n+\t// confirmed. Otherwise, the revision should not be considered as\n+\t// confirmed.\n+\tif co.hasRevision() {\n+\t\tco.RevisionConfirmed = false\n+\t} else {\n+\t\tco.RevisionConfirmed = true\n+\t}\n+}\n+\n+// revisionNumber returns the operating revision number of the obligation.\n+func (co *contractObligation) revisionNumber() uint64 {\n+\tif co.hasRevision() {\n+\t\treturn co.RevisionTxn.FileContractRevisions[0].NewRevisionNumber\n+\t}\n+\treturn co.OriginTxn.FileContracts[0].RevisionNumber\n+}\n+\n+// txnsConfirmed indiactes whether the file contract and its latest revision\n+// have been seen on the blockchain.\n+func (co *contractObligation) txnsConfirmed() bool {\n+\tif !co.OriginConfirmed {\n+\t\treturn false\n+\t}\n+\tif co.hasRevision() && !co.RevisionConfirmed {\n+\t\treturn false\n+\t}\n+\treturn true","path":"modules/host/obligations.go","position":126,"original_position":126,"commit_id":"bee35e2b75e14f01768f0a383b5954939b60e724","original_commit_id":"ce2bf437011c2e6ab4bea255d393f64e36c450b4","user":{"login":"lukechampine","id":3344749,"avatar_url":"https://avatars.githubusercontent.com/u/3344749?v=3","gravatar_id":"","url":"https://api.github.com/users/lukechampine","html_url":"https://github.com/lukechampine","followers_url":"https://api.github.com/users/lukechampine/followers","following_url":"https://api.github.com/users/lukechampine/following{/other_user}","gists_url":"https://api.github.com/users/lukechampine/gists{/gist_id}","starred_url":"https://api.github.com/users/lukechampine/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lukechampine/subscriptions","organizations_url":"https://api.github.com/users/lukechampine/orgs","repos_url":"https://api.github.com/users/lukechampine/repos","events_url":"https://api.github.com/users/lukechampine/events{/privacy}","received_events_url":"https://api.github.com/users/lukechampine/received_events","type":"User","site_admin":false},"body":"```go\r\nif co.hasRevision() {\r\n    return co.RevisionConfirmed\r\n}\r\n```","created_at":"2016-01-10T04:24:58Z","updated_at":"2016-01-10T04:24:58Z","html_url":"https://github.com/NebulousLabs/Sia/pull/898#discussion_r49271460","pull_request_url":"https://api.github.com/repos/NebulousLabs/Sia/pulls/898","_links":{"self":{"href":"https://api.github.com/repos/NebulousLabs/Sia/pulls/comments/49271460"},"html":{"href":"https://github.com/NebulousLabs/Sia/pull/898#discussion_r49271460"},"pull_request":{"href":"https://api.github.com/repos/NebulousLabs/Sia/pulls/898"}}},"pull_request":{"url":"https://api.github.com/repos/NebulousLabs/Sia/pulls/898","id":55554704,"html_url":"https://github.com/NebulousLabs/Sia/pull/898","diff_url":"https://github.com/NebulousLabs/Sia/pull/898.diff","patch_url":"https://github.com/NebulousLabs/Sia/pull/898.patch","issue_url":"https://api.github.com/repos/NebulousLabs/Sia/issues/898","number":898,"state":"open","locked":false,"title":"Host Revamp","user":{"login":"DavidVorick","id":2316841,"avatar_url":"https://avatars.githubusercontent.com/u/2316841?v=3","gravatar_id":"","url":"https://api.github.com/users/DavidVorick","html_url":"https://github.com/DavidVorick","followers_url":"https://api.github.com/users/DavidVorick/followers","following_url":"https://api.github.com/users/DavidVorick/following{/other_user}","gists_url":"https://api.github.com/users/DavidVorick/gists{/gist_id}","starred_url":"https://api.github.com/users/DavidVorick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DavidVorick/subscriptions","organizations_url":"https://api.github.com/users/DavidVorick/orgs","repos_url":"https://api.github.com/users/DavidVorick/repos","events_url":"https://api.github.com/users/DavidVorick/events{/privacy}","received_events_url":"https://api.github.com/users/DavidVorick/received_events","type":"User","site_admin":false},"body":"I don't know how this blew up so much, it was a lot simpler when I got started. I'm still not done testing, and there are a handful of other features I'd like to add, but I don't see those being ready by v0.5.0. It's time to call it quits on the feature adding until we're ready to start moving towards v1.0. I haven't finished writing tests yet, it's likely that some of the tests will turn up more bugs - some of the fancier logic still isn't checked.\r\n\r\nI added some tracking to the RPCs. Now, the host will count each time any of the RPCs are hit, which will give us information about how hosts are being used. This is particularly useful as a natural way to tell if the ports are forwarded. A very low hit count would indicate that the host has low visibility. A high hit count would indicate that the ports are probably forwarded correctly. Currently, there's no way to access these fields from outside the package.\r\n\r\nThe logger package will now get upset if you call `Write` after you call `Close`, or if you call `Close` twice.\r\n\r\nThe host is now able to track consensus on the transactions that it has. It can tell if a transaction is not in the consensus set, and will know to resubmit the transaction. It can tell if it has been double spent, and will know to delete the obligation. It can tell if there was a reorg after it submitted a storage proof.\r\n\r\nThe host now cleanly closes. All goroutines will get to terminate before stuff like the logger is closed, and when `Close` terminates there should be no more threads running in the host. Additionally, exported functions that require host resources will return errors if they are called after the host has been closed.\r\n\r\nHost now passes golint, and a travis check has been added that will run golint on the host.\r\n\r\nErrcheck has been run on the host, not all offences have been removed due to `defer Closer` type stuff, but a few mistakes were found and corrected. @lukechampine may disagree with some of the error handling, so it's worth a close look + discussion. I'm thinking mostly about checking the errors on `conn.SetDeadline`\r\n\r\nThings that could still be done:\r\n+ rpc tracking is not exported\r\n+ bandwidth ratelimiting\r\n+ merkle tree caching\r\n+ allowing edits to a file in random places (requires merkle tree caching)\r\n+ paid bandwidth\r\n+ toggle switch to stop the host from accepting new contracts\r\n+ some of the informational functions could be consolidated, which may make mocking easier","created_at":"2016-01-10T00:37:53Z","updated_at":"2016-01-10T04:24:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"750139b877b842c9fff779b3a343a64e5a4b674c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/NebulousLabs/Sia/pulls/898/commits","review_comments_url":"https://api.github.com/repos/NebulousLabs/Sia/pulls/898/comments","review_comment_url":"https://api.github.com/repos/NebulousLabs/Sia/pulls/comments{/number}","comments_url":"https://api.github.com/repos/NebulousLabs/Sia/issues/898/comments","statuses_url":"https://api.github.com/repos/NebulousLabs/Sia/statuses/bee35e2b75e14f01768f0a383b5954939b60e724","head":{"label":"NebulousLabs:host-revenue","ref":"host-revenue","sha":"bee35e2b75e14f01768f0a383b5954939b60e724","user":{"login":"NebulousLabs","id":7471422,"avatar_url":"https://avatars.githubusercontent.com/u/7471422?v=3","gravatar_id":"","url":"https://api.github.com/users/NebulousLabs","html_url":"https://github.com/NebulousLabs","followers_url":"https://api.github.com/users/NebulousLabs/followers","following_url":"https://api.github.com/users/NebulousLabs/following{/other_user}","gists_url":"https://api.github.com/users/NebulousLabs/gists{/gist_id}","starred_url":"https://api.github.com/users/NebulousLabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NebulousLabs/subscriptions","organizations_url":"https://api.github.com/users/NebulousLabs/orgs","repos_url":"https://api.github.com/users/NebulousLabs/repos","events_url":"https://api.github.com/users/NebulousLabs/events{/privacy}","received_events_url":"https://api.github.com/users/NebulousLabs/received_events","type":"Organization","site_admin":false},"repo":{"id":25370072,"name":"Sia","full_name":"NebulousLabs/Sia","owner":{"login":"NebulousLabs","id":7471422,"avatar_url":"https://avatars.githubusercontent.com/u/7471422?v=3","gravatar_id":"","url":"https://api.github.com/users/NebulousLabs","html_url":"https://github.com/NebulousLabs","followers_url":"https://api.github.com/users/NebulousLabs/followers","following_url":"https://api.github.com/users/NebulousLabs/following{/other_user}","gists_url":"https://api.github.com/users/NebulousLabs/gists{/gist_id}","starred_url":"https://api.github.com/users/NebulousLabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NebulousLabs/subscriptions","organizations_url":"https://api.github.com/users/NebulousLabs/orgs","repos_url":"https://api.github.com/users/NebulousLabs/repos","events_url":"https://api.github.com/users/NebulousLabs/events{/privacy}","received_events_url":"https://api.github.com/users/NebulousLabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/NebulousLabs/Sia","description":"Blockchain-based marketplace for file storage","fork":false,"url":"https://api.github.com/repos/NebulousLabs/Sia","forks_url":"https://api.github.com/repos/NebulousLabs/Sia/forks","keys_url":"https://api.github.com/repos/NebulousLabs/Sia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NebulousLabs/Sia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NebulousLabs/Sia/teams","hooks_url":"https://api.github.com/repos/NebulousLabs/Sia/hooks","issue_events_url":"https://api.github.com/repos/NebulousLabs/Sia/issues/events{/number}","events_url":"https://api.github.com/repos/NebulousLabs/Sia/events","assignees_url":"https://api.github.com/repos/NebulousLabs/Sia/assignees{/user}","branches_url":"https://api.github.com/repos/NebulousLabs/Sia/branches{/branch}","tags_url":"https://api.github.com/repos/NebulousLabs/Sia/tags","blobs_url":"https://api.github.com/repos/NebulousLabs/Sia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NebulousLabs/Sia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NebulousLabs/Sia/git/refs{/sha}","trees_url":"https://api.github.com/repos/NebulousLabs/Sia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NebulousLabs/Sia/statuses/{sha}","languages_url":"https://api.github.com/repos/NebulousLabs/Sia/languages","stargazers_url":"https://api.github.com/repos/NebulousLabs/Sia/stargazers","contributors_url":"https://api.github.com/repos/NebulousLabs/Sia/contributors","subscribers_url":"https://api.github.com/repos/NebulousLabs/Sia/subscribers","subscription_url":"https://api.github.com/repos/NebulousLabs/Sia/subscription","commits_url":"https://api.github.com/repos/NebulousLabs/Sia/commits{/sha}","git_commits_url":"https://api.github.com/repos/NebulousLabs/Sia/git/commits{/sha}","comments_url":"https://api.github.com/repos/NebulousLabs/Sia/comments{/number}","issue_comment_url":"https://api.github.com/repos/NebulousLabs/Sia/issues/comments{/number}","contents_url":"https://api.github.com/repos/NebulousLabs/Sia/contents/{+path}","compare_url":"https://api.github.com/repos/NebulousLabs/Sia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NebulousLabs/Sia/merges","archive_url":"https://api.github.com/repos/NebulousLabs/Sia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NebulousLabs/Sia/downloads","issues_url":"https://api.github.com/repos/NebulousLabs/Sia/issues{/number}","pulls_url":"https://api.github.com/repos/NebulousLabs/Sia/pulls{/number}","milestones_url":"https://api.github.com/repos/NebulousLabs/Sia/milestones{/number}","notifications_url":"https://api.github.com/repos/NebulousLabs/Sia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NebulousLabs/Sia/labels{/name}","releases_url":"https://api.github.com/repos/NebulousLabs/Sia/releases{/id}","created_at":"2014-10-17T17:50:25Z","updated_at":"2016-01-06T05:55:35Z","pushed_at":"2016-01-10T04:05:15Z","git_url":"git://github.com/NebulousLabs/Sia.git","ssh_url":"git@github.com:NebulousLabs/Sia.git","clone_url":"https://github.com/NebulousLabs/Sia.git","svn_url":"https://github.com/NebulousLabs/Sia","homepage":"https://sia.tech","size":26486,"stargazers_count":139,"watchers_count":139,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":22,"mirror_url":null,"open_issues_count":21,"forks":22,"open_issues":21,"watchers":139,"default_branch":"master"}},"base":{"label":"NebulousLabs:master","ref":"master","sha":"e3b434553767b8382198459f93647f4fb954cd3a","user":{"login":"NebulousLabs","id":7471422,"avatar_url":"https://avatars.githubusercontent.com/u/7471422?v=3","gravatar_id":"","url":"https://api.github.com/users/NebulousLabs","html_url":"https://github.com/NebulousLabs","followers_url":"https://api.github.com/users/NebulousLabs/followers","following_url":"https://api.github.com/users/NebulousLabs/following{/other_user}","gists_url":"https://api.github.com/users/NebulousLabs/gists{/gist_id}","starred_url":"https://api.github.com/users/NebulousLabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NebulousLabs/subscriptions","organizations_url":"https://api.github.com/users/NebulousLabs/orgs","repos_url":"https://api.github.com/users/NebulousLabs/repos","events_url":"https://api.github.com/users/NebulousLabs/events{/privacy}","received_events_url":"https://api.github.com/users/NebulousLabs/received_events","type":"Organization","site_admin":false},"repo":{"id":25370072,"name":"Sia","full_name":"NebulousLabs/Sia","owner":{"login":"NebulousLabs","id":7471422,"avatar_url":"https://avatars.githubusercontent.com/u/7471422?v=3","gravatar_id":"","url":"https://api.github.com/users/NebulousLabs","html_url":"https://github.com/NebulousLabs","followers_url":"https://api.github.com/users/NebulousLabs/followers","following_url":"https://api.github.com/users/NebulousLabs/following{/other_user}","gists_url":"https://api.github.com/users/NebulousLabs/gists{/gist_id}","starred_url":"https://api.github.com/users/NebulousLabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NebulousLabs/subscriptions","organizations_url":"https://api.github.com/users/NebulousLabs/orgs","repos_url":"https://api.github.com/users/NebulousLabs/repos","events_url":"https://api.github.com/users/NebulousLabs/events{/privacy}","received_events_url":"https://api.github.com/users/NebulousLabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/NebulousLabs/Sia","description":"Blockchain-based marketplace for file storage","fork":false,"url":"https://api.github.com/repos/NebulousLabs/Sia","forks_url":"https://api.github.com/repos/NebulousLabs/Sia/forks","keys_url":"https://api.github.com/repos/NebulousLabs/Sia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NebulousLabs/Sia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NebulousLabs/Sia/teams","hooks_url":"https://api.github.com/repos/NebulousLabs/Sia/hooks","issue_events_url":"https://api.github.com/repos/NebulousLabs/Sia/issues/events{/number}","events_url":"https://api.github.com/repos/NebulousLabs/Sia/events","assignees_url":"https://api.github.com/repos/NebulousLabs/Sia/assignees{/user}","branches_url":"https://api.github.com/repos/NebulousLabs/Sia/branches{/branch}","tags_url":"https://api.github.com/repos/NebulousLabs/Sia/tags","blobs_url":"https://api.github.com/repos/NebulousLabs/Sia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NebulousLabs/Sia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NebulousLabs/Sia/git/refs{/sha}","trees_url":"https://api.github.com/repos/NebulousLabs/Sia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NebulousLabs/Sia/statuses/{sha}","languages_url":"https://api.github.com/repos/NebulousLabs/Sia/languages","stargazers_url":"https://api.github.com/repos/NebulousLabs/Sia/stargazers","contributors_url":"https://api.github.com/repos/NebulousLabs/Sia/contributors","subscribers_url":"https://api.github.com/repos/NebulousLabs/Sia/subscribers","subscription_url":"https://api.github.com/repos/NebulousLabs/Sia/subscription","commits_url":"https://api.github.com/repos/NebulousLabs/Sia/commits{/sha}","git_commits_url":"https://api.github.com/repos/NebulousLabs/Sia/git/commits{/sha}","comments_url":"https://api.github.com/repos/NebulousLabs/Sia/comments{/number}","issue_comment_url":"https://api.github.com/repos/NebulousLabs/Sia/issues/comments{/number}","contents_url":"https://api.github.com/repos/NebulousLabs/Sia/contents/{+path}","compare_url":"https://api.github.com/repos/NebulousLabs/Sia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NebulousLabs/Sia/merges","archive_url":"https://api.github.com/repos/NebulousLabs/Sia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NebulousLabs/Sia/downloads","issues_url":"https://api.github.com/repos/NebulousLabs/Sia/issues{/number}","pulls_url":"https://api.github.com/repos/NebulousLabs/Sia/pulls{/number}","milestones_url":"https://api.github.com/repos/NebulousLabs/Sia/milestones{/number}","notifications_url":"https://api.github.com/repos/NebulousLabs/Sia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NebulousLabs/Sia/labels{/name}","releases_url":"https://api.github.com/repos/NebulousLabs/Sia/releases{/id}","created_at":"2014-10-17T17:50:25Z","updated_at":"2016-01-06T05:55:35Z","pushed_at":"2016-01-10T04:05:15Z","git_url":"git://github.com/NebulousLabs/Sia.git","ssh_url":"git@github.com:NebulousLabs/Sia.git","clone_url":"https://github.com/NebulousLabs/Sia.git","svn_url":"https://github.com/NebulousLabs/Sia","homepage":"https://sia.tech","size":26486,"stargazers_count":139,"watchers_count":139,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":22,"mirror_url":null,"open_issues_count":21,"forks":22,"open_issues":21,"watchers":139,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/NebulousLabs/Sia/pulls/898"},"html":{"href":"https://github.com/NebulousLabs/Sia/pull/898"},"issue":{"href":"https://api.github.com/repos/NebulousLabs/Sia/issues/898"},"comments":{"href":"https://api.github.com/repos/NebulousLabs/Sia/issues/898/comments"},"review_comments":{"href":"https://api.github.com/repos/NebulousLabs/Sia/pulls/898/comments"},"review_comment":{"href":"https://api.github.com/repos/NebulousLabs/Sia/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/NebulousLabs/Sia/pulls/898/commits"},"statuses":{"href":"https://api.github.com/repos/NebulousLabs/Sia/statuses/bee35e2b75e14f01768f0a383b5954939b60e724"}}}}