Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
raphjaph authored Oct 4, 2023
2 parents e29da68 + cd15017 commit d9904da
Show file tree
Hide file tree
Showing 18 changed files with 758 additions and 30 deletions.
35 changes: 35 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ bip39 = "2.0.0"
bitcoin = { version = "0.30.0", features = ["rand"] }
boilerplate = { version = "1.0.0", features = ["axum"] }
chrono = "0.4.19"
ciborium = "0.2.1"
clap = { version = "4.4.2", features = ["derive"] }
ctrlc = { version = "3.2.1", features = ["termination"] }
derive_more = "0.99.17"
Expand All @@ -49,7 +50,7 @@ rust-embed = "8.0.0"
rustls = "0.21.1"
rustls-acme = { version = "0.7.1", features = ["axum"] }
serde = { version = "1.0.137", features = ["derive"] }
serde_json = { version = "1.0.81" }
serde_json = { version = "1.0.81", features = ["preserve_order"] }
serde_yaml = "0.9.17"
sysinfo = "0.29.2"
tempfile = "3.2.0"
Expand Down
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- [Overview](overview.md)
- [Digital Artifacts](digital-artifacts.md)
- [Inscriptions](inscriptions.md)
- [Metadata](inscriptions/metadata.md)
- [Provenance](inscriptions/provenance.md)
- [Recursion](inscriptions/recursion.md)
- [FAQ](faq.md)
Expand Down
55 changes: 53 additions & 2 deletions docs/src/guides/inscriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ Making inscriptions requires Bitcoin Core 24 or newer.

This guide does not cover installing Bitcoin Core in detail. Once Bitcoin Core
is installed, you should be able to run `bitcoind -version` successfully from
the command line.
the command line. Do *NOT* use `bitcoin-qt`.

Configuring Bitcoin Core
------------------------

`ord` requires Bitcoin Core's transaction index.
`ord` requires Bitcoin Core's transaction index and rest interface.

