void interpret(uint16_t opcode){ int firstnibble = (opcode & 0xF000) >> 12; switch(firstnibble){ case 0: op_0(opcode); break; case 1: op_1(opcode); break; case 2: op_2(opcode); break; case 3: op_3(opcode); break; case 4: op_4(opcode); break; case 5: op_5(opcode); break; case 6: op_6(opcode); break; case 7: op_7(opcode); break; case 8: op_8(opcode); break; case 9: op_9(opcode); break; case 0xA: op_A(opcode); break; case 0xB: op_B(opcode); break; case 0xC: op_C(opcode); break; case 0xD: op_D(opcode); break; case 0xE: op_E(opcode); break; case 0xF: op_F(opcode); break; } }
void StackInterpreter::op_checkmultisig() { //stack needs to have at least m, n, output script if (stack_.size() < 3) throw ScriptException("insufficient stack size for checkmultisig operation"); //pop n auto&& n = pop_back(); auto nI = rawBinaryToInt(n); if (nI < 0 || nI > 20) throw ScriptException("invalid n"); //pop pubkeys map<unsigned, pair<BTC_PUBKEY, BinaryData>> pubkeys; for (unsigned i = 0; i < nI; i++) { auto&& pubkey = pop_back(); CryptoPP::ECP ecp = CryptoECDSA::Get_secp256k1_ECP(); BTC_ECPOINT ptPub; ecp.DecodePoint(ptPub, (byte*)pubkey.getPtr(), pubkey.getSize()); BTC_PUBKEY cppPubKey; cppPubKey.Initialize(CryptoPP::ASN1::secp256k1(), ptPub); BTC_PRNG prng; if (cppPubKey.Validate(prng, 3)) { txInEvalState_.pubKeyState_.insert(make_pair(pubkey, false)); auto&& pubkeypair = make_pair(move(cppPubKey), pubkey); pubkeys.insert(move(make_pair(i, move(pubkeypair)))); } } //pop m auto&& m = pop_back(); auto mI = rawBinaryToInt(m); if (mI < 0 || mI > nI) throw ScriptException("invalid m"); txInEvalState_.n_ = nI; txInEvalState_.m_ = mI; //pop sigs struct sigData { BinaryData sig_; SIGHASH_TYPE hashType_; }; vector<sigData> sigVec; while (stack_.size() > 0) { auto&& sig = pop_back(); if (sig.getSize() == 0) break; sigData sdata; sdata.sig_ = sig.getSliceCopy(0, sig.getSize() - 1); //grab hash type sdata.hashType_ = getSigHashSingleByte(*(sig.getPtr() + sig.getSize() - 1)); //push to vector sigVec.push_back(move(sdata)); } //should have at least as many sigs as m /*if (sigVec.size() < mI) throw ScriptException("invalid sig count");*/ //check sigs map<SIGHASH_TYPE, BinaryData> dataToHash; //check sighashdata object if (sigHashDataObject_ == nullptr) sigHashDataObject_ = make_shared<SigHashDataLegacy>(); unsigned validSigCount = 0; int index = nI - 1; auto sigIter = sigVec.rbegin(); while(sigIter != sigVec.rend()) { auto& sigD = *sigIter++; //get data to hash auto& hashdata = dataToHash[sigD.hashType_]; if (hashdata.getSize() == 0) { hashdata = sigHashDataObject_->getDataForSigHash( sigD.hashType_, *txStubPtr_, outputScriptRef_, inputIndex_); } //prepare sig auto&& rs = BtcUtils::extractRSFromDERSig(sigD.sig_); BinaryWriter sigW; //pop pubkeys from deque to verify against sig while (pubkeys.size() > 0) { auto pubkey = pubkeys[index]; pubkeys.erase(index--); #ifdef SIGNER_DEBUG LOGWARN << "Verifying sig for: "; LOGWARN << " pubkey: " << pubkey.second.toHexStr(); auto&& msg_hash = BtcUtils::getHash256(hashdata); LOGWARN << " message: " << hashdata.toHexStr(); #endif if (CryptoECDSA().VerifyData(hashdata, rs, pubkey.first)) { txInEvalState_.pubKeyState_[pubkey.second] = true; validSigCount++; break; } } } if (validSigCount >= mI) op_true(); else op_0(); }
void StackInterpreter::processOpCode(const OpCode& oc) { ++opcount_; //handle push data by itself, doesn't play well with switch if (oc.opcode_ == 0) { op_0(); return; } if (oc.opcode_ <= 75) { stack_.push_back(oc.dataRef_); return; } if (oc.opcode_ < 79) { //op push data stack_.push_back(oc.dataRef_); return; } if (oc.opcode_ == OP_1NEGATE) { op_1negate(); return; } if (oc.opcode_ <= 96 && oc.opcode_ >= 81) { //op_1 - op_16 uint8_t val = oc.opcode_ - 80; stack_.push_back(move(intToRawBinary(val))); return; } //If we got this far this op code is not push data. If this is the input //script, set the flag as per P2SH parsing rules (only push data in inputs) if (outputScriptRef_.getSize() == 0) onlyPushDataInInput_ = false; switch (oc.opcode_) { case OP_NOP: break; case OP_IF: { BinaryRefReader brr(oc.dataRef_); op_if(brr, false); break; } case OP_NOTIF: { op_not(); BinaryRefReader brr(oc.dataRef_); op_if(brr, false); break; } case OP_ELSE: //processed by opening if statement throw ScriptException("a wild else appears"); case OP_ENDIF: //processed by opening if statement throw ScriptException("a wild endif appears"); case OP_VERIFY: op_verify(); break; case OP_TOALTSTACK: op_toaltstack(); break; case OP_FROMALTSTACK: op_fromaltstack(); break; case OP_IFDUP: op_ifdup(); break; case OP_2DROP: { stack_.pop_back(); stack_.pop_back(); break; } case OP_2DUP: op_2dup(); break; case OP_3DUP: op_3dup(); break; case OP_2OVER: op_2over(); break; case OP_DEPTH: op_depth(); break; case OP_DROP: stack_.pop_back(); break; case OP_DUP: op_dup(); break; case OP_NIP: op_nip(); break; case OP_OVER: op_over(); break; case OP_PICK: op_pick(); break; case OP_ROLL: op_roll(); break; case OP_ROT: op_rot(); break; case OP_SWAP: op_swap(); break; case OP_TUCK: op_tuck(); break; case OP_SIZE: op_size(); break; case OP_EQUAL: { op_equal(); if (onlyPushDataInInput_ && p2shScript_.getSize() != 0) { //check the op_equal result op_verify(); if (!isValid_) break; if (flags_ & SCRIPT_VERIFY_SEGWIT) if (p2shScript_.getSize() == 22 || p2shScript_.getSize() == 34) { auto versionByte = p2shScript_.getPtr(); if (*versionByte <= 16) { processSW(p2shScript_); return; } } processScript(p2shScript_, true); } break; } case OP_EQUALVERIFY: { op_equal(); op_verify(); break; } case OP_1ADD: op_1add(); break; case OP_1SUB: op_1sub(); break; case OP_NEGATE: op_negate(); break; case OP_ABS: op_abs(); break; case OP_NOT: op_not(); break; case OP_0NOTEQUAL: op_0notequal(); break; case OP_ADD: op_add(); break; case OP_SUB: op_sub(); break; case OP_BOOLAND: op_booland(); break; case OP_BOOLOR: op_boolor(); break; case OP_NUMEQUAL: op_numequal(); break; case OP_NUMEQUALVERIFY: { op_numequal(); op_verify(); break; } case OP_NUMNOTEQUAL: op_numnotequal(); break; case OP_LESSTHAN: op_lessthan(); break; case OP_GREATERTHAN: op_greaterthan(); break; case OP_LESSTHANOREQUAL: op_lessthanorequal(); break; case OP_GREATERTHANOREQUAL: op_greaterthanorequal(); break; case OP_MIN: op_min(); break; case OP_MAX: op_max(); break; case OP_WITHIN: op_within(); break; case OP_RIPEMD160: op_ripemd160(); break; case OP_SHA256: { //save the script if this output is a possible p2sh if (flags_ & SCRIPT_VERIFY_P2SH_SHA256) if (opcount_ == 1 && onlyPushDataInInput_) p2shScript_ = stack_back(); op_sha256(); break; } case OP_HASH160: { //save the script if this output is a possible p2sh if (flags_ & SCRIPT_VERIFY_P2SH) if (opcount_ == 1 && onlyPushDataInInput_) p2shScript_ = stack_back(); op_hash160(); break; } case OP_HASH256: op_hash256(); break; case OP_CODESEPARATOR: { opcount_ = 0; if (outputScriptRef_.getSize() != 0) txStubPtr_->setLastOpCodeSeparator(inputIndex_, oc.offset_); break; } case OP_CHECKSIG: op_checksig(); break; case OP_CHECKSIGVERIFY: { op_checksig(); op_verify(); break; } case OP_CHECKMULTISIG: op_checkmultisig(); break; case OP_CHECKMULTISIGVERIFY: { op_checkmultisig(); op_verify(); } case OP_NOP1: break; case OP_NOP2: { if (!(flags_ & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) break; // not enabled; treat as a NOP //CLTV mechanics throw ScriptException("OP_CLTV not supported"); } case OP_NOP3: { if (!(flags_ & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) break; // not enabled; treat as a NOP //CSV mechanics throw ScriptException("OP_CSV not supported"); } case OP_NOP4: break; case OP_NOP5: break; case OP_NOP6: break; case OP_NOP7: break; case OP_NOP8: break; case OP_NOP9: break; case OP_NOP10: break; default: { stringstream ss; ss << "unknown opcode: " << (unsigned)oc.opcode_; throw runtime_error(ss.str()); } } }