Bitcoin transactions with message metadata have additional content attached.

CoinSpark message metadata contains everything required by the recipient of a bitcoin transaction to retrieve an attached message. It includes the address of the message delivery server, a list of output indexes for which the message is intended, and a hash of the full message content.

The message metadata is encoded in a transaction’s first OP_RETURN output following CoinSpark’s general metadata format. Unless it is combined with other CoinSpark metadata, the prefix will be SPKm (0x53 0x50 0x4B 0x6D). You can easily encode and extract CoinSpark message metadata using the appropriate CoinSpark library for your programming language.

CoinSparkMessage fields

A CoinSparkMessage structure or object contains the following fields:

  • useHttps – boolean flag for whether the message delivery server should be accessed using the https:// scheme.
  • serverHost – hostname (or IPv4 address) of the message delivery server. We recommend using IPv4 addresses where possible since these are represented very efficiently using raw bytes.
  • usePrefix – boolean flag for whether the message delivery server endpoint should include the coinspark/ prefix.
  • serverPath – additional path of the message delivery server endpoint, characters from a-z, 0-9, . and - only.
  • isPublic – boolean flag for whether the message is publicly readable.
  • outputRanges – an array of up to 16 CoinSparkIORange structures/objects, each of which indicates a contiguous range of output indexes for which the message is intended. (In C there is also the countOutputRanges field which indicates the size of this array.)
    outputRanges[i].first is the index of the first output in each contiguous range.
    outputRanges[i].count is the number of outputs in each contiguous range.
  • hash – SHA-256 hash of the message salt and payload (see message hashes for more information).
  • hashLen – length of prefix of hash which is relevant for encoding or comparison.

Creating and encoding message metadata

In these examples, a message is created for the first two outputs out of a three-output transaction, the message is not public, and the message delivery server endpoint is https://123.45.67.89/msg/.

C/C++

CoinSparkMessage message;
char metadata[40]; // 40 byte limit for OP_RETURNs
size_t metadataLen;
int countOutputs=3; // 3 outputs for this transaction

message.useHttps=TRUE;
strcpy(message.serverHost, "123.45.67.89");
message.usePrefix=FALSE;
strcpy(message.serverPath, "msg");
message.isPublic=FALSE;

message.countOutputRanges=1;
message.outputRange[0].first=0;
message.outputRange[0].count=2; // message is for outputs 0 and 1

CoinSparkCalcMessageHash(..., message.hash); // see 'Message Hashes' docs page
message.hashLen=CoinSparkMessageCalcHashLen(&message, countOutputs, 40); // 40 byte limit for OP_RETURN

metadataLen=CoinSparkMessageEncode(&message, countOutputs, metadata, sizeof(metadata));

if (metadataLen)
    ; // use CoinSparkMetadataToScript() to embed metadata (length metadataLen) in an output script
else
    ; // handle error

Java

CoinSparkMessage message=new CoinSparkMessage();

message.setUseHttps(true);
message.setServerHost("123.45.67.89");
message.setUsePrefix(false);
message.setServerPath("msg");
message.setIsPublic(false);
message.addOutputs(new CoinSparkIORange(0, 2)); // message is for outputs 0 and 1

int countOutputs=3; // 3 outputs for this transaction

message.setHash(CoinSparkMessage.calcMessageHash(...)); // see 'Message Hashes' docs page
message.setHashLen(message.calcHashLen(countOutputs, 40)); // 40 byte limit for OP_RETURN

byte[] metadata=message.encode(countOutputs, 40); // 40 byte limit for OP_RETURNs

if (metadata!=null)
    ; // use CoinSparkBase.metadataToScript() to embed metadata in an output script
else
    ; // handle error

Javascript

var message=new CoinSparkMessage();

message.useHttps=true;
message.serverHost="123.45.67.89";
message.usePrefix=false;
message.serverPath="msg";
message.isPublic=false;

ioRange=new CoinSparkIORange();
ioRange.first=0;
ioRange.count=2; // message is for outputs 0 and 1
message.outputRanges[0]=ioRange;

var countOutputs=3; // 3 outputs for this transaction

message.hash=CoinSparkCalcMessageHash(...); // see 'Message Hashes' docs page
message.hashLen=message.calcHashLen(countOutputs, 40); // 40 byte limit for OP_RETURN

var metadata=message.encode(countOutputs, 40); // 40 byte limit for OP_RETURN

if (metadata)
    ; // use CoinSparkMetadataToScript() to embed metadata in an output script
else
    ; // handle error

PHP

$message=new CoinSparkMessage();

$message->useHttps=true;
$message->serverHost="123.45.67.89";
$message->usePrefix=false;
$message->serverPath="msg";
$message->isPublic=false;

$ioRange=new CoinSparkIORange();
$ioRange->first=0;
$ioRange->count=2; // message is for outputs 0 and 1
$message->outputRanges[0]=$ioRange;

$countOutputs=3; // 3 outputs for this transaction

$message->hash=CoinSparkCalcMessageHash(...); // see 'Message Hashes' docs page
$message->hashLen=$message->calcHashLen($countOutputs, 40); // 40 byte limit for OP_RETURN

$metadata=$message->encode($countOutputs, 40); // 40 byte limit for OP_RETURNs

if (isset($metadata))
    ; // use CoinSparkMetadataToScript() to embed $metadata in an output script
else
    ; // handle error

Python

message=CoinSparkMessage()

message.useHttps=True
message.serverHost="123.45.67.89"
message.usePrefix=False
message.serverPath="msg"
message.isPublic=False

ioRange=CoinSparkIORange()
ioRange.first=0
ioRange.count=2 # message is for outputs 0 and 1
message.outputRanges.append(ioRange)

countOutputs=3 # 3 outputs for this transaction

message.hash=CoinSparkCalcMessageHash(...) // see 'Message Hashes' docs page
message.hashLen=message.calcHashLen(countOutputs, 40) # 40 byte limit for OP_RETURN

metadata=message.encode(countOutputs, 40) # 40 byte limit for OP_RETURNs

if not metadata is None:
     # use CoinSparkMetadataToScript() to embed metadata in an output script
else:
     # handle error

Ruby

message=CoinSparkMessage.new

message.useHttps=true
message.serverHost="123.45.67.89"
message.usePrefix=false
message.serverPath="msg"
message.isPublic=false

ioRange=CoinSparkIORange.new
ioRange.first=0
ioRange.count=2 # message is for outputs 0 and 1
message.outputRanges.push(ioRange)

countOutputs=3 # 3 outputs for this transaction

message.hash=CoinSparkCalcMessageHash(...) // see 'Message Hashes' docs page
message.hashLen=message.calcHashLen(countOutputs, 40) # 40 byte limit for OP_RETURN

metadata=message.encode(countOutputs, 40) # 40 byte limit for OP_RETURNs

if metadata!=nil
     # use CoinSparkMetadataToScript() to embed metadata in an output script
else
     # handle error
end

Extracting and decoding message metadata

Please see the general metadata sample code.