diff --git a/console/embedded/native_css.cpp b/console/embedded/native_css.cpp index dfbe847d..5f82ab0b 100644 --- a/console/embedded/native_css.cpp +++ b/console/embedded/native_css.cpp @@ -21,7 +21,6 @@ namespace libbitcoin { namespace server { -// Simple test css for embedded page, links in font. DEFINE_EMBEDDED_PAGE(native_pages, char, css, R"DELIM(@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-400:oklch(70.4% .191 22.216);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-rose-500:oklch(64.5% .246 16.439);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xl:36rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-wide:.025em;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0, 0, .2, 1);--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:wh)DELIM" R"DELIM(ere(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}input:where([type=text]),input:where(:not([type])),input:where([type=email]),input:where([type=url]),input:where([type=password]),input:where([type=number]),input:where([type=date]),input:where([type=datetime-local]),input:where([type=month]),input:where([type=search]),input:where([type=tel]),input:where([type=time]),input:where([type=week]),select:where([multiple]),textarea,select{appearance:none;--tw-shadow:0 0 #0000;background-color:#fff;border-width:1px;border-color:#6a7282;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem}:is(input:where([type=text]),input:where(:not([type])),input:where([type=email]),input:where([type=url]),input:where([type=password]),input:where([type=number]),input:where([type=date]),input:where([type=datetime-local]),input:where([type=month]),input:where([type=search]),input:where([type=tel]),input:where([type=time]),input:where([type=week]),select:where([multiple]),textarea,select):focus{outline-offset:2px;--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:oklch(54.6% .245 262.881);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#155dfc;outline:2px solid #0000}input::placeholder,textarea::placeholder{color:#6a7282;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-date-and-time-value{text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-month-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-day-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-hour-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-minute-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-second-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-millisecond-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{-webkit-print-color-adjust:exact;print-color-adjust:exact;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='oklch(55.1%25 0.027 264.364)' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem}select:where([multiple]),select:where([size]:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;-webkit-print-color-adjust:unset;print-color-adjust:unset;padding-right:.75rem}input:where([type=checkbox]),input:where([type=radio]){appearance:none;-webkit-print-color-adjust:exact;print-color-adjust:exact;vertical-align:middle;-webkit-user-select:none;user-select:none;color:#155dfc;--tw-shadow:0 0 #0000;background-color:#fff;background-origin:border-box;border-width:1px;border-color:#6a7282;flex-shrink:0;width:1rem;height:1rem;padding:0;display:inline-block}input:where([type=checkbox]){border-radius:0}input:where([type=radio]){border-radius:100%}input:where([type=checkbox]):focus,input:where([type=radio]):focus{outline-offset:2px;--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:2px;--tw)DELIM" @@ -32,7 +31,6 @@ DEFINE_EMBEDDED_PAGE(native_pages, char, css, R"DELIM(;inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false} )DELIM" - ) } // namespace server diff --git a/console/embedded/native_ecma.cpp b/console/embedded/native_ecma.cpp index fffd3e84..979ba251 100644 --- a/console/embedded/native_ecma.cpp +++ b/console/embedded/native_ecma.cpp @@ -21,7 +21,6 @@ namespace libbitcoin { namespace server { -// Simple test ecma script for embedded page. DEFINE_EMBEDDED_PAGE(native_pages, char, ecma, R"DELIM(function dS(t,a){for(var i=0;il[s]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))l(s);new MutationObserver(s=>{for(const u of s)if(u.type==="childList")for(const f of u.addedNodes)f.tagName==="LINK"&&f.rel==="modulepreload"&&l(f)}).observe(document,{childList:!0,subtree:!0});function i(s){const u={};return s.integrity&&(u.integrity=s.integrity),s.referrerPolicy&&(u.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?u.credentials="include":s.crossOrigin==="anonymous"?u.credentials="omit":u.credentials="same-origin",u}function l(s){if(s.ep)return;s.ep=!0;const u=i(s);fetch(s.href,u)}})();function Gd(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var Hf={exports:{}},Ul={};var fv;function hS(){if(fv)return Ul;fv=1;var t=Symbol.for("react.transitional.element"),a=Symbol.for("react.fragment");function i(l,s,u){var f=null;if(u!==void 0&&(f=""+u),s.key!==void 0&&(f=""+s.key),"key"in s){u={};for(var h in s)h!=="key"&&(u[h]=s[h])}else u=s;return s=u.ref,{$$typeof:t,type:l,key:f,ref:s!==void 0?s:null,props:u}}return Ul.Fragment=a,Ul.jsx=i,Ul.jsxs=i,Ul}var dv;function mS(){return dv||(dv=1,Hf.exports=hS()),Hf.exports}var b=mS(),Uf={exports:{}},ze={};var hv;function pS(){if(hv)return ze;hv=1;var t=Symbol.for("react.transitional.element"),a=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),l=Symbol.for("react.strict_mode"),s=Symbol.for("react.profiler"),u=Symbol.for("react.consumer"),f=Symbol.for("react.context"),h=Symbol.for("react.forward_ref"),p=Symbol.for("react.suspense"),m=Symbol.for("react.memo"),g=Symbol.for("react.lazy"),v=Symbol.for("react.activity"),x=Symbol.iterator;function w(A){return A===null||typeof A!="object"?null:(A=x&&A[x]||A["@@iterator"],typeof A=="function"?A:null)}var _={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},E=Object.assign,N={};function M(A,Q,te){this.props=A,this.context=Q,this.refs=N,this.updater=te||_}M.prototype.isReactComponent={},M.prototype.setState=function(A,Q){if(typeof A!="object"&&typeof A!="function"&&A!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,A,Q,"setState")},M.prototype.forceUpdate=function(A){this.updater.enqueueForceUpdate(this,A,"forceUpdate")};function D(){}D.prototype=M.prototype;function B(A,Q,te){this.props=A,this.context=Q,this.refs=N,this.updater=te||_}var K=B.prototype=new D;K.constructor=B,E(K,M.prototype),K.isPureReactComponent=!0;var X=Array.isArray;function le(){}var C={H:null,A:null,T:null,S:null},Y=Object.prototype.hasOwnProperty;function he(A,Q,te){var re=te.ref;return{$$typeof:t,type:A,key:Q,ref:re!==void 0?re:null,props:te}}function se(A,Q){return he(A.type,Q,A.props)}function ue(A){return typeof A=="object"&&A!==null&&A.$$typeof===t}function ne(A){var Q={"=":"=0",":":"=2"};return"$"+A.replace(/[=:]/g,function(te){return Q[te]})}var Te=/\/+/g;function Ae(A,Q){return typeof A=="object"&&A!==null&&A.key!=null?ne(""+A.key):Q.toString(36)}function ee(A){switch(A.status){case"fulfilled":return A.value;case"rejected":throw A.reason;default:switch(typeof A.status=="string"?A.then(le,le):(A.status="pending",A.then(function(Q){A.status==="pending"&&(A.status="fulfilled",A.value=Q)},function(Q){A.status==="pending"&&(A.status="rejected",A.reason=Q)})),A.status){case"fulfilled":return A.value;case"rejected":throw A.reason}}throw A}function O(A,Q,te,re,ve){var Re=typeof A;(Re==="undefined"||Re==="boolean")&&(A=null);var ie=!1;if(A===null)ie=!0;else switch(Re){case"bigint":case"string":case"number":ie=!0;break;case"object":switch(A.$$typeof){case t:case a:ie=!0;break;case g:return ie=A._init,O(ie(A._payload),Q,te,re,ve)}}if(ie)return ve=ve(A),ie=re===""?"."+Ae(A,0):re,X(ve)?(te="",ie!=null&&(te=ie.replace(Te,"$&/")+"/"),O(ve,Q,te,"",function(Yt){return Yt})):ve!=null&&(ue(ve)&&(ve=se(ve,te+(ve.key==null||A&&A.key===ve.key?"":(""+ve.key).replace(Te,"$&/")+"/")+ie)),Q.push(ve)),1;ie=0;var je=re===""?".":re+":";if(X(A))for(var Me=0;Me>>1,be=O[fe];if(0>>1;feb.jsxs("tr",{className:"hover:bg-accentSecondary/10",children:[b.jsx("td",{className:"px-4 py-3",children:b.jsx(Bt,{to:Ln(N.hash),className:"text-accent hover:text-accentSecondary font-medium",children:rt(N.height,{maximumFractionDigits:0})})}),b.jsx("td",{className:"text-textPrimary px-4 py-3",children:b.jsx(ia,{value:N.hash,className:"font-mono"})}),b.jsx("td",{className:"text-textMuted px-4 py-3",children:Eu(N.timestamp)}),b.jsx("td",{className:"text-textPrimary px-4 py-3 text-right",children:rt(N.txCount)}),b.jsx("td",{className:"text-textPrimary px-4 py-3 text-right",children:typeof N.size=="number"&&N.size>0?`${rt(N.size/1e6,{maximumFractionDigits:2})} MB`:"—"})]},N.hash))]})]})}),v&&!u&&!w&&b.jsx("div",{className:"flex justify-center",children:b.jsx("button",{type:"button",onClick:()=>{E()},disabled:h,className:"border-accent text-accent hover:border-accentSecondary hover:text-accentSecondary rounded-md border px-4 py-2 text-sm font-medium disabled:cursor-not-allowed disabled:opacity-60",children:h?"Loading more…":"Load more blocks"})})]})}const uA=qz(vz(b.jsxs(Mt,{element:b.jsx(k3,{}),errorElement:b.jsx(r0,{}),children:[b.jsx(Mt,{index:!0,element:b.jsx(mz,{to:"/v1",replace:!0})}),b.jsx(Mt,{path:"v1",element:b.jsx(L3,{})}),b.jsx(Mt,{path:"v1/block",element:b.jsx(sA,{})}),b.jsx(Mt,{path:"v1/block/hash/:hash",element:b.jsx(rr,{})}),b.jsx(Mt,{path:"v1/block/hash/:hash/header",element:b.jsx(rr,{})}),b.jsx(Mt,{path:"v1/block/hash/:hash/header/context",element:b.jsx(rr,{})}),b.jsx(Mt,{path:"v1/block/hash/:hash/details",element:b.jsx(rr,{})}),b.jsx(Mt,{path:"v1/block/height/:height",element:b.jsx(rr,{})}),b.jsx(Mt,{path:"v1/block/height/:height/header",element:b.jsx(rr,{})}),b.jsx(Mt,{path:"v1/block/height/:height/header/context",element:b.jsx(rr,{})}),b.jsx(Mt,{path:"v1/block/height/:height/details",element:b.jsx(rr,{})}),b.jsx(Mt,{path:"v1/tx/:txid",element:b.jsx(ud,{})}),b.jsx(Mt,{path:"v1/tx/:txid/header",element:b.jsx(ud,{})}),b.jsx(Mt,{path:"v1/tx/:txid/details",element:b.jsx(ud,{})}),b.jsx(Mt,{path:"v1/address/:address",element:b.jsx(iA,{})}),b.jsx(Mt,{path:"v1/search",element:b.jsx(oA,{})}),b.jsx(Mt,{path:"*",element:b.jsx(r0,{})})]})));function cA(){return b.jsx(T.Suspense,{fallback:b.jsx("div",{className:"p-6",children:"Loading…"}),children:b.jsx(t3,{router:uA})})}const i1=document.querySelector("#root");if(!i1)throw new Error("Root element not found");_S.createRoot(i1).render(b.jsx(i0.StrictMode,{children:b.jsx(RT,{children:b.jsx(cA,{})})})); )DELIM" - ) } // namespace server diff --git a/console/embedded/native_font.cpp b/console/embedded/native_font.cpp index 2ab63770..b59bfd3c 100644 --- a/console/embedded/native_font.cpp +++ b/console/embedded/native_font.cpp @@ -21,6 +21,7 @@ namespace libbitcoin { namespace server { +// Bogus default font for embedded page. DEFINE_EMBEDDED_PAGE(native_pages, uint8_t, font, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/console/embedded/native_html.cpp b/console/embedded/native_html.cpp index 63b7a9dd..1c7b2855 100644 --- a/console/embedded/native_html.cpp +++ b/console/embedded/native_html.cpp @@ -21,16 +21,15 @@ namespace libbitcoin { namespace server { -// Simple test html for embedded page, links in css and page icon. DEFINE_EMBEDDED_PAGE(native_pages, char, html, R"DELIM( - + - libbitcoin Explorer + Libbitcoin Server diff --git a/console/embedded/native_icon.cpp b/console/embedded/native_icon.cpp index eca820d2..5b699a41 100644 --- a/console/embedded/native_icon.cpp +++ b/console/embedded/native_icon.cpp @@ -21,6 +21,7 @@ namespace libbitcoin { namespace server { +// Bogus default icon for embedded page. DEFINE_EMBEDDED_PAGE(native_pages, uint8_t, icon, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/src/protocols/protocol_electrum.cpp b/src/protocols/protocol_electrum.cpp index c778f977..8555c062 100644 --- a/src/protocols/protocol_electrum.cpp +++ b/src/protocols/protocol_electrum.cpp @@ -447,11 +447,40 @@ void protocol_electrum::handle_blockchain_transaction_broadcast(const code& ec, } void protocol_electrum::handle_blockchain_transaction_get(const code& ec, - rpc_interface::blockchain_transaction_get, const std::string& , - bool ) NOEXCEPT + rpc_interface::blockchain_transaction_get, const std::string& tx_hash, + bool verbose) NOEXCEPT { - if (stopped(ec)) return; - send_code(error::not_implemented); + if (stopped(ec)) + return; + + // Changed in version 1.2: verbose argument added. + // Changed in version 1.1: ignored argument height removed. + + hash_digest hash{}; + if (!decode_hash(hash, tx_hash)) + { + send_code(error::invalid_argument); + return; + } + + const auto& query = archive(); + const auto tx = query.get_transaction(query.to_tx(hash), true); + if (!tx) + { + send_code(error::not_found); + return; + } + + const auto size = tx->serialized_size(true); + if (verbose) + { + // Verbose means whatever bitcoind returns for getrawtransaction, lolz. + send_result(value_from(bitcoind(*tx)), two * size, BIND(complete, _1)); + } + else + { + send_result(to_hex(*tx, size, true), two * size, BIND(complete, _1)); + } } void protocol_electrum::handle_blockchain_transaction_get_merkle(const code& ec, @@ -486,7 +515,7 @@ void protocol_electrum::handle_server_banner(const code& ec, if (stopped(ec)) return; - send_result({ options().banner_message }, 42, BIND(complete, _1)); + send_result(options().banner_message, 42, BIND(complete, _1)); } void protocol_electrum::handle_server_donation_address(const code& ec, @@ -495,7 +524,7 @@ void protocol_electrum::handle_server_donation_address(const code& ec, if (stopped(ec)) return; - send_result({ options().donation_address }, 42, BIND(complete, _1)); + send_result(options().donation_address, 42, BIND(complete, _1)); } void protocol_electrum::handle_server_features(const code& ec, @@ -520,7 +549,7 @@ void protocol_electrum::handle_server_ping(const code& ec, return; // Any receive, including ping, resets the base channel inactivity timer. - send_result({ null_t{} }, 42, BIND(complete, _1)); + send_result(null_t{}, 42, BIND(complete, _1)); } // Handlers (mempool). diff --git a/test/protocols/electrum/electrum_transactions.cpp b/test/protocols/electrum/electrum_transactions.cpp index 57b77054..63a11d96 100644 --- a/test/protocols/electrum/electrum_transactions.cpp +++ b/test/protocols/electrum/electrum_transactions.cpp @@ -26,6 +26,8 @@ BOOST_FIXTURE_TEST_SUITE(electrum_tests, electrum_setup_fixture) // blockchain.transaction.broadcast using namespace system; +static const code not_found{ server::error::not_found }; +static const code not_implemented{ server::error::not_implemented }; static const code invalid_argument{ server::error::invalid_argument }; static const code unconfirmable_transaction{ server::error::unconfirmable_transaction }; @@ -63,4 +65,79 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_broadcast__genesis_coinbas BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), unconfirmable_transaction.value()); } +// blockchain.transaction.get + +BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__empty_hash__invalid_argument) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":77,"method":"blockchain.transaction.get","params":["",false]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__invalid_hash__invalid_argument) +{ + BOOST_CHECK(handshake()); + + const auto response = get(R"({"id":78,"method":"blockchain.transaction.get","params":["deadbeef",false]})" "\n"); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__nonexistent_tx__not_found) +{ + BOOST_CHECK(handshake()); + + const auto bogus = "0000000000000000000000000000000000000000000000000000000000000042"; + const auto request = R"({"id":79,"method":"blockchain.transaction.get","params":["%1%",false]})" "\n"; + const auto response = get((boost::format(request) % bogus).str()); + BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__missing_param__dropped) +{ + BOOST_CHECK(handshake()); + + const auto& coinbase = *genesis.transactions_ptr()->front(); + const auto tx_hash = encode_hash(coinbase.hash(false)); + const auto request = R"({"id":80,"method":"blockchain.transaction.get","params":["%1%"]})" "\n"; + const auto response = get((boost::format(request) % tx_hash).str()); + BOOST_CHECK(response.at("dropped").as_bool()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__extra_param__dropped) +{ + BOOST_CHECK(handshake()); + + const auto& coinbase = *genesis.transactions_ptr()->front(); + const auto tx_hash = encode_hash(coinbase.hash(false)); + const auto request = R"({"id":81,"method":"blockchain.transaction.get","params":["%1%",false,"extra"]})" "\n"; + const auto response = get((boost::format(request) % tx_hash).str()); + BOOST_CHECK(response.at("dropped").as_bool()); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__genesis_coinbase_verbose_false__expected) +{ + BOOST_CHECK(handshake()); + + const auto& coinbase = *genesis.transactions_ptr()->front(); + const auto tx_hash = encode_hash(coinbase.hash(false)); + const auto request = R"({"id":82,"method":"blockchain.transaction.get","params":["%1%",false]})" "\n"; + const auto response = get((boost::format(request) % tx_hash).str()); + BOOST_CHECK_EQUAL(response.at("result").as_string(), encode_base16(coinbase.to_data(true))); +} + +BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__genesis_coinbase_verbose_true__expected) +{ + BOOST_CHECK(handshake()); + + const auto& coinbase = *genesis.transactions_ptr()->front(); + const auto tx_hash = encode_hash(coinbase.hash(false)); + const auto request = R"({"id":83,"method":"blockchain.transaction.get","params":["%1%",true]})" "\n"; + const auto response = get((boost::format(request) % tx_hash).str()); + BOOST_CHECK_EQUAL(response.at("result").as_object(), value_from(bitcoind(coinbase))); +} + +// blockchain.transaction.get_merkle +// blockchain.transaction.id_from_pos + BOOST_AUTO_TEST_SUITE_END()