#include "Script.h" #include "../hash.h" #include #include Script::Script(std::span data, bool coinbase) { for(auto iter = data.begin(); iter != data.end();) { ScriptOperation operation; operation.opCode = OpCode(*iter++); //std::cout << "found opcode: " << std::showbase << std::hex << std::setw(4) << (int)operation.opCode << std::dec << std::endl; 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) { dataSize = *iter++; } else if(operation.opCode == OpCode::OP_PUSHDATA2) { dataSize = *((uint16_t*)&iter[0]); iter += 2; } else if(operation.opCode == OpCode::OP_PUSHDATA4) { dataSize = *((uint32_t *)&iter[0]); iter += 4; } operation.input = std::vector(iter, iter + dataSize); iter += dataSize; } _operations.emplace_back(operation); } _type = coinbase ? ScriptType::Coinbase : type(); } ScriptType Script::type() const { 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 { throw std::runtime_error("Unsupported script type"); } } std::string Script::address() { auto pubKey = publicKey(); if(!pubKey.empty()) { auto hash160 = hash::hash160(pubKey); std::array prefixedHash{}; prefixedHash[0] = 0; // TODO: this value should depend on BlockMagic std::copy_n(hash160.begin(), hash160.size(), prefixedHash.begin() + 1); auto secondHash = hash::sha256(prefixedHash); std::array finalHash{}; std::copy(prefixedHash.begin(), prefixedHash.end(), finalHash.begin()); std::copy_n(secondHash.begin(), 4, finalHash.begin() + prefixedHash.size()); return hash::base58(finalHash); } else { return ""; } } std::span Script::publicKey() { switch (_type) { case P2PK: return _operations[0].input; default: return {}; } }