BtcExplorer/Models/Script.cpp

84 lines
2.8 KiB
C++

#include "Script.h"
#include "../hash.h"
#include <iostream>
#include <iomanip>
Script::Script(std::span<uint8_t> data, bool coinbase): _operations(10) {
for(auto iter = data.begin(); iter != data.end();) {
ScriptOperation operation;
operation.opCode = OpCode(*iter++);
size_t available = data.end() - iter;
if(operation.opCode <= OpCode::OP_PUSHDATA4) {
size_t dataSize = 0;
if(operation.opCode < OpCode::OP_PUSHDATA1) {
dataSize = operation.opCode;
} else if(operation.opCode == OpCode::OP_PUSHDATA1 && available >= 1) {
dataSize = *iter++;
} else if(operation.opCode == OpCode::OP_PUSHDATA2 && available >= 2) {
dataSize = *((uint16_t*)&iter[0]);
iter += 2;
} else if(operation.opCode == OpCode::OP_PUSHDATA4 && available >= 4) {
dataSize = *((uint32_t *)&iter[0]);
iter += 4;
}
available = data.end() - iter;
if(dataSize > available) {
dataSize = available;
}
if(dataSize > 0) {
operation.input = std::vector(iter, iter + dataSize);
iter += dataSize;
}
}
_operations.emplace_back(operation);
}
_type = coinbase ? ScriptType::Coinbase : type();
}
ScriptType Script::type() const {
if(_operations.size() < 2) {
return ScriptType::Unknown;
}
if(_operations[0].opCode <= OpCode::OP_PUSHDATA4 && _operations[1].opCode == OpCode::OP_CHECKSIG) {
return ScriptType::P2PK;
} else if(_operations[0].opCode == OpCode::OP_DUP
&& _operations[1].opCode == OpCode::OP_HASH160
&& _operations[2].opCode <= OpCode::OP_PUSHDATA4
&& _operations[3].opCode == OpCode::OP_EQUALVERIFY
&& _operations[4].opCode == OpCode::OP_CHECKSIG)
{
return ScriptType::P2PKH;
} else {
return ScriptType::Unknown;
//throw std::runtime_error("Unsupported script type");
}
}
std::string Script::address() {
auto pubKey = publicKey();
if(!pubKey.empty()) {
auto hash160 = hash::hash160(pubKey, 0); // TODO: this value should depend on BlockMagic
auto secondHash = hash::hash256(hash160);
std::array<uint8_t, RIPEMD160_DIGEST_LENGTH + 1 + 4> finalHash{};
std::copy(hash160.begin(), hash160.end(), finalHash.begin());
std::copy_n(secondHash.begin(), 4, finalHash.begin() + hash160.size());
return hash::base58(finalHash);
} else {
return "";
}
}
std::span<uint8_t> Script::publicKey() {
switch (_type) {
case P2PK: return _operations[0].input.value();
case P2PKH: return _operations[2].input.value();
default: return {};
}
}