function ProcessScriptTests($inputSource) { echo "CoinSpark Script Tests Output\n\n"; while (true) { $inputLines = GetNextInputLines($inputSource, 4); if (!isset($inputLines)) { break; } list($countInputs, $countOutputs, $scriptPubKeyHex) = $inputLines; $metadata = CoinSparkScriptToMetadata($scriptPubKeyHex, true); if (!isset($metadata)) { die("Could not decode script metadata: {$scriptPubKeyHex}\n"); } // Read in the different types of metadata $genesis = new CoinSparkGenesis(); $hasGenesis = $genesis->decode($metadata); $paymentRef = new CoinSparkPaymentRef(); $hasPaymentRef = $paymentRef->decode($metadata); $transfers = new CoinSparkTransferList(); $hasTransfers = $transfers->decode($metadata, $countInputs, $countOutputs); $message = new CoinSparkMessage(); $hasMessage = $message->decode($metadata, $countOutputs); // Output the toString()s if ($hasGenesis) { echo $genesis->toString(); } if ($hasPaymentRef) { echo $paymentRef->toString(); } if ($hasTransfers) { echo $transfers->toString(); } if ($hasMessage) { echo $message->toString(); } // Re-encode $testMetadata = ''; $testMetadataMaxLen = strlen($metadata); $nextMetadataMaxLen = $testMetadataMaxLen; $encodeOrder = array('genesis', 'paymentRef', 'transfers', 'message'); foreach ($encodeOrder as $encodeField) { $triedNextMetadata = false; switch ($encodeField) { case 'genesis': if ($hasGenesis) { $nextMetadata = $genesis->encode($nextMetadataMaxLen); $triedNextMetadata = true; } break; case 'paymentRef': if ($hasPaymentRef) { $nextMetadata = $paymentRef->encode($nextMetadataMaxLen); $triedNextMetadata = true; } break; case 'transfers': if ($hasTransfers) { $nextMetadata = $transfers->encode($countInputs, $countOutputs, $nextMetadataMaxLen); $triedNextMetadata = true; } break; case 'message': if ($hasMessage) { $nextMetadata = $message->encode($countOutputs, $nextMetadataMaxLen); $triedNextMetadata = true; } break; } if ($triedNextMetadata) { if (!isset($nextMetadata)) { die("Failed to reencode {$encodeField} metadata!\n"); } if (strlen($testMetadata)) { $testMetadata = CoinSparkMetadataAppend($testMetadata, $testMetadataMaxLen, $nextMetadata); if (!isset($testMetadata)) { die("Insufficient space to append {$encodeField} metadata!\n"); } } else { $testMetadata = $nextMetadata; } $nextMetadataMaxLen = CoinSparkMetadataMaxAppendLen($testMetadata, $testMetadataMaxLen); } } // Test other library functions while we are here if ($hasGenesis) { if (!$genesis->match($genesis, true)) { die("Failed to match genesis to itself!\n"); } if ($genesis->calcHashLen(strlen($metadata)) != $genesis->assetHashLen) { // assumes that metadata only contains genesis die("Failed to calculate matching asset hash length!\n"); } $testGenesis = new CoinSparkGenesis(); $testGenesis->decode($metadata); $rounding = rand(0, 2) - 1; $testGenesis->setQty(0, 0); $testGenesis->setQty($genesis->getQty(), $rounding); $testGenesis->setChargeFlat(0, 0); $testGenesis->setChargeFlat($genesis->getChargeFlat(), $rounding); if (!$genesis->match($testGenesis, false)) { die("Mismatch on genesis rounding!\n"); } } if ($hasPaymentRef) { if (!$paymentRef->match($paymentRef)) { die("Failed to match paymentRef to itself!\n"); } } if ($hasTransfers) { if (!$transfers->match($transfers, true)) { die("Failed to strictly match transfers to itself!\n"); } if (!$transfers->match($transfers, false)) { die("Failed to leniently match transfers to itself!\n"); } } if ($hasMessage) { if (!$message->match($message, true)) { die("Failed to strictly match message to itself!\n"); } if (!$message->match($message, false)) { die("Failed to leniently match message to itself!\n"); } $messageEncode = $message->encode($countOutputs, strlen($metadata)); // encode on its own to check calcHashLen() if ($message->calcHashLen($countOutputs, strlen($messageEncode)) != $message->hashLen) { die("Failed to calculate matching message hash length!\n"); } } // Compare to the original $encoded = CoinSparkMetadataToScript($testMetadata, true); if ($encoded != $scriptPubKeyHex) { die("Encode metadata mismatch: {$encoded} should be {$scriptPubKeyHex}\n"); } $checkMetadata = CoinSparkScriptToMetadata(CoinSparkMetadataToScript($testMetadata, false), false); if ($checkMetadata != $testMetadata) { die("Binary metadata to/from script mismatch!\n"); } } }
/** * Extracts first OP_RETURN metadata (not necessarily CoinSpark data) from an array of bitcoin tx output scripts. * * @param array $scriptPubKeys Array of output scripts as raw binary data or hexadecimal. * @param boolean $scriptsAreHex True to interpret each element of $scriptPubKeys as a hex string, false as raw binary. * * @return string|null First raw binary embedded metadata if found, null if none found. */ function CoinSparkScriptsToMetadata($scriptPubKeys, $scriptsAreHex) { foreach ($scriptPubKeys as $scriptPubKey) { if (!CoinSparkScriptIsRegular($scriptPubKey, $scriptsAreHex)) { return CoinSparkScriptToMetadata($scriptPubKey, $scriptsAreHex); } } return null; }