Merge branch 'rc-0.14.3' of https://github.com/blockstack/blockstack-core into rc-0.14.3

This commit is contained in:
Jude Nelson
2017-07-06 19:13:21 -04:00
9 changed files with 1393 additions and 117 deletions

View File

@@ -16,6 +16,7 @@ For more info on Blockstack see: http://github.com/blockstack/blockstack
- [Quick Start](#quick-start)
- [Development Status](#development-status)
- [Blockstack Docs](#blockstack-docs)
- [API Docs](#api-docs)
- [Contributing](#contributing)
- [Community](#community)
@@ -90,6 +91,11 @@ You can also read the Blockstack paper:
If you have high-level questions about Blockstack, try [searching our forum](https://forum.blockstack.org) and start a new question if your question is not answered there.
## API Docs
A local core node will expose a RESTful API after starting the api with `blockstack api start`.
Documentation for that lives [here](https://kantai.github.io/blockstack-core/), which is generated from blueprint markdown [here](docs/api-specs.md).
## Contributing
We welcome any small or big contributions! Please take a moment to

View File

@@ -2153,7 +2153,7 @@ class BlockstackAPIEndpointHandler(SimpleHTTPRequestHandler):
new_password = str(request['password'])
internal = self.server.get_internal_proxy()
res = internal.cli_wallet_passwod(password, new_password, config_path=self.server.config_path, interactive=False)
res = internal.cli_wallet_password(password, new_password, config_path=self.server.config_path, interactive=False)
if 'error' in res:
log.debug("Failed to change wallet password: {}".format(res['error']))
return self._reply_json({'error': 'Failed to change password: {}'.format(res['error'])}, status_code=500)

11
build_docs.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
PREVIOUS_BRANCH=$(git branch | grep ^\* | awk '{print $2}')
aglio -i docs/api-specs.md --theme-template docs/aglio_templates/core.jade -o /tmp/index.html
git checkout gh-pages
cp /tmp/index.html .
git add index.html
git commit -m "updating generated doc outputs"
git push
git checkout $PREVIOUS_BRANCH

View File

@@ -0,0 +1,36 @@
doctype
include mixins.jade
html
head
meta(charset="utf-8")
title= self.api.name || 'API Documentation'
link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css")
style!= self.css
body.preload
#nav-background
div.container-fluid.triple
.row
block nav
+Nav(false)
.content
#right-panel-background
block content
+ContentTriple(false)
.middle
p.text-muted(style="text-align: center;")
script: include scripts.js
if self.livePreview
script(src="/socket.io/socket.io.js")
script.
var socket = io();
socket.on('refresh', refresh);
socket.on('reconnect', function () {
socket.emit('request-refresh');
});

View File

@@ -0,0 +1,349 @@
mixin TryMe(action)
//- Give a "try-me" link for the public api endpoint
- var myUri = action.uriTemplate
- action.parameters.forEach( function (x) { myUri = myUri.replace( "{" + x.name + "}", x.example) } )
.title
strong
h4
div
span.method(class="badge get")
a.method(href="https://core.blockstack.org" + myUri, style="color:white;font-size:12pt")
= "Try It!"
|  
p
mixin Badge(method)
//- Draw a badge for a given HTTP method
case method
when 'GET'
span.badge.get: i.fa.fa-arrow-down
when 'HEAD'
span.badge.head: i.fa.fa-info-circle
when 'OPTIONS'
span.badge.options: i.fa.fa-dot-circle-o
when 'POST'
span.badge.post: i.fa.fa-plus
when 'PUT'
span.badge.put: i.fa.fa-pencil
when 'PATCH'
span.badge.patch: i.fa.fa-pencil
when 'DELETE'
span.badge.delete: i.fa.fa-times
default
span.badge: i.fa.fa-dot-circle-o
mixin Nav(onlyPublic)
//- Draw a navigation bar, which includes links to individual
//- resources and actions.
nav
if self.api.navItems && self.api.navItems.length
.resource-group
.heading
.chevron
i.open.fa.fa-angle-down
a(href='#top') Overview
.collapse-content
ul: each item in self.api.navItems
li
a(href=item[1])!= item[0]
- if (onlyPublic){
- myGroups = self.api.resourceGroups.filter( filter_public_resourcegroups )
- }else{
- myGroups = self.api.resourceGroups
- }
each resourceGroup in myGroups || []
.resource-group
.heading
.chevron
i.open.fa.fa-angle-down
a(href=resourceGroup.elementLink)!= resourceGroup.name || 'Resource Group'
.collapse-content
ul
each item in resourceGroup.navItems || []
li
a(href=item[1])!= item[0]
- if (onlyPublic){
- myResources = resourceGroup.resources.filter( filter_public_resources )
- }else{
- myResources = resourceGroup.resources
- }
each resource in myResources || []
li
- if (onlyPublic){
- myActions = resource.actions.filter( filter_public_actions )
- }else{
- myActions = resource.actions
- }
if !self.condenseNav || (myActions.length != 1)
a(href=resource.elementLink)!= resource.name || 'Resource'
ul: each action in myActions || []
li: a(href=resource.elementLink)
+Badge(action.method)
!= action.name || action.method + ' ' + (action.attributes && action.attributes.uriTemplate || resource.uriTemplate)
else
- var action = myActions[0]
a(href=resource.elementLink)
+Badge(action.method)
!= action.name || resource.name || action.method + ' ' + (action.attributes && action.attributes.uriTemplate || resource.uriTemplate)
//- Link to the API hostname, e.g. api.yourcompany.com
each meta in self.api.metadata || {}
if meta.name == 'HOST'
p(style="text-align: center; word-wrap: break-word;")
a(href=meta.value)= meta.value
mixin Parameters(params)
//- Draw a definition list of parameter names, types, defaults,
//- examples and descriptions.
.title
strong URI Parameters
.collapse-button.show
span.close Hide
span.open Show
.collapse-content
dl.inner: each param in params || []
dt= self.urldec(param.name)
dd
code= param.type || 'string'
|  
if param.required
span.required (required)
else
span (optional)
|  
if param.default
span.text-info.default
strong Default: 
span= param.default
|  
if param.example
span.text-muted.example
strong Example: 
span= param.example
!= self.markdown(param.description)
if param.values.length
p.choices
strong Choices: 
each value in param.values
code= self.urldec(value.value)
= ' '
mixin RequestResponse(title, request, collapse)
.title
strong
= title
if request.name
|   
code= request.name
if collapse && request.hasContent
.collapse-button
span.close Hide
span.open Show
+RequestResponseBody(request, collapse)
mixin RequestResponseBody(request, collapse, showBlank)
if request.hasContent || showBlank
div(class=collapse ? 'collapse-content' : ''): .inner
if request.description
.description!= self.markdown(request.description)
if Object.keys(request.headers).length
h5 Headers
pre: code
each item, index in request.headers
!= self.highlight(item.name + ': ' + item.value, 'http')
if index < request.headers.length - 1
br
div(style="height: 1px;")
if request.body
h5 Body
pre: code
!= self.highlight(request.body, null, ['json', 'yaml', 'xml', 'javascript'])
div(style="height: 1px;")
if request.schema
h5 Schema
pre: code
!= self.highlight(request.schema, null, ['json', 'yaml', 'xml'])
div(style="height: 1px;")
if !request.hasContent
.description.text-muted This response has no content.
div(style="height: 1px;")
mixin Examples(resourceGroup, resource, action)
each example in action.examples
each request in example.requests
+RequestResponse('Request', request, true)
each response in example.responses
+RequestResponse('Response', response, true)
mixin Content()
//- Page header and API description
header
h1#top!= self.api.name || 'API Documentation'
if self.api.descriptionHtml
!= self.api.descriptionHtml
//- Loop through and display information about all the resource
//- groups, resources, and actions.
each resourceGroup in self.api.resourceGroups || []
section.resource-group(id=resourceGroup.elementId)
h2.group-heading
!= resourceGroup.name || 'Resource Group'
= " "
a.permalink(href=resourceGroup.elementLink) &para;
if resourceGroup.descriptionHtml
!= resourceGroup.descriptionHtml
each resource in resourceGroup.resources || []
.resource(id=resource.elementId)
h3.resource-heading
!= resource.name || ((resource.actions[0] != null) && resource.actions[0].name) || 'Resource'
= " "
a.permalink(href=resource.elementLink) &nbsp;&para;
if resource.description
!= self.markdown(resource.description)
each action in resource.actions || []
.action(class=action.methodLower, id=action.elementId)
h4.action-heading
.name!= action.name
a.method(class=action.methodLower, href=action.elementLink)
= action.method
code.uri= self.urldec(action.uriTemplate)
if action.description
!= self.markdown(action.description)
h4 Example URI
.definition
span.method(class=action.methodLower)= action.method
| &nbsp;
span.uri
span.hostname= self.api.host
!= action.colorizedUriTemplate
//- A list of sub-sections for parameters, requests
//- and responses.
if action.parameters.length
+Parameters(action.parameters)
if action.examples
+Examples(resourceGroup, resource, action)
- function filter_public_actions(x){
- return x.description.includes('+ Public Endpoint')
- }
- function filter_public_resources(x){
- return (x.actions.filter( filter_public_actions ).length > 0)
- }
- function filter_public_resourcegroups(x){
- return (x.resources.filter( filter_public_resources ).length > 0)
- }
mixin ContentTriple(onlyPublic)
.middle
//- Page header and API description
header
h1#top!= self.api.name || 'API Documentation'
.right
h5 API Endpoint
a(href=self.api.host)= self.api.host
.middle
if self.api.descriptionHtml
!= self.api.descriptionHtml
//- Loop through and display information about all the resource
//- groups, resources, and actions.
- if (onlyPublic){
- myGroups = self.api.resourceGroups.filter( filter_public_resourcegroups )
- }else{
- myGroups = self.api.resourceGroups
- }
each resourceGroup in myGroups || []
.middle
section.resource-group(id=resourceGroup.elementId)
h2.group-heading
!= resourceGroup.name || 'Resource Group'
= " "
a.permalink(href=resourceGroup.elementLink) &para;
if resourceGroup.descriptionHtml
!= resourceGroup.descriptionHtml
- if (onlyPublic){
- myResources = resourceGroup.resources.filter( filter_public_resources )
- }else{
- myResources = resourceGroup.resources
- }
each resource in myResources || []
if resource.public != null
.middle
.resource(id=resource.elementId)
a.permalink(href=resource.elementLink)
h3.resource-heading
!= resource.name || ((resource.actions[0] != null) && resource.actions[0].name) || 'Resource'
= " "
&para;
if resource.description
!= self.markdown(resource.description)
- if (onlyPublic){
- myActions = resource.actions.filter( filter_public_actions )
- }else{
- myActions = resource.actions
- }
each action in myActions || []
if action.examples
.right
.definition
span.method(class=action.methodLower)= action.method
| &nbsp;
span.uri
span.hostname= self.api.host
!= action.colorizedUriTemplate
.tabs
if action.hasRequest
.example-names
span Requests
- var requestCount = 0
each example in action.examples
each request in example.requests
- requestCount++
span.tab-button= request.name || 'example ' + requestCount
each example in action.examples
each request in example.requests
.tab
+RequestResponseBody(request, false, true)
.tabs
.example-names
span Responses
each response in example.responses
span.tab-button= response.name
each response in example.responses
.tab
+RequestResponseBody(response, false, true)
else
each example in action.examples
.tabs
.example-names
span Responses
each response in example.responses
span.tab-button= response.name
each response in example.responses
.tab
+RequestResponseBody(response, false, true)
.middle
.action(class=action.methodLower, id=action.elementId)
h4.action-heading
.name!= action.name
a.method(class=action.methodLower, href=action.elementLink)
= action.method
code.uri= self.urldec(action.uriTemplate)
if action.description
!= self.markdown(action.description)
//- A list of sub-sections for parameters, requests
//- and responses.
if action.parameters.length
+Parameters(action.parameters)
if onlyPublic
+TryMe(action)
hr.split

View File

@@ -0,0 +1,36 @@
doctype
include mixins.jade
html
head
meta(charset="utf-8")
title= self.api.name || 'API Documentation'
link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css")
style!= self.css
body.preload
#nav-background
div.container-fluid.triple
.row
block nav
+Nav(true)
.content
#right-panel-background
block content
+ContentTriple(true)
.middle
p.text-muted(style="text-align: center;")
script: include scripts.js
if self.livePreview
script(src="/socket.io/socket.io.js")
script.
var socket = io();
socket.on('refresh', refresh);
socket.on('reconnect', function () {
socket.emit('request-refresh');
});

View File

@@ -0,0 +1,223 @@
/* eslint-env browser */
/* eslint quotes: [2, "single"] */
'use strict';
/*
Determine if a string ends with another string.
*/
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
/*
Get a list of direct child elements by class name.
*/
function childrenByClass(element, name) {
var filtered = [];
for (var i = 0; i < element.children.length; i++) {
var child = element.children[i];
var classNames = child.className.split(' ');
if (classNames.indexOf(name) !== -1) {
filtered.push(child);
}
}
return filtered;
}
/*
Get an array [width, height] of the window.
*/
function getWindowDimensions() {
var w = window,
d = document,
e = d.documentElement,
g = d.body,
x = w.innerWidth || e.clientWidth || g.clientWidth,
y = w.innerHeight || e.clientHeight || g.clientHeight;
return [x, y];
}
/*
Collapse or show a request/response example.
*/
function toggleCollapseButton(event) {
var button = event.target.parentNode;
var content = button.parentNode.nextSibling;
var inner = content.children[0];
if (button.className.indexOf('collapse-button') === -1) {
// Clicked without hitting the right element?
return;
}
if (content.style.maxHeight && content.style.maxHeight !== '0px') {
// Currently showing, so let's hide it
button.className = 'collapse-button';
content.style.maxHeight = '0px';
} else {
// Currently hidden, so let's show it
button.className = 'collapse-button show';
content.style.maxHeight = inner.offsetHeight + 12 + 'px';
}
}
function toggleTabButton(event) {
var i, index;
var button = event.target;
// Get index of the current button.
var buttons = childrenByClass(button.parentNode, 'tab-button');
for (i = 0; i < buttons.length; i++) {
if (buttons[i] === button) {
index = i;
button.className = 'tab-button active';
} else {
buttons[i].className = 'tab-button';
}
}
// Hide other tabs and show this one.
var tabs = childrenByClass(button.parentNode.parentNode, 'tab');
for (i = 0; i < tabs.length; i++) {
if (i === index) {
tabs[i].style.display = 'block';
} else {
tabs[i].style.display = 'none';
}
}
}
/*
Collapse or show a navigation menu. It will not be hidden unless it
is currently selected or `force` has been passed.
*/
function toggleCollapseNav(event, force) {
var heading = event.target.parentNode;
var content = heading.nextSibling;
var inner = content.children[0];
if (heading.className.indexOf('heading') === -1) {
// Clicked without hitting the right element?
return;
}
if (content.style.maxHeight && content.style.maxHeight !== '0px') {
// Currently showing, so let's hide it, but only if this nav item
// is already selected. This prevents newly selected items from
// collapsing in an annoying fashion.
if (force || window.location.hash && endsWith(event.target.href, window.location.hash)) {
content.style.maxHeight = '0px';
}
} else {
// Currently hidden, so let's show it
content.style.maxHeight = inner.offsetHeight + 12 + 'px';
}
}
/*
Refresh the page after a live update from the server. This only
works in live preview mode (using the `--server` parameter).
*/
function refresh(body) {
document.querySelector('body').className = 'preload';
document.body.innerHTML = body;
// Re-initialize the page
init();
autoCollapse();
document.querySelector('body').className = '';
}
/*
Determine which navigation items should be auto-collapsed to show as many
as possible on the screen, based on the current window height. This also
collapses them.
*/
function autoCollapse() {
var windowHeight = getWindowDimensions()[1];
var itemsHeight = 64; /* Account for some padding */
var itemsArray = Array.prototype.slice.call(
document.querySelectorAll('nav .resource-group .heading'));
// Get the total height of the navigation items
itemsArray.forEach(function (item) {
itemsHeight += item.parentNode.offsetHeight;
});
// Should we auto-collapse any nav items? Try to find the smallest item
// that can be collapsed to show all items on the screen. If not possible,
// then collapse the largest item and do it again. First, sort the items
// by height from smallest to largest.
var sortedItems = itemsArray.sort(function (a, b) {
return a.parentNode.offsetHeight - b.parentNode.offsetHeight;
});
while (sortedItems.length && itemsHeight > windowHeight) {
for (var i = 0; i < sortedItems.length; i++) {
// Will collapsing this item help?
var itemHeight = sortedItems[i].nextSibling.offsetHeight;
if ((itemsHeight - itemHeight <= windowHeight) || i === sortedItems.length - 1) {
// It will, so let's collapse it, remove its content height from
// our total and then remove it from our list of candidates
// that can be collapsed.
itemsHeight -= itemHeight;
toggleCollapseNav({target: sortedItems[i].children[0]}, true);
sortedItems.splice(i, 1);
break;
}
}
}
}
/*
Initialize the interactive functionality of the page.
*/
function init() {
var i, j;
// Make collapse buttons clickable
var buttons = document.querySelectorAll('.collapse-button');
for (i = 0; i < buttons.length; i++) {
buttons[i].onclick = toggleCollapseButton;
// Show by default? Then toggle now.
if (buttons[i].className.indexOf('show') !== -1) {
toggleCollapseButton({target: buttons[i].children[0]});
}
}
var responseCodes = document.querySelectorAll('.example-names');
for (i = 0; i < responseCodes.length; i++) {
var tabButtons = childrenByClass(responseCodes[i], 'tab-button');
for (j = 0; j < tabButtons.length; j++) {
tabButtons[j].onclick = toggleTabButton;
// Show by default?
if (j === 0) {
toggleTabButton({target: tabButtons[j]});
}
}
}
// Make nav items clickable to collapse/expand their content.
var navItems = document.querySelectorAll('nav .resource-group .heading');
for (i = 0; i < navItems.length; i++) {
navItems[i].onclick = toggleCollapseNav;
// Show all by default
toggleCollapseNav({target: navItems[i].children[0]});
}
}
// Initial call to set up buttons
init();
window.onload = function () {
autoCollapse();
// Remove the `preload` class to enable animations
document.querySelector('body').className = '';
};

View File

@@ -1,150 +1,758 @@
# Blockstack Specifications
# Group Authorization
## Dashboard Endpoints
## Auth Request View [GET /auth?authRequest={authRequestToken}]
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Dashboard Home | GET / | identity | Serves the identity management panel |
| Auth Request View | GET /auth?authRequest={authRequestToken} | identity | Serves the auth request view |
When the user clicks “login” in an application, the app should
redirect the user to this endpoint. If the user already has an
account, they will be redirected along with requested data. If the
user doesnt have an account, the user will be presented with each of
the apps requested permissions, then will satisfy or deny them. The
dashboard will then redirect the user back with a JWT. The response
JWT contains a signature and an API token that the app can use for
future authorization of endpoints.
#### Explanation of the auth request view:
Each application specifies in advance which family of API calls it
will need to make to function properly. This list is passed along to
the dashboard endpoint when creating an application account. The
account-creation page shows this list of API endpoints and what they
do, and allows the user to line-item approve or deny them. The list
is stored by the API server in the local account structure, and the
list is given to the application as part of the session JWT. The API
server will NACK requests to endpoints in API families absent from the
session JWT.
When the user clicks “login” in an application, the app should redirect the user to this endpoint. If the user already has an account, they will be redirected along with requested data. If the user doesnt have an account, the user will be presented with each of the apps requested permissions, then will satisfy or deny them. The dashboard will then redirect the user back with a JWT. The response JWT contains a signature and an API token that the app can use for future authorization of endpoints.
+ Requires root authorization
+ Parameters
+ authRequestToken: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJhcHBfZG9tYWluIjoiaGVsbG9ibG9ja3N0YWNrLmNvbSIsIm1ldGhvZHMiOltdLCJhcHBfcHVibGljX2tleSI6IjAyYjk0ZjY4NDgzOGFkMjdmZTE0Nzk1MGMyNjQ1ZjRhYzhjYmU1OTJlYjYzYmQwYTQ5MWQ2YzBlYWZjNjE0YzVjMCJ9.0lLrxt8uGtB2rCKB9sb0jK1DdrrWuuuWM-nsyjvFnmjNx0XfG14Npl72w6hp9W2OHoXdPe7VuXkfvKmVNlQdeA (jwt token) - app token before signing
+ Response 200
+ Body
Each application specifies in advance which family of API calls it will need to make to function properly. This list is passed along to the dashboard endpoint when creating an application account. The account-creation page shows this list of API endpoints and what they do, and allows the user to line-item approve or deny them. The list is stored by the API server in the local account structure, and the list is given to the application as part of the session JWT. The API server will NACK requests to endpoints in API families absent from the session JWT.
{"token":
"eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJhcHBfZG9tYWluIjoiaGVsbG9ibG9ja3N0YWNrLmNvbSIsIm1ldGhvZHMiOltdLCJ0aW1lc3RhbXAiOjE0OTkzNDc4OTUsImV4cGlyZXMiOjE0OTk5NTI2OTUsImFwcF91c2VyX2lkIjoiMUVITmE2UTRKejJ1dk5FeEw0OTdtRTQzaWtYaHdGNmtabSIsImRldmljZV9pZCI6IjAiLCJibG9ja2NoYWluX2lkIjpudWxsLCJzdG9yYWdlIjp7ImNsYXNzZXMiOnsid3JpdGVfcHJpdmF0ZSI6WyJkaXNrIiwiczMiLCJibG9ja3N0YWNrX3NlcnZlciIsImRodCJdLCJyZWFkX2xvY2FsIjpbImRpc2siXSwicmVhZF9wdWJsaWMiOlsiczMiLCJibG9ja3N0YWNrX3Jlc29sdmVyIiwiYmxvY2tzdGFja19zZXJ2ZXIiLCJodHRwIiwiZGh0Il0sIndyaXRlX2xvY2FsIjpbImRpc2siXSwid3JpdGVfcHVibGljIjpbXSwicmVhZF9wcml2YXRlIjpbImRpc2siXX0sInByZWZlcmVuY2VzIjp7fX0sImFwaV9lbmRwb2ludCI6ImxvY2FsaG9zdDo2MjcwIiwiYXBwX3B1YmxpY19rZXlzIjpbXSwidmVyc2lvbiI6MX0.Bhne8wQpPVfkV-VLf2mrsoMmNiE2e04crgLN7OUFKEh_YWeGmqjoZU7JVSzXA5r7LCpZ9Eki5uAWlJSHk-JuCA"
}
## Administrative API
# Group Core Node Administration
## Ping the node [GET /v1/node/ping]
Ping the blockstack node to see if it's alive.
+ Public Endpoint
+ Response 200 (application/json)
+ Body
{
"status": "alive",
"version": "0.14.2"
}
### Node
## Get the node's config [GET /v1/node/config]
Returns the current configuation settings of the blockstack node.
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Ping the node | GET /v1/node/ping | - | Requires pre-shared secret in the `Authorization:` header |
| - | - | - | - |
| Get the node's config | GET /v1/node/config | - | Requires pre-shared secret in the `Authorization:` header. Returns a dict with the config file |
| Set one or more config fields in a config section | POST /v1/node/config/{section}?{key}={value} | - | Requires pre-shared secret in the `Authorization:` header. |
| Delete a config field | DELETE /v1/node/config/{section}/{key} | - | Requires pre-shared secret in the `Authorization:` header. |
| Delete a config section | DELETE /v1/node/config/{section} | - | Requires pre-shared secret in the `Authorization:` header. |
| - | - | - | - |
| Get registrar state | GET /v1/node/registrar/state | - | Requires pre-shared secret in the `Authorization:` header. |
+ Response 200 (application/json)
+ Body
### Wallet
{
"bitcoind": {
"passwd": "blockstacksystem",
"port": "18332",
"regtest": "True",
"server": "localhost",
"spv_path": "/tmp/.../spv_headers.dat",
"use_https": "False",
"user": "blockstack"
},
"blockchain-reader": {
"port": "18332",
"rpc_password": "blockstacksystem",
"rpc_username": "blockstack",
"server": "localhost",
"use_https": "False",
"utxo_provider": "bitcoind_utxo",
"version_byte": "0"
},
"blockchain-writer": {
"port": "18332",
"rpc_password": "blockstacksystem",
"rpc_username": "blockstack",
"server": "localhost",
"use_https": "False",
"utxo_provider": "bitcoind_utxo",
"version_byte": "0"
},
"blockstack-client": {
"accounts": "/tmp/.../client/app_accounts",
"advanced_mode": "true",
"anonymous_statistics": false,
"api_endpoint_port": "16268",
"api_password": "blockstack_integration_test_api_password",
"blockchain_reader": "bitcoind_utxo",
"blockchain_writer": "bitcoind_utxo",
"client_version": "0.14.3.0",
"datastores": "/tmp/.../client/datastores",
"email": "",
"metadata": "/tmp/.../client/metadata",
"poll_interval": "1",
"port": "16264",
"queue_path": "/tmp/.../client/queues.db",
"rpc_detach": "True",
"server": "localhost",
"storage_drivers": "disk",
"storage_drivers_required_write": "disk",
"users": "/tmp/.../client/users"
}
}
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Get wallet payment address | GET /v1/wallet/payment_address | wallet_read | - |
| Get wallet owner address | GET /v1/wallet/owner_address | wallet_read | - |
| Get wallet data public key | GET /v1/wallet/data_pubkey | wallet_read | - |
| - | - | - | - |
| Set the wallet | PUT /v1/wallet/keys | - | Requires a pre-shared secret in the `Authorization:` header |
| Get the wallet | GET /v1/wallet/keys | - | Requires a pre-shared secret in the `Authorization:` header |
| - | - | - | - |
| Get the wallet balance | GET /v1/wallet/balance | wallet_read | - |
| Get the wallet balance, specifying the minconfs for txns included | GET /v1/wallet/balance/{minconfs} | wallet_read | - |
| Withdraw funds from the wallet | POST /v1/wallet/balance | wallet_write | Payload: `{'address': str, 'amount': int, 'min_confs': int, 'tx_only': bool} |
| - | - | - | - |
| Change wallet password | PUT /v1/wallet/password | wallet_write | Payload: `{'password': ..., 'new_password': ...}`|
## Set config field [POST /v1/node/config/{section}?{key}={value}]
Set one or more config fields in a config section.
### Authorization
+ Parameters
+ section: blockstack-client (string) - configuration section
+ key: server (string) - configuration variable to set
+ value: node.blockstack.org (string) - value to set
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Create an authorization token | GET /v1/auth?authRequest={authRequestToken} | - | Requires a pre-shared secret in the `Authorization:` header. |
+ Response 200 (application/json)
TODO: authRequestToken format
{ 'status' : true }
## Naming API
## Delete a config field [DELETE /v1/node/config/{section}/{key}]
Delete a single field from the configuration.
+ Parameters
+ section: blockstack-client (string) - configuration section
+ key: advanced_mode (string) - configuration variable to set
### Names
+ Response 200 (application/json)
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Get all names | GET /v1/names | names | - |
| Register name | POST /v1/names | register | Payload: {"name": NAME, "zonefile" : ZONEFILE, "owner_address" : OWNER, "min_confs" : MIN_PAYMENT_CONFS, "unsafe" : REGISTRATION_SAFETY} Required: "name" |
| Get name info | GET /v1/names/{name} | names | - |
| Get name history | GET /v1/names/{name}/history | names | - |
| Get historical zone file | GET /names/{name}/zonefile/{zoneFileHash} | zonefiles | - |
| Revoke name | DELETE /v1/names/{name} | revoke | - |
| Transfer name | PUT /v1/names/{name}/owner | transfer | Payload: {"owner": OWNER } |
| Set zone file | PUT /v1/names/{name}/zonefile | update | Payload: {"zonefile": ZONE_FILE } |
| Set zone file hash | PUT /v1/names/{name}/zonefile | update | Payload: {"zonefile_hash": ZONE_FILE_HASH } |
{ 'status' : true }
### Addresses
## Delete a config section [DELETE /v1/node/config/{section}]
Deletes a whole section from the node's configuration.
+ Parameters
+ section: blockstack-client (string) - configuration section
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Get names owned by address | GET /v1/addresses/{address} | names | - |
+ Response 200 (application/json)
### Namespaces
{ 'status' : true }
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Get all namespaces | GET /v1/namespaces | namespaces | - |
| Create namespace | POST /v1/namespaces | namespace_registration | NOT IMPLEMENTED |
| Launch namespace | PUT /v1/namespaces/{tld} | namespace_registration | NOT IMPLEMENTED |
| Get namespace names | GET /v1/namespaces/{tld}/names | namespaces | - |
| Pre-register a name | POST /v1/namespaces/{tld}/names | namespace_registration | NOT IMPLEMENTED |
| Update pre-registered name | PUT /v1/namespaces/{tld}/names/{name} | namespace_registration | NOT IMPLEMENTED |
## Get registrar state [GET /v1/node/registrar/state]
Gets the current state of the registrar. That is, the blockstack operations
that have been submitted that are still waiting on confirmations.
### Prices
+ Requires root authorization
+ Response 200 (application/json)
+ Body
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Get namespace price | GET /v1/prices/namespaces/{tld} | prices | May return a warning if the wallet does not have enough funds |
| Get name price | GET /v1/prices/names/{name} | prices | May return a warning if the wallet does not have enough funds |
[
{
"block_height": 666,
"fqu": "bar.test",
"owner_address": "myaPViveUWiiZQQTb51KXCDde4iLC3Rf3K",
"payment_address": "mv1uqYWZpnap4VBSKTHfKW6noTZcNtxtCW",
"profile": {
"@type": "Person",
"accounts": []
},
"transfer_address": null,
"tx_hash": "b0fa7d4d79bb69cb3eccf40978514dec1620d05fe7822c550c2764c670efcd29",
"type": "preorder",
"zonefile": "$ORIGIN bar.test\n$TTL 3600\npubkey TXT \"pubkey:data:03ea5d8c2a3ba84eb17625162320bb53440557c71f7977a57d61405e86be7bdcda\"\n_file URI 10 1 \"file:///home/bar/.blockstack/storage-disk/mutable/bar.test\"\n",
"zonefile_hash": "cbe11bbbfffe415b915a7f9566748f72a0d8b2bd"
}
]
# Group Core Wallet Management
The blockstack core node manages its own wallet -- this has three keys
for payment, name ownership, and signing data (e.g., user profiles). This
wallet can be managed through these endpoints.
## Get wallet payment address [GET /v1/wallet/payment_address]
Returns core node's payment address.
+ Authorization: `wallet_read`
+ Response 200 (application/json)
+ Body
{
"address": "mv1uqYWZpnap4VBSKTHfKW6noTZcNtxtCW"
}
## Set a specific wallet key [PUT /v1/wallet/keys/{keyname}]
This call instructs the blockstack core node to use a particular key
instead of the core node's configured wallet key. The setting of this
key is *temporary*. It is not written to `~/.blockstack/wallet.json`,
and on a subsequent restart, the key will return to the original key.
Therefore, particular care should be taken when registering with such
a key, as the registrar may be unable to issue a `REGISTER` or
`UPDATE` if the node restarts in the middle of the registration process.
+ Requires root authorization
+ Parameters
+ keyname: owner (string) - which key to set (one of 'owner', 'data', 'payment')
+ Request (application/json)
+ Schema
{
"anyOf": [
{
"anyOf": [
{
"pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$",
"type": "string"
},
{
"pattern": "^([0-9a-fA-F]+)$",
"type": "string"
}
]
},
{
"properties": {
"address": {
"pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$",
"type": "string"
},
"private_keys": {
"items": {
"anyOf": [
{
"pattern": "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)$",
"type": "string"
},
{
"pattern": "^([0-9a-fA-F]+)$",
"type": "string"
}
]
},
"type": "array"
},
"redeem_script": {
"pattern": "^([0-9a-fA-F]+)$",
"type": "string"
}
},
"required": [
"address",
"redeem_script",
"private_keys"
],
"type": "object"
}
]
}
## Get payment wallet balance [GET /v1/wallet/balance/{minconfs}]
Fetches wallet balance, including UTXOs from transactions with at
least a specified number of confirmations.
+ Authorization: `wallet_read`
+ Parameters
+ minconfs: 0 (number, optional) - the minimum confs of transactions to include in balance
+ Response 200 (application/json)
+ Body
{
"balance": {
"bitcoin": 49.931727,
"satoshis": 4993172700
}
}
## Withdraw payment wallet funds [POST /v1/wallet/balance]
Withdraw an amount (given in satoshis) from the core payment
wallet, to a particular address.
+ Authorization: `wallet_write`
+ Request (application/json)
+ Body
{'address' : 'mibZW6EBpXSTWQNQ9E4fi9hhGKYSMkjyg9',
'amount' : 100,
'min_confs' : 6,
'tx_only' : false}
+ Response 200 (application/json)
+ Body
{
"status": true,
"transaction_hash": "c4ee8d1993794487e6b5aca802a1793530bdff35c763ca051fbaa4b998780822",
"success": true
}
## Get wallet owner address [GET /v1/wallet/owner_address]
Returns core node's owner address.
+ Authorization: `wallet_read`
+ Response 200 (application/json)
+ Body
{
"address": "myaPViveUWiiZQQTb51KXCDde4iLC3Rf3K"
}
## Get wallet data public key [GET /v1/wallet/data_pubkey]
Returns the public key the core node uses for signing user data
+ Authorization: `wallet_read`
+ Response 200 (application/json)
+ Body
{
"public_key": "03ea5d8c2a3ba84eb17625162320bb53440557c71f7977a57d61405e86be7bdcda"
}
## Change wallet password [PUT /v1/wallet/password]
This will change the password for core's wallet. Currently not working endpoint.
+ Authorization: `wallet_write`
+ Request (application/json)
+ Body
{'password' : '"0123456789abcdef"',
'new_password' : "abcdef0123456789"'}
## Set all wallet keys [PUT /v1/wallet/keys]
+ Requires root authorization
## Get all wallet keys [GET /v1/wallet/keys]
+ Requires root authorization
# Group Managing Names
## Register a name [POST /v1/names]
Registers a name. If no `owner_address` is supplied in the POSTed JSON
object, core will register a name for the current owner address in core's
wallet. If an `owner_address` is supplied, a `TRANSFER` operation will be
called to send the name to appropriate owner.
The `min_confs` keyword controls the minimum number of confirmations for
UTXOs used as payments for name registration.
The `unsafe` keyword instructs core's registrar to ignore certain
safety checks while registering the name (in particular, the registrar
will not verify that the user own's the name before issuing a
`REGISTER` and `UPDATE`). This allows the registrar to submit
operations before they have been confirmed on remote resolvers or
indexers, in this mode, the registrar will wait for 4 confirmations on
a `PREORDER`, 1 confirmation on a `REGISTER` and 1 confirmation on an
`UPDATE`. `node.blockstack.org` will correctly detect the registration
after the `UPDATE` has 6 confirmations.
+ Authorization: `register`
+ Request (application/json)
+ Body
{
'name' : 'bar.test'
}
+ Request (application/json)
+ Schema
{
'type': 'object',
'properties': {
"name": {
'type': 'string',
'pattern': OP_NAME_PATTERN
},
"zonefile": {
'type': 'string',
'maxLength': RPC_MAX_ZONEFILE_LEN,
},
"owner_address": {
'type': 'string',
'pattern': OP_BASE58CHECK_PATTERN,
},
'min_confs': {
'type': 'integer',
'minimum': 0,
},
'tx_fee': {
'type': 'integer',
'minimum': 0,
'maximum': TX_MAX_FEE,
},
'cost_satoshis': {
'type': 'integer',
'minimum': 0,
},
'unsafe': {
'type': 'boolean'
}
},
'required': [
'name'
],
'additionalProperties': False,
}
+ Response 200 (application/json)
+ Body
{
"message": "Name queued for registration. The process takes several hours. You can check the status with `blockstack info`.",
"success": true,
"transaction_hash": "6cdb9722f72875b30e1ab3de463e3960aced951f674be942b302581a9a9469a5"
}
## Revoke name [DELETE /v1/names/{name}]
Revokes the name from blockstack.
+ Parameters
+ name: bar.test (string) - fully-qualified name
+ Authorization: `revoke`
+ Response 200 (application/json)
+ Body
{
"message": "Name queued for revocation. The process takes ~1 hour. You can check the status with `blockstack info`.",
"success": true,
"transaction_hash": "b2745b706d7a14ce652265de103d7eaefb44a75eb658d7bb1db8868da08768b2"
}
## Transfer name [PUT /v1/names/{name}/owner]
Transfers a name to a different owner.
+ Authorization: `transfer`
+ Parameters
+ name: bar.test (string) - name to transfer
+ Request (application/json)
+ Body
{ "owner" : "mjZicz7GSJBZuGeCMEgpzr8U9w6d41DfXm" }
+ Response 202 (application/json)
{
"message": "Name queued for transfer. The process takes ~1 hour. You can check the status with `blockstack info`.",
"success": true,
"transaction_hash": "c0d677f9ee681abbed8ca6d231bc4ece517c8c6695ce883e5e196b5395402779"
}
## Set zone file [PUT /v1/names/{name}/zonefile]
Sets the user's zonefile hash, and, if supplied, propagates the
zonefile. If you supply the zonefile, the hash will be calculated from
that. Ultimately, your requests should only supply one of `zonefile`,
`zonefile_b64`, or `zonefile_hash`.
+ Authorization: `update`
+ Request (application/json)
+ Schema
request_schema = {
'type': 'object',
'properties': {
"zonefile": {
'type': 'string',
'maxLength': RPC_MAX_ZONEFILE_LEN,
},
'zonefile_b64': {
'type': 'string',
'maxLength': (RPC_MAX_ZONEFILE_LEN * 4) / 3 + 1,
},
'zonefile_hash': {
'type': 'string',
'pattern': OP_ZONEFILE_HASH_PATTERN,
},
'tx_fee': {
'type': 'integer',
'minimum': 0,
'maximum': TX_MAX_FEE
},
},
'additionalProperties': False,
}
+ Response 202 (application/json)
+ Body
{'transaction_hash' : '...'}
# Group Name Querying
This family of API endpoints deals with querying name information.
## Get all names [GET /v1/names?page={page}]
Fetch a list of all names known to the node.
+ Public Endpoint
+ Parameters
+ page: 23 (number) - names are returned in pages of size 100,
so specify the page number.
+ Response 200 (application/json)
+ Body
[ "aldenquimby.id", "aldeoryn.id",
"alderete.id", "aldert.id",
"aldi.id", "aldighieri.id", ... ]
## Get name info [GET /v1/names/{name}]
+ Public Endpoint
+ Parameters
+ name: muneeb.id (string) - fully-qualified name
+ Response 200 (application/json)
+ Body
{
"address": "1QJQxDas5JhdiXhEbNS14iNjr8auFT96GP",
"blockchain": "bitcoin",
"expire_block": 489247,
"last_txid": "1edfa419f7b83f33e00830bc9409210da6c6d1db60f99eda10c835aa339cad6b",
"status": "registered",
"zonefile": "$ORIGIN muneeb.id\n$TTL 3600\n_http._tcp IN URI 10 1 \"https://blockstack.s3.amazonaws.com/muneeb.id\"\n",
"zonefile_hash": "b100a68235244b012854a95f9114695679002af9"
}
## Name history [GET /v1/names/{name}/history]
Get a history of all blockchain records of a registered name.
+ Public Endpoint
+ Parameters
+ name: muneeb.id (string) - name to query
+ Response 200 (application/json)
+ Body
{
"373821": [
{
"address": "1QJQxDas5JhdiXhEbNS14iNjr8auFT96GP",
"block_number": 373821,
"consensus_hash": null,
"first_registered": 373821,
"importer": "76a9143e2b5fdd12db7580fb4d3434b31d4fe9124bd9f088ac",
"importer_address": "16firc3qZU97D1pWkyL6ZYwPX5UVnWc82V",
"last_creation_op": ";",
"last_renewed": 373821,
"name": "muneeb.id",
"name_hash128": "deb7fe99776122b77925cbf0a24ab6f8",
"namespace_block_number": 373601,
"namespace_id": "id",
"op": ";",
"op_fee": 100000.0,
"opcode": "NAME_IMPORT",
"preorder_block_number": 373821,
}
]
}
## Get historical zone file [GET /v1/names/{name}/zonefile/{zoneFileHash}]
Fetches the historical zonefile specified by the username and zone hash.
+ Public Endpoint
+ Parameters
+ name: muneeb.id (string) username to fetch
+ zoneFileHash: b100a68235244b012854a95f9114695679002af9
+ Response 200 (application/json)
+ Body
{
"zonefile":
"$ORIGIN muneeb.id\n$TTL 3600\n_http._tcp IN URI 10 1 \"https://blockstack.s3.amazonaws.com/muneeb.id\"\n"
}
## Get names owned by address [GET /v1/addresses/{blockchain}/{address}]
Retrieves a list of names owned by the address provided.
+ Public Endpoint
+ Parameters
+ blockchain: bitcoin (string) - the layer-1 blockchain for the address
+ address: 1QJQxDas5JhdiXhEbNS14iNjr8auFT96GP (string) - the address to lookup
+ Response 200 (application/json)
+ Body
{
"names": ["muneeb.id"]
}
### Blockchains
# Group Price Checks
## Get namespace price [GET /v1/prices/namespaces/{tld}]
+ Public Endpoint
+ Parameters
+ tld: id (string) - namespace to query price for
+ Response 200 (application/json)
+ Body
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Get block operations | GET /v1/blockchains/{blockchainName}/block/{blockHeight} | blockchain | - |
| Get raw name history | GET /v1/blockchains/{blockchainName}/names/{nameID}/history | blockchain | - |
| Get consensus hash | GET /v1/blockchains/{blockchainName}/consensusHash | blockchain | - |
| Get pending transactions | GET /v1/blockchains/{blockchainName}/pending | blockchain | - |
{
"satoshis": 4000000000
}
| Method | API Call | API family | Notes |
| ------ | -------- | ---------- | ----- |
| Get unspent outputs | GET /v1/blockchains/{blockchainName}/{address}/unspent | blockchain | Returns `{"transaction_hash": str, "output_index": int, "value": int (satoshis), "script_hex": str, "confirmations": int}` |
| Broadcast transaction | POST /v1/blockchains/{blockchainName}/txs | blockchain | Takes `{"tx": str}` as its payload |
## Get name price [GET /v1/prices/names/{name}]
+ Public Endpoint
+ Parameters
+ name: muneeb.id (string) - name to query price information for
+ Response 200 (application/json)
+ Body
## Identity API
{
"name_price": {
"satoshis": 100000,
"btc": 0.001
},
"total_tx_fees": 519209,
"register_tx_fee": {
"satoshis": 159110,
"btc": 0.0015911
},
"preorder_tx_fee": {
"satoshis": 163703,
"btc": 0.00163703
},
"warnings": [
"Insufficient funds; fees are rough estimates."
],
"total_estimated_cost": {
"satoshis": 619209,
"btc": 0.00619209
},
"update_tx_fee": {
"satoshis": 196396,
"btc": 0.00196396
}
}
### Profiles
# Group Blockchain Operations
## Get consensus hash [GET /v1/blockchains/{blockchainName}/consensus]
Get the current Blockstack consensus hash on a blockchain.
+ Public Endpoint
+ Parameters
+ blockchainName : bitcoin (string) - the given blockchain
+ Response 200 (application/json)
+ Body
TODO: this is not decided
{
"consensus_hash": "2fcbdf66c350894fe03b42c6a2e8a6ac"
}
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Create profile | POST /v1/profiles | profile_write | Payload: `{"name": NAME, "profile": PROFILE}`. Wallet must own the name. |
| Get profile | GET /v1/profiles/{name} | profile_read | - |
| Delete profile | DELETE /v1/profiles/{name} | profile_write | Wallet must own {name} |
| Update profile | PATCH /v1/profiles/{name} | profile_write | Payload: `{"blockchain_id": NAME, "profile": PROFILE }`. Wallet must own the name |
## Get pending transactions [GET /v1/blockchains/{blockchainName}/pending]
Get the current transactions that the node has issued and are still pending.
+ Public Endpoint
+ Parameters
+ blockchainName : bitcoin (string) - the given blockchain
+ Response 200 (application/json)
+ Body
### Datastores
{
"queues": {}
}
## Get unspent outputs [GET /v1/blockchains/{blockchainName}/{address}/unspent]
+ Authorization: `blockchain`
+ Parameters
+ blockchainName : bitcoin (string) - the given blockchain
+ address : 1GuKR3nJi2VH3E1ZSPvuX8nAu3jNnr7xzq (string) - the address to get unspents for
+ Response 200 (application/json)
+ Body
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Create store for this session | POST /v1/stores | store_write | Creates a datastore for the application indicated by the session |
| Get store metadata | GET /v1/stores/{storeID} | store_admin | - |
| Delete store | DELETE /v1/stores/{storeID} | store_write | Deletes all files and directories in the store as well |
| - | - | - | - |
| Get inode info (stat) | GET /v1/stores/{storeID}/inodes?path={path} | store_read | - |
| - | - | - | - |
| Get directory files (ls) | GET /v1/stores/{storeID}/directories?path={path} | store_read | Returns structured inode data |
| Create directory (mkdir) | POST /v1/stores/{storeID}/directories?path={path} | store_write | Only works on the datastore for the application indicated by the session |
| Delete directory (rmdir) | DELETE /v1/stores/{storeID}/directories?path={path} | store_write | Only works on the datastore for the application indicated by the session |
| - | - | - | - |
| Get file data (cat) | GET /v1/stores/{storeID}/files?path={path} | store_read | Returns `application/octet-stream` data |
| Create file | POST /v1/stores/{storeID}/files?path={path} | store_write | Uploads `application/octet-stream` raw file data. Only works on the datastore for the application indicated by the session. |
| Update file | PUT /v1/stores/{storeID}/files?path={path} | store_write | Uploads `application/octet-stream` raw file data. Only works on the datastore for the application indicated by the session. |
| Delete file (rm) | DELETE /v1/stores/{storeID}/files?path={path} | store_write | Only works on the datastore for the application indicated by the session |
[
{
"confirmations": 18,
"out_script": "76a914ae6ee3760fccb8225541ca89f08c927930adf97b88ac",
"outpoint": {
"hash": "977d3a025790e2cbdb50f63761872f36e78fbb9c53d515cb4c53155a1964932d",
"index": 1
},
"transaction_hash": "977d3a025790e2cbdb50f63761872f36e78fbb9c53d515cb4c53155a1964932d",
"value": 76779
}
]
### Collections
## Broadcast transaction [POST /v1/blockchains/{blockchainName}/txs]
+ Authorization: `blockchain`
+ Parameters
+ blockchainName : bitcoin (string) - the blockchain to broadcast on
+ Request (application/json)
+ Schema
{
'type': 'object',
'properties': {
'tx': {
'type': 'string',
'pattern': OP_HEX_PATTERN,
},
},
'additionalProperties': False,
'required': [
'tx'
],
}
| Method | API Call | API family | Notes |
| ------------- | ------------- | ------------- | ------------- |
| Create collection | POST /v1/collections | collection_admin | NOT IMPLEMENTED |
| Get all collection items | GET /v1/collections/{collectionID} | collection_read | NOT IMPLEMENTED |
| Create collection item | POST /v1/collections/{collectionID} | collection_write | NOT IMPLEMENTED |
| Get collection item | GET /v1/{collectionID}/{itemID} | collection_read | NOT IMPLEMENTED |
+ Response 200 (application/json)
+ Body
{ 'status' : True, 'tx_hash' : '...' }
## Get raw name history [GET /v1/blockchains/{blockchainName}/names/{nameID}/history]
Not implemented
+ Response 405 (application/json)
+ Body
{ 'error' : 'Unimplemented' }
# Group Gaia Endpoints
The Gaia endpoints interface with `blockstack-storage.js` to provide
storage to blockstack applications.
## Create "store" for this session [POST /v1/stores]
## Get "store" metadata [GET /v1/stores/{storeID}]
+ Parameters
+ storeID : (string)
## Delete "store" [DELETE /v1/stores/{storeID}]
+ Parameters
+ storeID : (string)
## Get inode info [GET /v1/stores/{storeID}/inodes?path={path}]
+ Parameters
+ storeID : (string)
+ path : (string) - path of inode
## Get directory files [GET /v1/stores/{storeID}/directories?path={path}]
+ Parameters
+ storeID : (string)
+ path : (string) - path of inode
## Create directory [POST /v1/stores/{storeID}/directories?path={path}]
+ Parameters
+ storeID : (string)
+ path : (string) - path of inode
## Delete directory [DELETE /v1/stores/{storeID}/directories?path={path}]
+ Parameters
+ storeID : (string)
+ path : (string) - path of inode
## Get file data [GET /v1/stores/{storeID}/files?path={path}]
+ Parameters
+ storeID : (string)
+ path : (string) - path of inode
## Create file [POST /v1/stores/{storeID}/files?path={path}]
+ Parameters
+ storeID : (string)
+ path : (string) - path of inode
## Update file [PUT /v1/stores/{storeID}/files?path={path}]
+ Parameters
+ storeID : (string)
+ path : (string) - path of inode
## Delete file [DELETE /v1/stores/{storeID}/files?path={path}]
+ Parameters
+ storeID : (string)
+ path : (string) - path of inode
# Group Namespace Operations
## Get all namespaces [GET /v1/namespaces]
+ Public Endpoint
+ Response 200 (application/json)
+ Body
{
"namespaces": [
".id"
]
}
## Get namespace names [GET /v1/namespaces/{tld}/names?page={page}]
Fetch a list of names from the namespace.
+ Public Endpoint
+ Parameters
+ tld: id (string) - the namespace to fetch names from
+ page: 23 (number) - names are returned in pages of size 100,
so specify the page number.
+ Response 200 (application/json)
+ Body
[ "aldenquimby.id", "aldeoryn.id",
"alderete.id", "aldert.id",
"aldi.id", "aldighieri.id", ... ]
## Create namespace [POST /v1/namespaces]
Not implemented.
## Pre-register a name [POST /v1/namespaces/{tld}/names]
Not implemented.
## Update pre-registered name [POST /v1/namespaces/{tld}/names/{name}]
Not implemented.
## Launch namespace [PUT /v1/namespaces/{tld}]
Not implemented.

View File

@@ -22,6 +22,13 @@ more conventional types of applications.
refactored to run many I/O operations in parallel. This leads to faster data
interaction times, even for indexed storage systems like Dropbox.
* **Support for Fast Registrations.** This release adds an `unsafe` attribute to
registration requests which, when enabled, directs core's registrar to issue
the blockstack transactions _preorder_, _register_, and _update_, with only 4,
1, and 1 confirmations respectively. To support this, core must ignore some
of the safety checks, because our resolvers will not have processed the name
before the _update_ is issued.
* **Initial subdomain support.** This release allows users to register
subdomains of existing blockchain IDs, such that _subdomains are independently
owned_. A user Bob can register `bob.alice.id`, where `alice.id` is owned by