#include "Script.h" #include "../hash.h" #include #include Script::Script(std::span data, bool coinbase) { _operations.reserve(5); for(auto iter = data.begin(); iter != data.end();) { auto opCode = OpCode(*iter++); size_t available = data.end() - iter; if(opCode <= OpCode::OP_PUSHDATA4) { size_t dataSize = 0; if(opCode < OpCode::OP_PUSHDATA1) { dataSize = opCode; } else if(opCode == OpCode::OP_PUSHDATA1 && available >= 1) { dataSize = *iter++; } else if(opCode == OpCode::OP_PUSHDATA2 && available >= 2) { dataSize = *((uint16_t*)&iter[0]); iter += 2; } else if(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) { _operations.emplace_back(opCode, std::vector(iter, iter + dataSize)); iter += dataSize; continue; } } _operations.emplace_back(ScriptOperation(opCode)); } _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 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 Script::publicKey() { switch (_type) { case P2PK: return _operations[0].input.value(); case P2PKH: return _operations[2].input.value(); default: return {}; } }