| 1924 | } |
| 1925 | |
| 1926 | static RPCHelpMan combinepsbt() |
| 1927 | { |
| 1928 | return RPCHelpMan{"combinepsbt", |
| 1929 | "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n" |
| 1930 | "Implements the Combiner role.\n", |
| 1931 | { |
| 1932 | {"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The base64 strings of partially signed transactions", |
| 1933 | { |
| 1934 | {"psbt", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A base64 string of a PSBT"}, |
| 1935 | }, |
| 1936 | }, |
| 1937 | }, |
| 1938 | RPCResult{ |
| 1939 | RPCResult::Type::STR, "", "The base64-encoded partially signed transaction" |
| 1940 | }, |
| 1941 | RPCExamples{ |
| 1942 | HelpExampleCli("combinepsbt", R"('["mybase64_1", "mybase64_2", "mybase64_3"]')") |
| 1943 | }, |
| 1944 | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
| 1945 | { |
| 1946 | if (!g_con_elementsmode) |
| 1947 | throw std::runtime_error("PSBT operations are disabled when not in elementsmode.\n"); |
| 1948 | |
| 1949 | RPCTypeCheck(request.params, {UniValue::VARR}, true); |
| 1950 | |
| 1951 | // Unserialize the transactions |
| 1952 | std::vector<PartiallySignedTransaction> psbtxs; |
| 1953 | UniValue txs = request.params[0].get_array(); |
| 1954 | if (txs.empty()) { |
| 1955 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter 'txs' cannot be empty"); |
| 1956 | } |
| 1957 | for (unsigned int i = 0; i < txs.size(); ++i) { |
| 1958 | PartiallySignedTransaction psbtx; |
| 1959 | std::string error; |
| 1960 | if (!DecodeBase64PSBT(psbtx, txs[i].get_str(), error)) { |
| 1961 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); |
| 1962 | } |
| 1963 | psbtxs.push_back(psbtx); |
| 1964 | } |
| 1965 | |
| 1966 | // Find if (and which) psbt has all the output blinding stuff set |
| 1967 | unsigned int base_psbt_index = 0; |
| 1968 | bool has_fully_blinded = false; |
| 1969 | for (unsigned int i = 0; i < psbtxs.size(); ++i) { |
| 1970 | const auto& psbt = psbtxs[i]; |
| 1971 | bool is_fully_blinded = true; |
| 1972 | int unblinded_count = 0; |
| 1973 | for (const auto& psbt_out : psbt.outputs) { |
| 1974 | if (psbt_out.IsBlinded()) { |
| 1975 | is_fully_blinded &= psbt_out.IsFullyBlinded(); |
| 1976 | } else { |
| 1977 | unblinded_count++; |
| 1978 | } |
| 1979 | } |
| 1980 | if (is_fully_blinded) { |
| 1981 | base_psbt_index = i; |
| 1982 | has_fully_blinded = true; |
| 1983 | break; |
nothing calls this directly
no test coverage detected