To configure your Bitcoin Core node to maintain a transaction
index, add the following to your `bitcoin.conf`:
Expand All @@ -66,6 +66,9 @@ Or, run `bitcoind` with `-txindex`:
bitcoind -txindex
```

Details on creating or modifying your `bitcoin.conf` file can be found
[here](https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md).

Syncing the Bitcoin Blockchain
------------------------------

Expand All @@ -85,6 +88,54 @@ agrees with the block count on a block explorer like [the mempool.space block
explorer](https://mempool.space/). `ord` interacts with `bitcoind`, so you
should leave `bitcoind` running in the background when you're using `ord`.

The blockchain takes about 600GB of disk space. If you have an external drive
you want to store blocks on, use the configuration option
`blocksdir=<external_drive_path>`. This is much simpler than using the
`datadir` option because the cookie file will still be in the default location
for `bitcoin-cli` and `ord` to find.

Troubleshooting
---------------

Make sure you can access `bitcoind` with `bitcoin-cli -getinfo` and that it is
fully synced.

If `bitcoin-cli -getinfo` returns `Could not connect to the server`, `bitcoind`
is not running.

Make sure `rpcuser`, `rpcpassword`, or `rpcauth` are *NOT* set in your
`bitcoin.conf` file. `ord` requires using cookie authentication. Make sure there
is a file `.cookie` in your bitcoin data directory.

If `bitcoin-cli -getinfo` returns `Could not locate RPC credentials`, then you
must specify the cookie file location.
If you are using a custom data directory (specifying the `datadir` option),
then you must specify the cookie location like
`bitcoin-cli -rpccookiefile=<your_bitcoin_datadir>/.cookie -getinfo`.
When running `ord` you must specify the cookie file location with
`--cookie-file=<your_bitcoin_datadir>/.cookie`.

Make sure you do *NOT* have `disablewallet=1` in your `bitcoin.conf` file. If
`bitcoin-cli listwallets` returns `Method not found` then the wallet is disabled
and you won't be able to use `ord`.

Make sure `txindex=1` is set. Run `bitcoin-cli getindexinfo` and it should
return something like
```json
{
"txindex": {
"synced": true,
"best_block_height": 776546
}
}
```
If it only returns `{}`, `txindex` is not set.
If it returns `"synced": false`, `bitcoind` is still creating the `txindex`.
Wait until `"synced": true` before using `ord`.

If you have `maxuploadtarget` set it can interfere with fetching blocks for
`ord` index. Either remove it or set `whitebind=127.0.0.1:8333`.

Installing `ord`
----------------

Expand Down
86 changes: 86 additions & 0 deletions docs/src/inscriptions/metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
Metadata
========

Inscriptions may include [CBOR](https://cbor.io/) metadata, stored as data
pushes in fields with tag `5`. Since data pushes are limited to 520 bytes,
metadata longer than 520 bytes must be split into multiple tag `5` fields,
which will then be concatenated before decoding.

Metadata is human readable, and all metadata will be displayed to the user with
its inscription. Inscribers are encouraged to consider how metadata will be
displayed, and make metadata concise and attractive.

Metadata is rendered to HTML for display as follows:

- `null`, `true`, `false`, numbers, floats, and strings are rendered as plain
text.
- Byte strings are rendered as uppercase hexadecimal.
- Arrays are rendered as `<ul>` tags, with every element wrapped in `<li>`
tags.
- Maps are rendered as `<dl>` tags, with every key wrapped in `<dt>` tags, and
every value wrapped in `<dd>` tags.
- Tags are rendered as the tag , enclosed in a `<sup>` tag, followed by the
value.

CBOR is a complex spec with many different data types, and multiple ways of
representing the same data. Exotic data types, such as tags, floats, and
bignums, and encoding such as indefinite values, may fail to display correctly
or at all. Contributions to `ord` to remedy this are welcome.

Example
-------

Since CBOR is not human readable, in these examples it is represented as JSON.
Keep in mind that this is *only* for these examples, and JSON metadata will
*not* be displayed correctly.

The metadata `{"foo":"bar","baz":[null,true,false,0]}` would be included in an inscription as:

```
OP_FALSE
OP_IF
...
OP_PUSH 0x05 OP_PUSH '{"foo":"bar","baz":[null,true,false,0]}'
...
OP_ENDIF
```

And rendered as:

```
<dl>
...
<dt>metadata</dt>
<dd>
<dl>
<dt>foo</dt>
<dd>bar</dd>
<dt>baz</dt>
<dd>
<ul>
<li>null</li>
<li>true</li>
<li>false</li>
<li>0</li>
</ul>
</dd>
</dl>
</dd>
...
</dl>
```

Metadata longer than 520 bytes must be split into multiple fields:

```
OP_FALSE
OP_IF
...
OP_PUSH 0x05 OP_PUSH '{"very":"long","metadata":'
OP_PUSH 0x05 OP_PUSH '"is","finally":"done"}'
...
OP_ENDIF
```

Which would then be concatinated into
`{"very":"long","metadata":"is","finally":"done"}`.
47 changes: 47 additions & 0 deletions src/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub(crate) const PROTOCOL_ID: [u8; 3] = *b"ord";
pub(crate) const BODY_TAG: [u8; 0] = [];
pub(crate) const CONTENT_TYPE_TAG: [u8; 1] = [1];
pub(crate) const PARENT_TAG: [u8; 1] = [3];
pub(crate) const METADATA_TAG: [u8; 1] = [5];
pub(crate) const METAPROTOCOL_TAG: [u8; 1] = [7];

type Result<T> = std::result::Result<T, script::Error>;
Expand All @@ -34,6 +35,19 @@ fn remove_field(fields: &mut BTreeMap<&[u8], Vec<&[u8]>>, field: &[u8]) -> Optio
}
}

fn remove_and_concatenate_field(
fields: &mut BTreeMap<&[u8], Vec<&[u8]>>,
field: &[u8],
) -> Option<Vec<u8>> {
let value = fields.remove(field)?;

if value.is_empty() {
None
} else {
Some(value.into_iter().flatten().cloned().collect())
}
}

impl From<RawEnvelope> for ParsedEnvelope {
fn from(envelope: RawEnvelope) -> Self {
let body = envelope
Expand All @@ -58,6 +72,7 @@ impl From<RawEnvelope> for ParsedEnvelope {
let content_type = remove_field(&mut fields, &CONTENT_TYPE_TAG);
let parent = remove_field(&mut fields, &PARENT_TAG);
let metaprotocol = remove_field(&mut fields, &METAPROTOCOL_TAG);
let metadata = remove_and_concatenate_field(&mut fields, &METADATA_TAG);

let unrecognized_even_field = fields
.keys()
Expand All @@ -78,6 +93,7 @@ impl From<RawEnvelope> for ParsedEnvelope {
duplicate_field,
incomplete_field,
metaprotocol,
metadata,
},
input: envelope.input,
offset: envelope.offset,
Expand Down Expand Up @@ -689,4 +705,35 @@ mod tests {
}],
);
}

#[test]
fn metadata_is_parsed_correctly() {
assert_eq!(
parse(&[envelope(&[b"ord", &[5], &[]])]),
vec![ParsedEnvelope {
payload: Inscription {
metadata: Some(vec![]),
..Default::default()
},
input: 0,
offset: 0,
}]
);
}

#[test]
fn metadata_is_parsed_correctly_from_chunks() {
assert_eq!(
parse(&[envelope(&[b"ord", &[5], &[0], &[5], &[1]])]),
vec![ParsedEnvelope {
payload: Inscription {
metadata: Some(vec![0, 1]),
duplicate_field: true,
..Default::default()
},
input: 0,
offset: 0,
}]
);
}
}
Loading

0 comments on commit d9904da

Please sign in to comment.