Skip to content

Commit ca2cc38

Browse files
committed
Add anchor argument to transaction creation rpcs
1 parent 55f3266 commit ca2cc38

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

src/rpc/rawtransaction.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ static std::vector<RPCArg> CreateTxDoc()
154154
{"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
155155
},
156156
},
157+
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
158+
{
159+
{"anchor", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Creates an ephemeral anchor. A key-value pair. The key must be \"anchor\", the value is the amount in " + CURRENCY_UNIT},
160+
},
161+
},
157162
},
158163
},
159164
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},

src/rpc/rawtransaction_util.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
104104
// Duplicate checking
105105
std::set<CTxDestination> destinations;
106106
bool has_data{false};
107+
bool has_anchor{false};
107108

108109
for (const std::string& name_ : outputs.getKeys()) {
109110
if (name_ == "data") {
@@ -115,6 +116,16 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
115116

116117
CTxOut out(0, CScript() << OP_RETURN << data);
117118
rawTx.vout.push_back(out);
119+
} else if (name_ == "anchor") {
120+
if (has_anchor) {
121+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: anchor");
122+
}
123+
has_anchor = true;
124+
rawTx.nVersion = 3; // Implicitly opts into V3
125+
CAmount nAmount = AmountFromValue(outputs[name_]);
126+
127+
CTxOut out(nAmount, CScript() << OP_TRUE);
128+
rawTx.vout.push_back(out);
118129
} else {
119130
CTxDestination destination = DecodeDestination(name_);
120131
if (!IsValidDestination(destination)) {

test/functional/rpc_psbt.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
MAX_BIP125_RBF_SEQUENCE,
1919
WITNESS_SCALE_FACTOR,
2020
ser_compact_size,
21+
tx_from_hex,
2122
)
2223
from test_framework.psbt import (
2324
PSBT,
@@ -29,6 +30,7 @@
2930
PSBT_IN_HASH256,
3031
PSBT_OUT_TAP_TREE,
3132
)
33+
from test_framework.script import OP_TRUE
3234
from test_framework.test_framework import BitcoinTestFramework
3335
from test_framework.util import (
3436
assert_approx,
@@ -853,5 +855,20 @@ def test_psbt_input_keys(psbt_input, keys):
853855
assert_equal(self.nodes[0].combinepsbt([psbt1, psbt1]), psbt1)
854856

855857

858+
self.log.info("Test that PSBT can have ephemeral anchor added in rpc")
859+
# Fake input to avoid tripping up segwit deserialization error
860+
raw_anchor = self.nodes[0].createrawtransaction([{"txid": "ff"*32, "vout": 0}], [{"anchor":"0.00000001"}])
861+
anchor_tx = tx_from_hex(raw_anchor)
862+
863+
assert_equal(anchor_tx.nVersion, 3)
864+
assert_equal(len(anchor_tx.vout), 1)
865+
assert_equal(anchor_tx.vout[0].nValue, 1)
866+
assert_equal(anchor_tx.vout[0].scriptPubKey, bytes([OP_TRUE]))
867+
868+
psbt_anchor = self.nodes[0].createpsbt([{"txid": "ff"*32, "vout": 0}], [{"anchor":"0.00000001"}])
869+
anchor = PSBT.from_base64(psbt_anchor)
870+
871+
assert_equal(anchor.g.map[0], anchor_tx.serialize())
872+
856873
if __name__ == '__main__':
857874
PSBTTest().main()

0 commit comments

Comments
 (0)