diff --git a/404.html b/404.html new file mode 100644 index 000000000..edc3922da --- /dev/null +++ b/404.html @@ -0,0 +1,27 @@ + + + + + + 404 | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + +
Skip to content

404

PAGE NOT FOUND

But if you don't change your direction, and if you keep looking, you may end up where you are heading.
+ + + + \ No newline at end of file diff --git a/apple-touch-icon.png b/apple-touch-icon.png new file mode 100644 index 000000000..63b5d4f8c Binary files /dev/null and b/apple-touch-icon.png differ diff --git a/assets/KeePassXC.31ae0839.png b/assets/KeePassXC.31ae0839.png new file mode 100644 index 000000000..be329b6dc Binary files /dev/null and b/assets/KeePassXC.31ae0839.png differ diff --git a/assets/app.b49dec97.js b/assets/app.b49dec97.js new file mode 100644 index 000000000..dd4373b57 --- /dev/null +++ b/assets/app.b49dec97.js @@ -0,0 +1 @@ +import{d as _,o as l,b as d,a1 as E,l as i,w as m,k as P,H as f,U as c,V as s,_ as y,C as u,a2 as R,a3 as g,s as p,a4 as L,a5 as T,a6 as w,a7 as x,a8 as D,a9 as O,aa as V,ab as k,ac as M,ad as $,u as I,j as S,y as B,ae as F,af as j,ag as N,ah as U}from"./chunks/framework.1293becd.js";import{t as h}from"./chunks/theme.3c30bd29.js";const H={class:"sticky-container py-4"},W=_({__name:"LayoutCustom",setup(e){const{Layout:t}=h,a=c(()=>s(()=>import("./chunks/ShareFeedback.9f629070.js"),["assets/chunks/ShareFeedback.9f629070.js","assets/chunks/framework.1293becd.js","assets/chunks/vue-kakuyaku.a530ead7.js","assets/chunks/IconCheck.ce061fbd.js"])),n="https://164.92.190.45/feedback/form";return(r,A)=>(l(),d(i(t),null,E({_:2},[i(n)?{name:"sidebar-nav-before",fn:m(()=>[P("div",H,[f(i(a),{"feedback-url":i(n)},null,8,["feedback-url"])])]),key:"0"}:void 0]),1024))}});const z=y(W,[["__scopeId","data-v-12fc2a51"]]),G=c(()=>s(()=>import("./chunks/MermaidRender.7316e2cd.js"),["assets/chunks/MermaidRender.7316e2cd.js","assets/chunks/vue-kakuyaku.a530ead7.js","assets/chunks/framework.1293becd.js"])),K={components:{Mermaid:G},inheritAttrs:!1};function q(e,t,a,n,r,A){const C=u("Mermaid"),b=u("ClientOnly");return l(),d(b,null,{default:m(()=>[f(C,R(g(e.$attrs)),null,16)]),_:1})}const J=y(K,[["render",q]]);const Q={...h,Layout:z,enhanceApp({app:e}){e.component("MermaidRenderWrap",J),e.component("CompatibilityMatrixTable",c(()=>s(()=>import("./chunks/CompatibilityMatrixTable.b72f55f3.js"),["assets/chunks/CompatibilityMatrixTable.b72f55f3.js","assets/chunks/vue-kakuyaku.a530ead7.js","assets/chunks/framework.1293becd.js","assets/chunks/CompatibilityMatrixTableIcon.vue_vue_type_style_index_0_lang.db34105e.js","assets/chunks/IconCheck.ce061fbd.js"]))),e.component("CompatibilityMatrixTableIcon",c(async()=>s(()=>import("./chunks/CompatibilityMatrixTableIcon.e79f5bb9.js"),["assets/chunks/CompatibilityMatrixTableIcon.e79f5bb9.js","assets/chunks/CompatibilityMatrixTableIcon.vue_vue_type_style_index_0_lang.db34105e.js","assets/chunks/IconCheck.ce061fbd.js","assets/chunks/framework.1293becd.js"])))}};function v(e){if(e.extends){const t=v(e.extends);return{...t,...e,async enhanceApp(a){t.enhanceApp&&await t.enhanceApp(a),e.enhanceApp&&await e.enhanceApp(a)}}}return e}const o=v(Q),X=_({name:"VitePressApp",setup(){const{site:e}=I();return S(()=>{B(()=>{document.documentElement.lang=e.value.lang,document.documentElement.dir=e.value.dir})}),F(),j(),N(),o.setup&&o.setup(),()=>U(o.Layout)}});async function Y(){const e=ee(),t=Z();t.provide(T,e);const a=w(e.route);return t.provide(x,a),t.component("Content",D),t.component("ClientOnly",O),Object.defineProperties(t.config.globalProperties,{$frontmatter:{get(){return a.frontmatter.value}},$params:{get(){return a.page.value.params}}}),o.enhanceApp&&await o.enhanceApp({app:t,router:e,siteData:V}),{app:t,router:e,data:a}}function Z(){return k(X)}function ee(){let e=p,t;return M(a=>{let n=$(a),r=null;return n&&(e&&(t=n),(e||t===n)&&(n=n.replace(/\.js$/,".lean.js")),r=s(()=>import(n),[])),p&&(e=!1),r},o.NotFound)}p&&Y().then(({app:e,router:t,data:a})=>{t.go().then(()=>{L(t.route,a.site),e.mount("#app")})});export{Y as createApp}; diff --git a/assets/appendix_running-iroha_cli-output.159236c6.png b/assets/appendix_running-iroha_cli-output.159236c6.png new file mode 100644 index 000000000..36ef3d37d Binary files /dev/null and b/assets/appendix_running-iroha_cli-output.159236c6.png differ diff --git a/assets/chunks/@localSearchIndexroot.46c613a1.js b/assets/chunks/@localSearchIndexroot.46c613a1.js new file mode 100644 index 000000000..bd22fb599 --- /dev/null +++ b/assets/chunks/@localSearchIndexroot.46c613a1.js @@ -0,0 +1 @@ +const e='{"documentCount":778,"nextId":778,"documentIds":{"0":"/iroha-2-docs/documenting/snippets.html#code-snippets","1":"/iroha-2-docs/documenting/snippets.html#how-it-works","2":"/iroha-2-docs/documenting/snippets.html#snippet-sources","3":"/iroha-2-docs/documenting/snippets.html#fetching-snippets","4":"/iroha-2-docs/documenting/snippets.html#using-snippets-in-markdown","5":"/iroha-2-docs/documenting/snippets.html#example","6":"/iroha-2-docs/guide/advanced/hot-reload.html#how-to-hot-reload-iroha-in-a-docker-container","7":"/iroha-2-docs/guide/advanced/hot-reload.html#wiping-previous-blockchain-state-recommit-genesis","8":"/iroha-2-docs/guide/advanced/hot-reload.html#use-custom-configuration-files","9":"/iroha-2-docs/guide/advanced/hot-reload.html#use-custom-environment-variables","10":"/iroha-2-docs/guide/advanced/metrics.html#metrics","11":"/iroha-2-docs/guide/advanced/metrics.html#how-to-use-metrics","12":"/iroha-2-docs/guide/advanced/metrics.html#metrics-endpoint","13":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#iroha-on-bare-metal","14":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#prerequisites","15":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#setup","16":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#setup-environment-variables","17":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#setup-files","18":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#first-run-of-iroha-on-bare-metal","19":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#deploy-a-minimal-bft-network","20":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#using-environment-variables","21":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#using-files","22":"/iroha-2-docs/guide/advanced/running-iroha-on-bare-metal.html#real-world-deployment","23":"/iroha-2-docs/guide/blockchain/accounts.html#accounts","24":"/iroha-2-docs/guide/blockchain/assets.html#assets","25":"/iroha-2-docs/guide/blockchain/assets.html#value-types","26":"/iroha-2-docs/guide/blockchain/assets.html#asset-structure","27":"/iroha-2-docs/guide/blockchain/assets.html#instructions","28":"/iroha-2-docs/guide/blockchain/consensus.html#consensus","29":"/iroha-2-docs/guide/blockchain/data-model.html#data-model","30":"/iroha-2-docs/guide/blockchain/domains.html#domains","31":"/iroha-2-docs/guide/blockchain/events.html#events","32":"/iroha-2-docs/guide/blockchain/events.html#pipeline-events","33":"/iroha-2-docs/guide/blockchain/events.html#data-events","34":"/iroha-2-docs/guide/blockchain/events.html#time-events","35":"/iroha-2-docs/guide/blockchain/events.html#trigger-execution-events","36":"/iroha-2-docs/guide/blockchain/expressions.html#expressions-conditionals-logic","37":"/iroha-2-docs/guide/blockchain/filters.html#filters","38":"/iroha-2-docs/guide/blockchain/filters.html#data-filters","39":"/iroha-2-docs/guide/blockchain/how-iroha-works.html#how-iroha-works","40":"/iroha-2-docs/guide/blockchain/instructions.html#iroha-special-instructions","41":"/iroha-2-docs/guide/blockchain/instructions.html#summary","42":"/iroha-2-docs/guide/blockchain/instructions.html#un-register","43":"/iroha-2-docs/guide/blockchain/instructions.html#mint-burn","44":"/iroha-2-docs/guide/blockchain/instructions.html#transfer","45":"/iroha-2-docs/guide/blockchain/instructions.html#grant-revoke","46":"/iroha-2-docs/guide/blockchain/instructions.html#setkeyvalue-removekeyvalue","47":"/iroha-2-docs/guide/blockchain/instructions.html#newparameter-setparameter","48":"/iroha-2-docs/guide/blockchain/instructions.html#executetrigger","49":"/iroha-2-docs/guide/blockchain/instructions.html#composite-instructions","50":"/iroha-2-docs/guide/blockchain/metadata.html#metadata","51":"/iroha-2-docs/guide/blockchain/metadata.html#metadatachanged","52":"/iroha-2-docs/guide/blockchain/metadata.html#store-asset","53":"/iroha-2-docs/guide/blockchain/metadata.html#working-with-metadata","54":"/iroha-2-docs/guide/blockchain/metadata.html#queries","55":"/iroha-2-docs/guide/blockchain/metadata.html#permissions","56":"/iroha-2-docs/guide/blockchain/permissions.html#permissions","57":"/iroha-2-docs/guide/blockchain/permissions.html#permission-tokens","58":"/iroha-2-docs/guide/blockchain/permissions.html#pre-configured-permission-tokens","59":"/iroha-2-docs/guide/blockchain/permissions.html#permission-groups-roles","60":"/iroha-2-docs/guide/blockchain/permissions.html#register-a-new-role","61":"/iroha-2-docs/guide/blockchain/permissions.html#grant-a-role","62":"/iroha-2-docs/guide/blockchain/permissions.html#permission-validators","63":"/iroha-2-docs/guide/blockchain/permissions.html#runtime-validators","64":"/iroha-2-docs/guide/blockchain/permissions.html#supported-queries","65":"/iroha-2-docs/guide/blockchain/queries.html#queries","66":"/iroha-2-docs/guide/blockchain/queries.html#create-a-query","67":"/iroha-2-docs/guide/blockchain/queries.html#pagination","68":"/iroha-2-docs/guide/blockchain/queries.html#filters","69":"/iroha-2-docs/guide/blockchain/queries.html#sorting","70":"/iroha-2-docs/guide/blockchain/queries.html#reference","71":"/iroha-2-docs/guide/blockchain/transactions.html#transactions","72":"/iroha-2-docs/guide/blockchain/triggers.html#triggers","73":"/iroha-2-docs/guide/blockchain/triggers.html#the-anatomy-of-a-trigger","74":"/iroha-2-docs/guide/blockchain/triggers.html#trigger-id","75":"/iroha-2-docs/guide/blockchain/triggers.html#trigger-action","76":"/iroha-2-docs/guide/blockchain/triggers.html#action-executable","77":"/iroha-2-docs/guide/blockchain/triggers.html#action-repeats","78":"/iroha-2-docs/guide/blockchain/triggers.html#action-technical-account","79":"/iroha-2-docs/guide/blockchain/triggers.html#action-filter","80":"/iroha-2-docs/guide/blockchain/triggers.html#action-metadata","81":"/iroha-2-docs/guide/blockchain/triggers.html#how-triggers-work","82":"/iroha-2-docs/guide/blockchain/triggers.html#scope","83":"/iroha-2-docs/guide/blockchain/triggers.html#domain-scoped-triggers","84":"/iroha-2-docs/guide/blockchain/triggers.html#repetition-schema","85":"/iroha-2-docs/guide/blockchain/triggers.html#types-of-triggers","86":"/iroha-2-docs/guide/blockchain/triggers.html#data-triggers","87":"/iroha-2-docs/guide/blockchain/triggers.html#time-triggers","88":"/iroha-2-docs/guide/blockchain/triggers.html#scheduled-triggers","89":"/iroha-2-docs/guide/blockchain/triggers.html#pre-commit-triggers","90":"/iroha-2-docs/guide/blockchain/triggers.html#by-call-triggers","91":"/iroha-2-docs/guide/blockchain/triggers.html#event-triggers-by-example","92":"/iroha-2-docs/guide/blockchain/triggers.html#_1-register-accounts","93":"/iroha-2-docs/guide/blockchain/triggers.html#_2-register-a-trigger","94":"/iroha-2-docs/guide/blockchain/triggers.html#_3-define-an-event-filter","95":"/iroha-2-docs/guide/blockchain/triggers.html#_4-create-a-trigger-instance","96":"/iroha-2-docs/guide/blockchain/triggers.html#_5-create-a-transaction","97":"/iroha-2-docs/guide/blockchain/triggers.html#how-it-works","98":"/iroha-2-docs/guide/blockchain/triggers.html#supported-isi","99":"/iroha-2-docs/guide/blockchain/triggers.html#supported-queries","100":"/iroha-2-docs/guide/blockchain/wasm.html#wasm","101":"/iroha-2-docs/guide/blockchain/wasm.html#working-with-wasm","102":"/iroha-2-docs/guide/blockchain/wasm.html#simple-rust-smart-contract-example","103":"/iroha-2-docs/guide/blockchain/wasm.html#_1-create-a-new-project","104":"/iroha-2-docs/guide/blockchain/wasm.html#_2-write-a-smart-contract","105":"/iroha-2-docs/guide/blockchain/wasm.html#advanced-smart-contracts-optimising-for-size","106":"/iroha-2-docs/guide/blockchain/wasm.html#remove-debugging-info","107":"/iroha-2-docs/guide/blockchain/wasm.html#work-under-a-no-std-environment","108":"/iroha-2-docs/guide/blockchain/wasm.html#re-compile-libcore","109":"/iroha-2-docs/guide/blockchain/wasm.html#use-tools-to-optimise-wasm-size","110":"/iroha-2-docs/guide/blockchain/wasm.html#conclusion","111":"/iroha-2-docs/guide/blockchain/world.html#world","112":"/iroha-2-docs/guide/blockchain/world.html#world-state-view-wsv","113":"/iroha-2-docs/guide/configure/client-configuration.html#client-configuration","114":"/iroha-2-docs/guide/configure/client-configuration.html#generation","115":"/iroha-2-docs/guide/configure/client-configuration.html#public-and-private-keys","116":"/iroha-2-docs/guide/configure/client-configuration.html#user-account","117":"/iroha-2-docs/guide/configure/client-configuration.html#basic-authentication-credentials","118":"/iroha-2-docs/guide/configure/client-configuration.html#iroha-public-addresses","119":"/iroha-2-docs/guide/configure/client-configuration.html#torii-api-url","120":"/iroha-2-docs/guide/configure/client-configuration.html#torii-telemetry-url","121":"/iroha-2-docs/guide/configure/client-configuration.html#transaction-limits","122":"/iroha-2-docs/guide/configure/client-configuration.html#transaction-ttl-and-timeout","123":"/iroha-2-docs/guide/configure/client-configuration.html#transaction-nonce","124":"/iroha-2-docs/guide/configure/configuration-types.html#configuration-types","125":"/iroha-2-docs/guide/configure/configuration-types.html#option","126":"/iroha-2-docs/guide/configure/configuration-types.html#option-option","127":"/iroha-2-docs/guide/configure/configuration-types.html#sumeragi-default-null-values","128":"/iroha-2-docs/guide/configure/genesis.html#genesis-block","129":"/iroha-2-docs/guide/configure/genesis.html#generation","130":"/iroha-2-docs/guide/configure/genesis.html#generate-default-genesis-block","131":"/iroha-2-docs/guide/configure/genesis.html#configuration","132":"/iroha-2-docs/guide/configure/keys-for-network-deployment.html#keys-for-network-deployment","133":"/iroha-2-docs/guide/configure/keys-for-network-deployment.html#setting-keys-for-a-new-network","134":"/iroha-2-docs/guide/configure/keys-for-network-deployment.html#_1-generate-new-key-pairs","135":"/iroha-2-docs/guide/configure/keys-for-network-deployment.html#_2-update-keys-for-peers","136":"/iroha-2-docs/guide/configure/keys-for-network-deployment.html#_3-register-a-non-genesis-account","137":"/iroha-2-docs/guide/configure/metadata-and-store-assets.html#choosing-between-the-store-and-metadata-assets","138":"/iroha-2-docs/guide/configure/modes.html#public-and-private-blockchains","139":"/iroha-2-docs/guide/configure/modes.html#permissions","140":"/iroha-2-docs/guide/configure/modes.html#peers","141":"/iroha-2-docs/guide/configure/modes.html#registering-accounts","142":"/iroha-2-docs/guide/configure/overview.html#configuration-and-management","143":"/iroha-2-docs/guide/configure/peer-configuration.html#peer-configuration","144":"/iroha-2-docs/guide/configure/peer-configuration.html#generation","145":"/iroha-2-docs/guide/configure/peer-configuration.html#public-and-private-keys","146":"/iroha-2-docs/guide/configure/peer-configuration.html#trusted-peers","147":"/iroha-2-docs/guide/configure/peer-configuration.html#iroha-public-addresses","148":"/iroha-2-docs/guide/configure/peer-configuration.html#api-url","149":"/iroha-2-docs/guide/configure/peer-configuration.html#p2p-addr","150":"/iroha-2-docs/guide/configure/peer-configuration.html#telemetry-url","151":"/iroha-2-docs/guide/configure/peer-configuration.html#genesis","152":"/iroha-2-docs/guide/configure/peer-configuration.html#logger","153":"/iroha-2-docs/guide/configure/peer-configuration.html#max-log-level","154":"/iroha-2-docs/guide/configure/peer-configuration.html#log-file-path","155":"/iroha-2-docs/guide/configure/peer-configuration.html#kura","156":"/iroha-2-docs/guide/configure/peer-management.html#peer-management","157":"/iroha-2-docs/guide/configure/peer-management.html#public-blockchain","158":"/iroha-2-docs/guide/configure/peer-management.html#private-blockchain","159":"/iroha-2-docs/guide/configure/peer-management.html#registering-peers","160":"/iroha-2-docs/guide/configure/peer-management.html#_1-grant-the-user-permissions","161":"/iroha-2-docs/guide/configure/peer-management.html#_2-set-up-a-peer","162":"/iroha-2-docs/guide/configure/peer-management.html#_3-submit-the-instruction","163":"/iroha-2-docs/guide/configure/peer-management.html#unregistering-peers","164":"/iroha-2-docs/guide/configure/sample-configuration.html#sample-configuration-files","165":"/iroha-2-docs/guide/get-started/bash.html#bash-guide","166":"/iroha-2-docs/guide/get-started/bash.html#_0-a-brief-primer-on-cli-applications","167":"/iroha-2-docs/guide/get-started/bash.html#_1-iroha-2-client-setup","168":"/iroha-2-docs/guide/get-started/bash.html#_2-configuring-iroha-2","169":"/iroha-2-docs/guide/get-started/bash.html#_3-registering-a-domain","170":"/iroha-2-docs/guide/get-started/bash.html#_4-registering-an-account","171":"/iroha-2-docs/guide/get-started/bash.html#_5-registering-and-minting-assets","172":"/iroha-2-docs/guide/get-started/bash.html#_6-transferring-assets","173":"/iroha-2-docs/guide/get-started/bash.html#_7-burning-assets","174":"/iroha-2-docs/guide/get-started/bash.html#_8-visualizing-outputs","175":"/iroha-2-docs/guide/get-started/build.html#build-iroha-2-client","176":"/iroha-2-docs/guide/get-started/build.html#install-the-rust-toolchain","177":"/iroha-2-docs/guide/get-started/build.html#build-iroha-client","178":"/iroha-2-docs/guide/get-started/#get-started","179":"/iroha-2-docs/guide/get-started/install.html#install-iroha-2","180":"/iroha-2-docs/guide/get-started/install.html#choose-version","181":"/iroha-2-docs/guide/get-started/install.html#install-prerequisites","182":"/iroha-2-docs/guide/get-started/install.html#install-openssl","183":"/iroha-2-docs/guide/get-started/install.html#install-iroha-from-github","184":"/iroha-2-docs/guide/get-started/install.html#what-s-next","185":"/iroha-2-docs/guide/get-started/javascript.html#javascript-typescript-guide","186":"/iroha-2-docs/guide/get-started/javascript.html#_1-client-installation","187":"/iroha-2-docs/guide/get-started/javascript.html#_2-client-configuration","188":"/iroha-2-docs/guide/get-started/javascript.html#_3-registering-a-domain","189":"/iroha-2-docs/guide/get-started/javascript.html#_4-registering-an-account","190":"/iroha-2-docs/guide/get-started/javascript.html#_5-registering-and-minting-assets","191":"/iroha-2-docs/guide/get-started/javascript.html#_6-transferring-assets","192":"/iroha-2-docs/guide/get-started/javascript.html#_7-querying-for-domains-accounts-and-assets","193":"/iroha-2-docs/guide/get-started/javascript.html#_8-visualizing-outputs-in-web-ui","194":"/iroha-2-docs/guide/get-started/javascript.html#demo","195":"/iroha-2-docs/guide/get-started/javascript.html#_9-subscribing-to-block-stream","196":"/iroha-2-docs/guide/get-started/kotlin-java.html#kotlin-java-guide","197":"/iroha-2-docs/guide/get-started/kotlin-java.html#_1-iroha-2-client-setup","198":"/iroha-2-docs/guide/get-started/kotlin-java.html#_2-configuring-iroha-2","199":"/iroha-2-docs/guide/get-started/kotlin-java.html#_3-querying-and-registering-domains","200":"/iroha-2-docs/guide/get-started/kotlin-java.html#_4-registering-an-account","201":"/iroha-2-docs/guide/get-started/kotlin-java.html#_5-registering-and-minting-assets","202":"/iroha-2-docs/guide/get-started/kotlin-java.html#_6-transferring-assets","203":"/iroha-2-docs/guide/get-started/kotlin-java.html#_7-burning-assets","204":"/iroha-2-docs/guide/get-started/kotlin-java.html#_8-visualizing-outputs","205":"/iroha-2-docs/guide/get-started/kotlin-java.html#_9-samples-in-pure-java","206":"/iroha-2-docs/guide/get-started/python.html#python-3-guide","207":"/iroha-2-docs/guide/get-started/python.html#_1-iroha-2-client-setup","208":"/iroha-2-docs/guide/get-started/python.html#_2-configuring-iroha-2","209":"/iroha-2-docs/guide/get-started/python.html#_3-registering-a-domain","210":"/iroha-2-docs/guide/get-started/python.html#_4-registering-an-account","211":"/iroha-2-docs/guide/get-started/python.html#_5-registering-and-minting-assets","212":"/iroha-2-docs/guide/get-started/python.html#_6-visualizing-outputs","213":"/iroha-2-docs/guide/get-started/quick-start.html#quick-start-with-docker","214":"/iroha-2-docs/guide/get-started/quick-start.html#docker-options","215":"/iroha-2-docs/guide/get-started/rust.html#rust-guide","216":"/iroha-2-docs/guide/get-started/rust.html#_1-iroha-2-client-setup","217":"/iroha-2-docs/guide/get-started/rust.html#_2-configuring-iroha-2","218":"/iroha-2-docs/guide/get-started/rust.html#_3-registering-a-domain","219":"/iroha-2-docs/guide/get-started/rust.html#_4-registering-an-account","220":"/iroha-2-docs/guide/get-started/rust.html#_5-registering-and-minting-assets","221":"/iroha-2-docs/guide/get-started/rust.html#_6-burning-assets","222":"/iroha-2-docs/guide/get-started/rust.html#_7-visualising-outputs","223":"/iroha-2-docs/guide/get-started/tutorials.html#introduction","224":"/iroha-2-docs/guide/get-started/tutorials.html#preamble","225":"/iroha-2-docs/guide/get-started/tutorials.html#navigation","226":"/iroha-2-docs/guide/get-started/tutorials.html#tutorial-updates","227":"/iroha-2-docs/guide/introduction.html#iroha-2","228":"/iroha-2-docs/guide/introduction.html#learn-more","229":"/iroha-2-docs/guide/iroha-2.html#iroha-2-vs-iroha-1","230":"/iroha-2-docs/guide/iroha-2.html#fault-tolerance","231":"/iroha-2-docs/guide/iroha-2.html#minimalist-code-base","232":"/iroha-2-docs/guide/iroha-2.html#flexibility","233":"/iroha-2-docs/guide/iroha-2.html#smart-contracts","234":"/iroha-2-docs/guide/iroha-2.html#static-and-dynamic-linking","235":"/iroha-2-docs/guide/iroha-2.html#testing","236":"/iroha-2-docs/guide/reports/csd-rtgs.html#csd-rtgs-linkages-proof-of-concept","237":"/iroha-2-docs/guide/security/generating-cryptographic-keys.html#generating-cryptographic-keys","238":"/iroha-2-docs/guide/security/generating-cryptographic-keys.html#kagami","239":"/iroha-2-docs/guide/security/generating-cryptographic-keys.html#examples","240":"/iroha-2-docs/guide/security/generating-cryptographic-keys.html#other-operations-with-kagami","241":"/iroha-2-docs/guide/security/generating-cryptographic-keys.html#_1-building-kagami","242":"/iroha-2-docs/guide/security/generating-cryptographic-keys.html#_2-installing-the-source-built-kagami-into-bin","243":"/iroha-2-docs/guide/security/generating-cryptographic-keys.html#_3-moving-kagami-to-the-local-bin-directory","244":"/iroha-2-docs/guide/security/generating-cryptographic-keys.html#making-the-username-local-bin-directory-available-to-the-shell","245":"/iroha-2-docs/guide/security/#security","246":"/iroha-2-docs/guide/security/#navigation","247":"/iroha-2-docs/guide/security/operational-security.html#operational-security","248":"/iroha-2-docs/guide/security/operational-security.html#recommended-opsec-measures","249":"/iroha-2-docs/guide/security/operational-security.html#using-browsers","250":"/iroha-2-docs/guide/security/operational-security.html#recovery-plan","251":"/iroha-2-docs/guide/security/password-security.html#password-security","252":"/iroha-2-docs/guide/security/password-security.html#password-strength","253":"/iroha-2-docs/guide/security/password-security.html#password-vulnerabilities","254":"/iroha-2-docs/guide/security/public-key-cryptography.html#public-key-cryptography","255":"/iroha-2-docs/guide/security/public-key-cryptography.html#encryption-and-signatures","256":"/iroha-2-docs/guide/security/public-key-cryptography.html#keys-on-the-client-side","257":"/iroha-2-docs/guide/security/security-principles.html#security-principles","258":"/iroha-2-docs/guide/security/security-principles.html#general-security-principles","259":"/iroha-2-docs/guide/security/security-principles.html#security-principles-for-individual-users","260":"/iroha-2-docs/guide/security/security-principles.html#security-principles-for-organisations","261":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#storing-cryptographic-keys","262":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#storing-cryptographic-keys-digitally","263":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#using-ssh-and-ssh-agent","264":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#adding-a-password-manager-program","265":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#configuring-keepassxc","266":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#expected-results","267":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#storing-cryptographic-keys-physically","268":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#using-a-hardware-key","269":"/iroha-2-docs/guide/security/storing-cryptographic-keys.html#using-a-mnemonic-phrase","270":"/iroha-2-docs/guide/support.html#receive-support","271":"/iroha-2-docs/guide/troubleshooting/configuration-issues.html#troubleshooting-configuration-issues","272":"/iroha-2-docs/guide/troubleshooting/configuration-issues.html#outdated-genesis-on-a-docker-compose-setup","273":"/iroha-2-docs/guide/troubleshooting/configuration-issues.html#multihash-format-of-private-and-public-keys","274":"/iroha-2-docs/guide/troubleshooting/deployment-issues.html#troubleshooting-deployment-issues","275":"/iroha-2-docs/guide/troubleshooting/deployment-issues.html#docker","276":"/iroha-2-docs/guide/troubleshooting/deployment-issues.html#kubernetes","277":"/iroha-2-docs/guide/troubleshooting/installation-issues.html#troubleshooting-installation-issues","278":"/iroha-2-docs/guide/troubleshooting/installation-issues.html#troubleshooting-rust-toolchain","279":"/iroha-2-docs/guide/troubleshooting/installation-issues.html#check-rust-version","280":"/iroha-2-docs/guide/troubleshooting/installation-issues.html#check-installation-location","281":"/iroha-2-docs/guide/troubleshooting/installation-issues.html#check-the-default-rust-version","282":"/iroha-2-docs/guide/troubleshooting/installation-issues.html#check-if-there-are-other-rust-versions","283":"/iroha-2-docs/guide/troubleshooting/installation-issues.html#troubleshooting-python-toolchain","284":"/iroha-2-docs/guide/troubleshooting/integration-issues.html#troubleshooting-integration-issues","285":"/iroha-2-docs/guide/troubleshooting/overview.html#troubleshooting","286":"/iroha-2-docs/guide/troubleshooting/overview.html#check-the-keys","287":"/iroha-2-docs/reference/compatibility-matrix.html#compatibility-matrix","288":"/iroha-2-docs/reference/data-model-schema.html#data-model-schema","289":"/iroha-2-docs/reference/data-model-schema.html#account","290":"/iroha-2-docs/reference/data-model-schema.html#accountevent","291":"/iroha-2-docs/reference/data-model-schema.html#accounteventfilter","292":"/iroha-2-docs/reference/data-model-schema.html#accountfilter","293":"/iroha-2-docs/reference/data-model-schema.html#accountid","294":"/iroha-2-docs/reference/data-model-schema.html#accountpermissionchanged","295":"/iroha-2-docs/reference/data-model-schema.html#accountrolechanged","296":"/iroha-2-docs/reference/data-model-schema.html#action-triggeringfilterbox-executable","297":"/iroha-2-docs/reference/data-model-schema.html#action-triggeringfilterbox-optimizedexecutable","298":"/iroha-2-docs/reference/data-model-schema.html#add","299":"/iroha-2-docs/reference/data-model-schema.html#algorithm","300":"/iroha-2-docs/reference/data-model-schema.html#and","301":"/iroha-2-docs/reference/data-model-schema.html#array-interval-u16-8","302":"/iroha-2-docs/reference/data-model-schema.html#array-interval-u8-4","303":"/iroha-2-docs/reference/data-model-schema.html#array-u16-8","304":"/iroha-2-docs/reference/data-model-schema.html#array-u8-32","305":"/iroha-2-docs/reference/data-model-schema.html#array-u8-4","306":"/iroha-2-docs/reference/data-model-schema.html#asset","307":"/iroha-2-docs/reference/data-model-schema.html#assetchanged","308":"/iroha-2-docs/reference/data-model-schema.html#assetdefinition","309":"/iroha-2-docs/reference/data-model-schema.html#assetdefinitionevent","310":"/iroha-2-docs/reference/data-model-schema.html#assetdefinitioneventfilter","311":"/iroha-2-docs/reference/data-model-schema.html#assetdefinitionfilter","312":"/iroha-2-docs/reference/data-model-schema.html#assetdefinitionid","313":"/iroha-2-docs/reference/data-model-schema.html#assetdefinitionownerchanged","314":"/iroha-2-docs/reference/data-model-schema.html#assetdefinitiontotalquantitychanged","315":"/iroha-2-docs/reference/data-model-schema.html#assetevent","316":"/iroha-2-docs/reference/data-model-schema.html#asseteventfilter","317":"/iroha-2-docs/reference/data-model-schema.html#assetfilter","318":"/iroha-2-docs/reference/data-model-schema.html#assetid","319":"/iroha-2-docs/reference/data-model-schema.html#assetvalue","320":"/iroha-2-docs/reference/data-model-schema.html#assetvaluetype","321":"/iroha-2-docs/reference/data-model-schema.html#atindex","322":"/iroha-2-docs/reference/data-model-schema.html#batchedresponse-value","323":"/iroha-2-docs/reference/data-model-schema.html#batchedresponse-vec-versionedsignedtransaction","324":"/iroha-2-docs/reference/data-model-schema.html#binaryopincompatiblenumericvaluetypeserror","325":"/iroha-2-docs/reference/data-model-schema.html#blockheader","326":"/iroha-2-docs/reference/data-model-schema.html#blockmessage","327":"/iroha-2-docs/reference/data-model-schema.html#blockpayload","328":"/iroha-2-docs/reference/data-model-schema.html#blockrejectionreason","329":"/iroha-2-docs/reference/data-model-schema.html#blocksubscriptionrequest","330":"/iroha-2-docs/reference/data-model-schema.html#burnbox","331":"/iroha-2-docs/reference/data-model-schema.html#conditional","332":"/iroha-2-docs/reference/data-model-schema.html#configurationevent","333":"/iroha-2-docs/reference/data-model-schema.html#container","334":"/iroha-2-docs/reference/data-model-schema.html#contains","335":"/iroha-2-docs/reference/data-model-schema.html#containsall","336":"/iroha-2-docs/reference/data-model-schema.html#containsany","337":"/iroha-2-docs/reference/data-model-schema.html#contextvalue","338":"/iroha-2-docs/reference/data-model-schema.html#dataentityfilter","339":"/iroha-2-docs/reference/data-model-schema.html#dataevent","340":"/iroha-2-docs/reference/data-model-schema.html#divide","341":"/iroha-2-docs/reference/data-model-schema.html#domain","342":"/iroha-2-docs/reference/data-model-schema.html#domainevent","343":"/iroha-2-docs/reference/data-model-schema.html#domaineventfilter","344":"/iroha-2-docs/reference/data-model-schema.html#domainfilter","345":"/iroha-2-docs/reference/data-model-schema.html#domainid","346":"/iroha-2-docs/reference/data-model-schema.html#duration","347":"/iroha-2-docs/reference/data-model-schema.html#equal","348":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-accountid","349":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-assetdefinitionid","350":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-assetid","351":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-domainid","352":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-hashof-versionedsignedblock","353":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-hashof-versionedsignedtransaction","354":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-idbox","355":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-level","356":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-name","357":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-numericvalue","358":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-parameter","359":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-registrablebox","360":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-roleid","361":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-string","362":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-triggerid","363":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-upgradablebox","364":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-value","365":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-vec-value","366":"/iroha-2-docs/reference/data-model-schema.html#evaluatesto-bool","367":"/iroha-2-docs/reference/data-model-schema.html#evaluationerror","368":"/iroha-2-docs/reference/data-model-schema.html#event","369":"/iroha-2-docs/reference/data-model-schema.html#eventmessage","370":"/iroha-2-docs/reference/data-model-schema.html#eventsubscriptionrequest","371":"/iroha-2-docs/reference/data-model-schema.html#executable","372":"/iroha-2-docs/reference/data-model-schema.html#executetriggerbox","373":"/iroha-2-docs/reference/data-model-schema.html#executetriggerevent","374":"/iroha-2-docs/reference/data-model-schema.html#executetriggereventfilter","375":"/iroha-2-docs/reference/data-model-schema.html#executiontime","376":"/iroha-2-docs/reference/data-model-schema.html#expression","377":"/iroha-2-docs/reference/data-model-schema.html#failbox","378":"/iroha-2-docs/reference/data-model-schema.html#filterbox","379":"/iroha-2-docs/reference/data-model-schema.html#filteropt-accounteventfilter","380":"/iroha-2-docs/reference/data-model-schema.html#filteropt-accountfilter","381":"/iroha-2-docs/reference/data-model-schema.html#filteropt-assetdefinitioneventfilter","382":"/iroha-2-docs/reference/data-model-schema.html#filteropt-assetdefinitionfilter","383":"/iroha-2-docs/reference/data-model-schema.html#filteropt-asseteventfilter","384":"/iroha-2-docs/reference/data-model-schema.html#filteropt-assetfilter","385":"/iroha-2-docs/reference/data-model-schema.html#filteropt-dataentityfilter","386":"/iroha-2-docs/reference/data-model-schema.html#filteropt-domaineventfilter","387":"/iroha-2-docs/reference/data-model-schema.html#filteropt-domainfilter","388":"/iroha-2-docs/reference/data-model-schema.html#filteropt-originfilter-accountevent","389":"/iroha-2-docs/reference/data-model-schema.html#filteropt-originfilter-assetdefinitionevent","390":"/iroha-2-docs/reference/data-model-schema.html#filteropt-originfilter-assetevent","391":"/iroha-2-docs/reference/data-model-schema.html#filteropt-originfilter-domainevent","392":"/iroha-2-docs/reference/data-model-schema.html#filteropt-originfilter-peerevent","393":"/iroha-2-docs/reference/data-model-schema.html#filteropt-originfilter-roleevent","394":"/iroha-2-docs/reference/data-model-schema.html#filteropt-originfilter-triggerevent","395":"/iroha-2-docs/reference/data-model-schema.html#filteropt-peereventfilter","396":"/iroha-2-docs/reference/data-model-schema.html#filteropt-peerfilter","397":"/iroha-2-docs/reference/data-model-schema.html#filteropt-roleeventfilter","398":"/iroha-2-docs/reference/data-model-schema.html#filteropt-rolefilter","399":"/iroha-2-docs/reference/data-model-schema.html#filteropt-triggereventfilter","400":"/iroha-2-docs/reference/data-model-schema.html#filteropt-triggerfilter","401":"/iroha-2-docs/reference/data-model-schema.html#findaccountbyid","402":"/iroha-2-docs/reference/data-model-schema.html#findaccountkeyvaluebyidandkey","403":"/iroha-2-docs/reference/data-model-schema.html#findaccountsbydomainid","404":"/iroha-2-docs/reference/data-model-schema.html#findaccountsbyname","405":"/iroha-2-docs/reference/data-model-schema.html#findaccountswithasset","406":"/iroha-2-docs/reference/data-model-schema.html#findallaccounts","407":"/iroha-2-docs/reference/data-model-schema.html#findallactivetriggerids","408":"/iroha-2-docs/reference/data-model-schema.html#findallassets","409":"/iroha-2-docs/reference/data-model-schema.html#findallassetsdefinitions","410":"/iroha-2-docs/reference/data-model-schema.html#findallblockheaders","411":"/iroha-2-docs/reference/data-model-schema.html#findallblocks","412":"/iroha-2-docs/reference/data-model-schema.html#findalldomains","413":"/iroha-2-docs/reference/data-model-schema.html#findallparameters","414":"/iroha-2-docs/reference/data-model-schema.html#findallpeers","415":"/iroha-2-docs/reference/data-model-schema.html#findallroleids","416":"/iroha-2-docs/reference/data-model-schema.html#findallroles","417":"/iroha-2-docs/reference/data-model-schema.html#findalltransactions","418":"/iroha-2-docs/reference/data-model-schema.html#findassetbyid","419":"/iroha-2-docs/reference/data-model-schema.html#findassetdefinitionbyid","420":"/iroha-2-docs/reference/data-model-schema.html#findassetdefinitionkeyvaluebyidandkey","421":"/iroha-2-docs/reference/data-model-schema.html#findassetkeyvaluebyidandkey","422":"/iroha-2-docs/reference/data-model-schema.html#findassetquantitybyid","423":"/iroha-2-docs/reference/data-model-schema.html#findassetsbyaccountid","424":"/iroha-2-docs/reference/data-model-schema.html#findassetsbyassetdefinitionid","425":"/iroha-2-docs/reference/data-model-schema.html#findassetsbydomainid","426":"/iroha-2-docs/reference/data-model-schema.html#findassetsbydomainidandassetdefinitionid","427":"/iroha-2-docs/reference/data-model-schema.html#findassetsbyname","428":"/iroha-2-docs/reference/data-model-schema.html#findblockheaderbyhash","429":"/iroha-2-docs/reference/data-model-schema.html#finddomainbyid","430":"/iroha-2-docs/reference/data-model-schema.html#finddomainkeyvaluebyidandkey","431":"/iroha-2-docs/reference/data-model-schema.html#finderror","432":"/iroha-2-docs/reference/data-model-schema.html#findpermissiontokenschema","433":"/iroha-2-docs/reference/data-model-schema.html#findpermissiontokensbyaccountid","434":"/iroha-2-docs/reference/data-model-schema.html#findrolebyroleid","435":"/iroha-2-docs/reference/data-model-schema.html#findrolesbyaccountid","436":"/iroha-2-docs/reference/data-model-schema.html#findtotalassetquantitybyassetdefinitionid","437":"/iroha-2-docs/reference/data-model-schema.html#findtransactionbyhash","438":"/iroha-2-docs/reference/data-model-schema.html#findtransactionsbyaccountid","439":"/iroha-2-docs/reference/data-model-schema.html#findtriggerbyid","440":"/iroha-2-docs/reference/data-model-schema.html#findtriggerkeyvaluebyidandkey","441":"/iroha-2-docs/reference/data-model-schema.html#findtriggersbydomainid","442":"/iroha-2-docs/reference/data-model-schema.html#fixed","443":"/iroha-2-docs/reference/data-model-schema.html#fixedpoint-i64","444":"/iroha-2-docs/reference/data-model-schema.html#forwardcursor","445":"/iroha-2-docs/reference/data-model-schema.html#genericpredicatebox-valuepredicate","446":"/iroha-2-docs/reference/data-model-schema.html#grantbox","447":"/iroha-2-docs/reference/data-model-schema.html#greater","448":"/iroha-2-docs/reference/data-model-schema.html#hash","449":"/iroha-2-docs/reference/data-model-schema.html#hashof-merkletree-versionedsignedtransaction","450":"/iroha-2-docs/reference/data-model-schema.html#hashof-versionedsignedblock","451":"/iroha-2-docs/reference/data-model-schema.html#hashof-versionedsignedtransaction","452":"/iroha-2-docs/reference/data-model-schema.html#hashof-wasmsmartcontract","453":"/iroha-2-docs/reference/data-model-schema.html#hashvalue","454":"/iroha-2-docs/reference/data-model-schema.html#idbox","455":"/iroha-2-docs/reference/data-model-schema.html#identifiablebox","456":"/iroha-2-docs/reference/data-model-schema.html#if","457":"/iroha-2-docs/reference/data-model-schema.html#instructionbox","458":"/iroha-2-docs/reference/data-model-schema.html#instructionevaluationerror","459":"/iroha-2-docs/reference/data-model-schema.html#instructionexecutionerror","460":"/iroha-2-docs/reference/data-model-schema.html#instructionexecutionfail","461":"/iroha-2-docs/reference/data-model-schema.html#instructiontype","462":"/iroha-2-docs/reference/data-model-schema.html#interval-u16","463":"/iroha-2-docs/reference/data-model-schema.html#interval-u8","464":"/iroha-2-docs/reference/data-model-schema.html#invalidparametererror","465":"/iroha-2-docs/reference/data-model-schema.html#ipfspath","466":"/iroha-2-docs/reference/data-model-schema.html#ipv4addr","467":"/iroha-2-docs/reference/data-model-schema.html#ipv4predicate","468":"/iroha-2-docs/reference/data-model-schema.html#ipv6addr","469":"/iroha-2-docs/reference/data-model-schema.html#ipv6predicate","470":"/iroha-2-docs/reference/data-model-schema.html#isassetdefinitionowner","471":"/iroha-2-docs/reference/data-model-schema.html#lengthlimits","472":"/iroha-2-docs/reference/data-model-schema.html#less","473":"/iroha-2-docs/reference/data-model-schema.html#level","474":"/iroha-2-docs/reference/data-model-schema.html#limits","475":"/iroha-2-docs/reference/data-model-schema.html#logbox","476":"/iroha-2-docs/reference/data-model-schema.html#matherror","477":"/iroha-2-docs/reference/data-model-schema.html#merkletree-versionedsignedtransaction","478":"/iroha-2-docs/reference/data-model-schema.html#metadata","479":"/iroha-2-docs/reference/data-model-schema.html#metadatachanged-accountid","480":"/iroha-2-docs/reference/data-model-schema.html#metadatachanged-assetdefinitionid","481":"/iroha-2-docs/reference/data-model-schema.html#metadatachanged-assetid","482":"/iroha-2-docs/reference/data-model-schema.html#metadatachanged-domainid","483":"/iroha-2-docs/reference/data-model-schema.html#metadataerror","484":"/iroha-2-docs/reference/data-model-schema.html#mintbox","485":"/iroha-2-docs/reference/data-model-schema.html#mintabilityerror","486":"/iroha-2-docs/reference/data-model-schema.html#mintable","487":"/iroha-2-docs/reference/data-model-schema.html#mismatch-assetdefinitionid","488":"/iroha-2-docs/reference/data-model-schema.html#mismatch-assetvaluetype","489":"/iroha-2-docs/reference/data-model-schema.html#mismatch-value","490":"/iroha-2-docs/reference/data-model-schema.html#mod","491":"/iroha-2-docs/reference/data-model-schema.html#multiply","492":"/iroha-2-docs/reference/data-model-schema.html#name","493":"/iroha-2-docs/reference/data-model-schema.html#newaccount","494":"/iroha-2-docs/reference/data-model-schema.html#newassetdefinition","495":"/iroha-2-docs/reference/data-model-schema.html#newdomain","496":"/iroha-2-docs/reference/data-model-schema.html#newparameterbox","497":"/iroha-2-docs/reference/data-model-schema.html#newrole","498":"/iroha-2-docs/reference/data-model-schema.html#nontrivial-genericpredicatebox-valuepredicate","499":"/iroha-2-docs/reference/data-model-schema.html#nonzero-u32","500":"/iroha-2-docs/reference/data-model-schema.html#nonzero-u64","501":"/iroha-2-docs/reference/data-model-schema.html#not","502":"/iroha-2-docs/reference/data-model-schema.html#notificationevent","503":"/iroha-2-docs/reference/data-model-schema.html#notificationeventfilter","504":"/iroha-2-docs/reference/data-model-schema.html#numericvalue","505":"/iroha-2-docs/reference/data-model-schema.html#optimizedexecutable","506":"/iroha-2-docs/reference/data-model-schema.html#option-domainid","507":"/iroha-2-docs/reference/data-model-schema.html#option-duration","508":"/iroha-2-docs/reference/data-model-schema.html#option-hash","509":"/iroha-2-docs/reference/data-model-schema.html#option-hashof-merkletree-versionedsignedtransaction","510":"/iroha-2-docs/reference/data-model-schema.html#option-hashof-versionedsignedblock","511":"/iroha-2-docs/reference/data-model-schema.html#option-instructionbox","512":"/iroha-2-docs/reference/data-model-schema.html#option-ipfspath","513":"/iroha-2-docs/reference/data-model-schema.html#option-nonzero-u32","514":"/iroha-2-docs/reference/data-model-schema.html#option-nonzero-u64","515":"/iroha-2-docs/reference/data-model-schema.html#option-pipelineentitykind","516":"/iroha-2-docs/reference/data-model-schema.html#option-pipelinestatuskind","517":"/iroha-2-docs/reference/data-model-schema.html#option-string","518":"/iroha-2-docs/reference/data-model-schema.html#option-timeinterval","519":"/iroha-2-docs/reference/data-model-schema.html#option-transactionrejectionreason","520":"/iroha-2-docs/reference/data-model-schema.html#option-triggercompletedoutcometype","521":"/iroha-2-docs/reference/data-model-schema.html#option-triggerid","522":"/iroha-2-docs/reference/data-model-schema.html#or","523":"/iroha-2-docs/reference/data-model-schema.html#originfilter-accountevent","524":"/iroha-2-docs/reference/data-model-schema.html#originfilter-assetdefinitionevent","525":"/iroha-2-docs/reference/data-model-schema.html#originfilter-assetevent","526":"/iroha-2-docs/reference/data-model-schema.html#originfilter-domainevent","527":"/iroha-2-docs/reference/data-model-schema.html#originfilter-peerevent","528":"/iroha-2-docs/reference/data-model-schema.html#originfilter-roleevent","529":"/iroha-2-docs/reference/data-model-schema.html#originfilter-triggerevent","530":"/iroha-2-docs/reference/data-model-schema.html#pair","531":"/iroha-2-docs/reference/data-model-schema.html#parameter","532":"/iroha-2-docs/reference/data-model-schema.html#parameterid","533":"/iroha-2-docs/reference/data-model-schema.html#peer","534":"/iroha-2-docs/reference/data-model-schema.html#peerevent","535":"/iroha-2-docs/reference/data-model-schema.html#peereventfilter","536":"/iroha-2-docs/reference/data-model-schema.html#peerfilter","537":"/iroha-2-docs/reference/data-model-schema.html#peerid","538":"/iroha-2-docs/reference/data-model-schema.html#permissionremoved","539":"/iroha-2-docs/reference/data-model-schema.html#permissiontoken","540":"/iroha-2-docs/reference/data-model-schema.html#permissiontokenschema","541":"/iroha-2-docs/reference/data-model-schema.html#permissiontokenschemaupdateevent","542":"/iroha-2-docs/reference/data-model-schema.html#pipelineentitykind","543":"/iroha-2-docs/reference/data-model-schema.html#pipelineevent","544":"/iroha-2-docs/reference/data-model-schema.html#pipelineeventfilter","545":"/iroha-2-docs/reference/data-model-schema.html#pipelinerejectionreason","546":"/iroha-2-docs/reference/data-model-schema.html#pipelinestatus","547":"/iroha-2-docs/reference/data-model-schema.html#pipelinestatuskind","548":"/iroha-2-docs/reference/data-model-schema.html#publickey","549":"/iroha-2-docs/reference/data-model-schema.html#querybox","550":"/iroha-2-docs/reference/data-model-schema.html#queryexecutionfail","551":"/iroha-2-docs/reference/data-model-schema.html#querypayload","552":"/iroha-2-docs/reference/data-model-schema.html#raiseto","553":"/iroha-2-docs/reference/data-model-schema.html#rawgenesisblock","554":"/iroha-2-docs/reference/data-model-schema.html#registerbox","555":"/iroha-2-docs/reference/data-model-schema.html#registrablebox","556":"/iroha-2-docs/reference/data-model-schema.html#removekeyvaluebox","557":"/iroha-2-docs/reference/data-model-schema.html#repeats","558":"/iroha-2-docs/reference/data-model-schema.html#repetitionerror","559":"/iroha-2-docs/reference/data-model-schema.html#revokebox","560":"/iroha-2-docs/reference/data-model-schema.html#role","561":"/iroha-2-docs/reference/data-model-schema.html#roleevent","562":"/iroha-2-docs/reference/data-model-schema.html#roleeventfilter","563":"/iroha-2-docs/reference/data-model-schema.html#rolefilter","564":"/iroha-2-docs/reference/data-model-schema.html#roleid","565":"/iroha-2-docs/reference/data-model-schema.html#schedule","566":"/iroha-2-docs/reference/data-model-schema.html#semiinterval-fixed","567":"/iroha-2-docs/reference/data-model-schema.html#semiinterval-u128","568":"/iroha-2-docs/reference/data-model-schema.html#semiinterval-u32","569":"/iroha-2-docs/reference/data-model-schema.html#semirange","570":"/iroha-2-docs/reference/data-model-schema.html#sequencebox","571":"/iroha-2-docs/reference/data-model-schema.html#setkeyvaluebox","572":"/iroha-2-docs/reference/data-model-schema.html#setparameterbox","573":"/iroha-2-docs/reference/data-model-schema.html#signature","574":"/iroha-2-docs/reference/data-model-schema.html#signaturecheckcondition","575":"/iroha-2-docs/reference/data-model-schema.html#signatureof-blockpayload","576":"/iroha-2-docs/reference/data-model-schema.html#signatureof-querypayload","577":"/iroha-2-docs/reference/data-model-schema.html#signatureof-transactionpayload","578":"/iroha-2-docs/reference/data-model-schema.html#signaturesof-blockpayload","579":"/iroha-2-docs/reference/data-model-schema.html#signaturesof-transactionpayload","580":"/iroha-2-docs/reference/data-model-schema.html#signedblock","581":"/iroha-2-docs/reference/data-model-schema.html#signedquery","582":"/iroha-2-docs/reference/data-model-schema.html#signedtransaction","583":"/iroha-2-docs/reference/data-model-schema.html#sizeerror","584":"/iroha-2-docs/reference/data-model-schema.html#socketaddr","585":"/iroha-2-docs/reference/data-model-schema.html#socketaddrhost","586":"/iroha-2-docs/reference/data-model-schema.html#socketaddrv4","587":"/iroha-2-docs/reference/data-model-schema.html#socketaddrv6","588":"/iroha-2-docs/reference/data-model-schema.html#sortedmap-accountid-account","589":"/iroha-2-docs/reference/data-model-schema.html#sortedmap-assetdefinitionid-assetdefinition","590":"/iroha-2-docs/reference/data-model-schema.html#sortedmap-assetdefinitionid-numericvalue","591":"/iroha-2-docs/reference/data-model-schema.html#sortedmap-assetid-asset","592":"/iroha-2-docs/reference/data-model-schema.html#sortedmap-name-evaluatesto-value","593":"/iroha-2-docs/reference/data-model-schema.html#sortedmap-name-value","594":"/iroha-2-docs/reference/data-model-schema.html#sortedvec-permissiontoken","595":"/iroha-2-docs/reference/data-model-schema.html#sortedvec-publickey","596":"/iroha-2-docs/reference/data-model-schema.html#sortedvec-roleid","597":"/iroha-2-docs/reference/data-model-schema.html#sortedvec-signatureof-blockpayload","598":"/iroha-2-docs/reference/data-model-schema.html#sortedvec-signatureof-transactionpayload","599":"/iroha-2-docs/reference/data-model-schema.html#string","600":"/iroha-2-docs/reference/data-model-schema.html#stringpredicate","601":"/iroha-2-docs/reference/data-model-schema.html#stringwithjson","602":"/iroha-2-docs/reference/data-model-schema.html#subtract","603":"/iroha-2-docs/reference/data-model-schema.html#timeevent","604":"/iroha-2-docs/reference/data-model-schema.html#timeeventfilter","605":"/iroha-2-docs/reference/data-model-schema.html#timeinterval","606":"/iroha-2-docs/reference/data-model-schema.html#transactionlimiterror","607":"/iroha-2-docs/reference/data-model-schema.html#transactionlimits","608":"/iroha-2-docs/reference/data-model-schema.html#transactionpayload","609":"/iroha-2-docs/reference/data-model-schema.html#transactionqueryoutput","610":"/iroha-2-docs/reference/data-model-schema.html#transactionrejectionreason","611":"/iroha-2-docs/reference/data-model-schema.html#transactionvalue","612":"/iroha-2-docs/reference/data-model-schema.html#transferbox","613":"/iroha-2-docs/reference/data-model-schema.html#trigger-triggeringfilterbox-executable","614":"/iroha-2-docs/reference/data-model-schema.html#trigger-triggeringfilterbox-optimizedexecutable","615":"/iroha-2-docs/reference/data-model-schema.html#triggerbox","616":"/iroha-2-docs/reference/data-model-schema.html#triggercompletedevent","617":"/iroha-2-docs/reference/data-model-schema.html#triggercompletedeventfilter","618":"/iroha-2-docs/reference/data-model-schema.html#triggercompletedoutcome","619":"/iroha-2-docs/reference/data-model-schema.html#triggercompletedoutcometype","620":"/iroha-2-docs/reference/data-model-schema.html#triggerevent","621":"/iroha-2-docs/reference/data-model-schema.html#triggereventfilter","622":"/iroha-2-docs/reference/data-model-schema.html#triggerfilter","623":"/iroha-2-docs/reference/data-model-schema.html#triggerid","624":"/iroha-2-docs/reference/data-model-schema.html#triggernumberofexecutionschanged","625":"/iroha-2-docs/reference/data-model-schema.html#triggeringfilterbox","626":"/iroha-2-docs/reference/data-model-schema.html#typeerror","627":"/iroha-2-docs/reference/data-model-schema.html#unregisterbox","628":"/iroha-2-docs/reference/data-model-schema.html#upgradablebox","629":"/iroha-2-docs/reference/data-model-schema.html#upgradebox","630":"/iroha-2-docs/reference/data-model-schema.html#validationfail","631":"/iroha-2-docs/reference/data-model-schema.html#validator","632":"/iroha-2-docs/reference/data-model-schema.html#validatorevent","633":"/iroha-2-docs/reference/data-model-schema.html#validatormode","634":"/iroha-2-docs/reference/data-model-schema.html#value","635":"/iroha-2-docs/reference/data-model-schema.html#valueofkey","636":"/iroha-2-docs/reference/data-model-schema.html#valuepredicate","637":"/iroha-2-docs/reference/data-model-schema.html#vec-event","638":"/iroha-2-docs/reference/data-model-schema.html#vec-genericpredicatebox-valuepredicate","639":"/iroha-2-docs/reference/data-model-schema.html#vec-instructionbox","640":"/iroha-2-docs/reference/data-model-schema.html#vec-name","641":"/iroha-2-docs/reference/data-model-schema.html#vec-peerid","642":"/iroha-2-docs/reference/data-model-schema.html#vec-publickey","643":"/iroha-2-docs/reference/data-model-schema.html#vec-transactionvalue","644":"/iroha-2-docs/reference/data-model-schema.html#vec-value","645":"/iroha-2-docs/reference/data-model-schema.html#vec-vec-instructionbox","646":"/iroha-2-docs/reference/data-model-schema.html#vec-versionedsignedtransaction","647":"/iroha-2-docs/reference/data-model-schema.html#vec-u8","648":"/iroha-2-docs/reference/data-model-schema.html#versionedbatchedresponse-value","649":"/iroha-2-docs/reference/data-model-schema.html#versionedbatchedresponse-vec-versionedsignedtransaction","650":"/iroha-2-docs/reference/data-model-schema.html#versionedsignedblock","651":"/iroha-2-docs/reference/data-model-schema.html#versionedsignedquery","652":"/iroha-2-docs/reference/data-model-schema.html#versionedsignedtransaction","653":"/iroha-2-docs/reference/data-model-schema.html#wasmexecutionfail","654":"/iroha-2-docs/reference/data-model-schema.html#wasminternalrepr","655":"/iroha-2-docs/reference/data-model-schema.html#wasmsmartcontract","656":"/iroha-2-docs/reference/data-model-schema.html#where","657":"/iroha-2-docs/reference/data-model-schema.html#bool","658":"/iroha-2-docs/reference/data-model-schema.html#i64","659":"/iroha-2-docs/reference/data-model-schema.html#u128","660":"/iroha-2-docs/reference/data-model-schema.html#u16","661":"/iroha-2-docs/reference/data-model-schema.html#u32","662":"/iroha-2-docs/reference/data-model-schema.html#u64","663":"/iroha-2-docs/reference/data-model-schema.html#u8","664":"/iroha-2-docs/reference/ffi.html#foreign-function-interfaces-ffi","665":"/iroha-2-docs/reference/ffi.html#why-ffi","666":"/iroha-2-docs/reference/ffi.html#example","667":"/iroha-2-docs/reference/ffi.html#ffi-binding-generation","668":"/iroha-2-docs/reference/ffi.html#name-mangling","669":"/iroha-2-docs/reference/glossary.html#glossary","670":"/iroha-2-docs/reference/glossary.html#blockchain-ledgers","671":"/iroha-2-docs/reference/glossary.html#peer","672":"/iroha-2-docs/reference/glossary.html#asset","673":"/iroha-2-docs/reference/glossary.html#fungible-assets","674":"/iroha-2-docs/reference/glossary.html#non-fungible-assets","675":"/iroha-2-docs/reference/glossary.html#mintable-assets","676":"/iroha-2-docs/reference/glossary.html#non-mintable-assets","677":"/iroha-2-docs/reference/glossary.html#byzantine-fault-tolerance-bft","678":"/iroha-2-docs/reference/glossary.html#iroha-components","679":"/iroha-2-docs/reference/glossary.html#sumeragi-emperor","680":"/iroha-2-docs/reference/glossary.html#torii-gate","681":"/iroha-2-docs/reference/glossary.html#kura-warehouse","682":"/iroha-2-docs/reference/glossary.html#kagami-teacher-and-exemplar-and-or-looking-glass","683":"/iroha-2-docs/reference/glossary.html#merkle-tree-hash-tree","684":"/iroha-2-docs/reference/glossary.html#smart-contracts","685":"/iroha-2-docs/reference/glossary.html#triggers","686":"/iroha-2-docs/reference/glossary.html#versioning","687":"/iroha-2-docs/reference/glossary.html#hijiri-peer-reputation-system","688":"/iroha-2-docs/reference/glossary.html#iroha-modules","689":"/iroha-2-docs/reference/glossary.html#iroha-special-instructions-isi","690":"/iroha-2-docs/reference/glossary.html#utility-iroha-special-instructions","691":"/iroha-2-docs/reference/glossary.html#core-iroha-special-instructions","692":"/iroha-2-docs/reference/glossary.html#domain-specific-iroha-special-instructions","693":"/iroha-2-docs/reference/glossary.html#custom-iroha-special-instruction","694":"/iroha-2-docs/reference/glossary.html#iroha-query","695":"/iroha-2-docs/reference/glossary.html#view-change","696":"/iroha-2-docs/reference/glossary.html#world-state-view-wsv","697":"/iroha-2-docs/reference/glossary.html#leader","698":"/iroha-2-docs/reference/instructions.html#iroha-special-instructions","699":"/iroha-2-docs/reference/naming.html#naming-conventions","700":"/iroha-2-docs/reference/permissions.html#permissions","701":"/iroha-2-docs/reference/permissions.html#permission-tokens","702":"/iroha-2-docs/reference/permissions.html#canmintuserassetdefinitions","703":"/iroha-2-docs/reference/permissions.html#canburnassetwithdefinition","704":"/iroha-2-docs/reference/permissions.html#canburnuserassets","705":"/iroha-2-docs/reference/permissions.html#canunregisterassetwithdefinition","706":"/iroha-2-docs/reference/permissions.html#cantransferuserassets","707":"/iroha-2-docs/reference/permissions.html#cantransferonlyfixednumberoftimesperperiod","708":"/iroha-2-docs/reference/permissions.html#cansetkeyvalueinuserassets","709":"/iroha-2-docs/reference/permissions.html#canremovekeyvalueinuserassets","710":"/iroha-2-docs/reference/permissions.html#cansetkeyvalueinusermetadata","711":"/iroha-2-docs/reference/permissions.html#canremovekeyvalueinusermetadata","712":"/iroha-2-docs/reference/permissions.html#cansetkeyvalueinassetdefinition","713":"/iroha-2-docs/reference/permissions.html#canremovekeyvalueinassetdefinition","714":"/iroha-2-docs/reference/permissions.html#canregisterdomains","715":"/iroha-2-docs/reference/queries.html#queries","716":"/iroha-2-docs/reference/queries.html#conventions","717":"/iroha-2-docs/reference/queries.html#role","718":"/iroha-2-docs/reference/queries.html#findallroles","719":"/iroha-2-docs/reference/queries.html#findallroleids","720":"/iroha-2-docs/reference/queries.html#findrolebyroleid","721":"/iroha-2-docs/reference/queries.html#findrolesbyaccountid","722":"/iroha-2-docs/reference/queries.html#permission","723":"/iroha-2-docs/reference/queries.html#findallpermissiontokendefinitions","724":"/iroha-2-docs/reference/queries.html#findpermissiontokensbyaccountid","725":"/iroha-2-docs/reference/queries.html#account","726":"/iroha-2-docs/reference/queries.html#findallaccounts","727":"/iroha-2-docs/reference/queries.html#findaccountbyid","728":"/iroha-2-docs/reference/queries.html#findaccountkeyvaluebyidandkey","729":"/iroha-2-docs/reference/queries.html#findaccountsbyname","730":"/iroha-2-docs/reference/queries.html#findaccountsbydomainid","731":"/iroha-2-docs/reference/queries.html#findaccountswithasset","732":"/iroha-2-docs/reference/queries.html#asset","733":"/iroha-2-docs/reference/queries.html#findallassets","734":"/iroha-2-docs/reference/queries.html#findallassetdefinitions","735":"/iroha-2-docs/reference/queries.html#findassetbyid","736":"/iroha-2-docs/reference/queries.html#findassetsbyname","737":"/iroha-2-docs/reference/queries.html#findassetsbyaccountid","738":"/iroha-2-docs/reference/queries.html#findassetsbyassetdefinitionid","739":"/iroha-2-docs/reference/queries.html#findassetsbydomainid","740":"/iroha-2-docs/reference/queries.html#findassetsbydomainidandassetdefinitionid","741":"/iroha-2-docs/reference/queries.html#findassetquantitybyid","742":"/iroha-2-docs/reference/queries.html#findassetkeyvaluebyidandkey","743":"/iroha-2-docs/reference/queries.html#findassetdefinitionkeyvaluebyidandkey","744":"/iroha-2-docs/reference/queries.html#findtotalassetquantitybyassetdefinitionid","745":"/iroha-2-docs/reference/queries.html#block","746":"/iroha-2-docs/reference/queries.html#findallblocks","747":"/iroha-2-docs/reference/queries.html#findallblockheaders","748":"/iroha-2-docs/reference/queries.html#findblockheaderbyhash","749":"/iroha-2-docs/reference/queries.html#domain","750":"/iroha-2-docs/reference/queries.html#findalldomains","751":"/iroha-2-docs/reference/queries.html#finddomainbyid","752":"/iroha-2-docs/reference/queries.html#finddomainkeyvaluebyidandkey","753":"/iroha-2-docs/reference/queries.html#peer","754":"/iroha-2-docs/reference/queries.html#findallpeers","755":"/iroha-2-docs/reference/queries.html#findallparameters","756":"/iroha-2-docs/reference/queries.html#transaction","757":"/iroha-2-docs/reference/queries.html#findtransactionsbyaccountid","758":"/iroha-2-docs/reference/queries.html#findtransactionbyhash","759":"/iroha-2-docs/reference/queries.html#trigger","760":"/iroha-2-docs/reference/queries.html#findallactivetriggerids","761":"/iroha-2-docs/reference/queries.html#findtriggerbyid","762":"/iroha-2-docs/reference/queries.html#findtriggerkeyvaluebyidandkey","763":"/iroha-2-docs/reference/queries.html#findtriggersbydomainid","764":"/iroha-2-docs/reference/specification.html#api-specification","765":"/iroha-2-docs/reference/torii-endpoints.html#torii-endpoints","766":"/iroha-2-docs/reference/torii-endpoints.html#api-version","767":"/iroha-2-docs/reference/torii-endpoints.html#blocks-stream","768":"/iroha-2-docs/reference/torii-endpoints.html#events","769":"/iroha-2-docs/reference/torii-endpoints.html#transaction-events","770":"/iroha-2-docs/reference/torii-endpoints.html#health","771":"/iroha-2-docs/reference/torii-endpoints.html#metrics","772":"/iroha-2-docs/reference/torii-endpoints.html#query","773":"/iroha-2-docs/reference/torii-endpoints.html#account-not-found-404","774":"/iroha-2-docs/reference/torii-endpoints.html#asset-not-found-404","775":"/iroha-2-docs/reference/torii-endpoints.html#status","776":"/iroha-2-docs/reference/torii-endpoints.html#sub-routing","777":"/iroha-2-docs/reference/torii-endpoints.html#transaction"},"fieldIds":{"title":0,"titles":1,"text":2},"fieldLength":{"0":[2,1,34],"1":[3,2,1],"2":[2,5,58],"3":[2,5,59],"4":[4,5,49],"5":[1,2,141],"6":[9,1,156],"7":[7,9,26],"8":[4,9,30],"9":[4,9,36],"10":[1,1,38],"11":[4,1,87],"12":[3,1,6],"13":[4,1,92],"14":[1,4,86],"15":[1,4,1],"16":[3,5,205],"17":[2,5,74],"18":[7,4,227],"19":[5,4,31],"20":[3,9,114],"21":[2,9,133],"22":[3,4,93],"23":[1,1,2],"24":[1,1,53],"25":[2,1,71],"26":[2,1,1],"27":[1,1,35],"28":[1,1,161],"29":[2,1,83],"30":[1,1,2],"31":[1,1,31],"32":[2,1,50],"33":[2,1,37],"34":[2,1,16],"35":[3,1,12],"36":[3,1,89],"37":[1,1,22],"38":[2,1,46],"39":[3,1,140],"40":[3,1,112],"41":[1,3,135],"42":[3,3,164],"43":[2,3,82],"44":[1,3,32],"45":[2,3,39],"46":[2,3,37],"47":[2,3,17],"48":[1,3,8],"49":[2,3,28],"50":[1,1,74],"51":[1,1,41],"52":[2,1,80],"53":[3,1,107],"54":[1,1,18],"55":[1,1,27],"56":[1,1,80],"57":[2,1,94],"58":[4,3,14],"59":[4,1,31],"60":[4,5,42],"61":[3,5,29],"62":[2,1,119],"63":[2,3,70],"64":[2,1,17],"65":[1,1,144],"66":[3,1,39],"67":[1,1,70],"68":[1,1,17],"69":[1,1,64],"70":[1,1,12],"71":[1,1,61],"72":[1,1,90],"73":[5,1,15],"74":[2,6,37],"75":[2,6,25],"76":[2,7,16],"77":[2,7,17],"78":[3,7,59],"79":[2,7,55],"80":[2,7,31],"81":[3,1,55],"82":[1,3,61],"83":[3,4,43],"84":[2,3,60],"85":[3,1,49],"86":[2,3,42],"87":[2,3,62],"88":[2,4,200],"89":[3,4,87],"90":[3,3,65],"91":[4,1,34],"92":[3,4,72],"93":[4,4,60],"94":[5,4,156],"95":[5,4,32],"96":[4,4,27],"97":[3,4,253],"98":[2,1,75],"99":[2,1,62],"100":[1,1,103],"101":[3,1,124],"102":[5,1,22],"103":[5,6,155],"104":[5,6,141],"105":[6,1,96],"106":[3,7,90],"107":[6,7,51],"108":[3,7,64],"109":[6,7,42],"110":[1,7,72],"111":[1,1,41],"112":[5,1,28],"113":[2,1,65],"114":[1,2,16],"115":[4,2,37],"116":[2,2,56],"117":[3,2,52],"118":[3,2,25],"119":[3,5,40],"120":[3,5,63],"121":[2,2,32],"122":[4,2,33],"123":[2,2,21],"124":[2,1,49],"125":[4,2,114],"126":[4,2,90],"127":[4,2,96],"128":[2,1,211],"129":[1,2,28],"130":[4,3,42],"131":[1,2,27],"132":[4,1,48],"133":[6,4,1],"134":[5,8,101],"135":[5,8,122],"136":[6,4,70],"137":[7,1,301],"138":[4,1,57],"139":[1,4,41],"140":[1,4,24],"141":[2,4,162],"142":[3,1,1],"143":[2,1,206],"144":[1,2,16],"145":[4,2,36],"146":[2,2,129],"147":[3,2,31],"148":[2,5,46],"149":[2,5,24],"150":[2,5,71],"151":[1,2,94],"152":[1,2,15],"153":[3,3,58],"154":[3,3,98],"155":[1,2,44],"156":[2,1,22],"157":[2,2,26],"158":[2,2,31],"159":[2,4,25],"160":[5,6,83],"161":[5,6,71],"162":[4,6,35],"163":[2,4,57],"164":[3,1,246],"165":[2,1,1],"166":[7,2,237],"167":[5,2,93],"168":[3,2,231],"169":[4,2,67],"170":[4,2,291],"171":[5,2,122],"172":[3,2,33],"173":[3,2,26],"174":[3,2,142],"175":[4,1,21],"176":[4,4,91],"177":[3,4,84],"178":[2,1,133],"179":[3,1,18],"180":[2,3,84],"181":[2,3,10],"182":[2,4,40],"183":[4,3,99],"184":[3,3,8],"185":[3,1,31],"186":[3,3,273],"187":[3,3,332],"188":[4,3,197],"189":[4,3,171],"190":[5,3,188],"191":[3,3,69],"192":[7,3,77],"193":[6,3,359],"194":[1,9,12],"195":[5,3,92],"196":[3,1,1],"197":[5,3,172],"198":[3,3,139],"199":[5,3,219],"200":[4,3,171],"201":[5,3,174],"202":[3,3,160],"203":[3,3,115],"204":[3,3,112],"205":[5,3,136],"206":[3,1,31],"207":[5,3,117],"208":[3,3,102],"209":[4,3,101],"210":[4,3,130],"211":[5,3,203],"212":[3,3,142],"213":[4,1,143],"214":[2,4,56],"215":[2,1,1],"216":[5,2,240],"217":[3,2,178],"218":[4,2,209],"219":[4,2,170],"220":[5,2,254],"221":[3,2,41],"222":[3,2,156],"223":[1,1,27],"224":[1,1,80],"225":[1,1,54],"226":[2,1,48],"227":[2,1,49],"228":[2,2,32],"229":[4,1,89],"230":[2,4,117],"231":[3,4,95],"232":[1,4,77],"233":[2,4,88],"234":[4,4,74],"235":[1,4,41],"236":[6,1,47],"237":[3,1,52],"238":[5,3,235],"239":[1,5,54],"240":[4,3,1],"241":[3,7,88],"242":[8,7,44],"243":[8,7,77],"244":[11,15,81],"245":[1,1,58],"246":[1,2,110],"247":[2,1,150],"248":[3,2,183],"249":[2,2,311],"250":[2,2,158],"251":[2,1,30],"252":[2,2,246],"253":[2,2,248],"254":[3,1,65],"255":[3,3,75],"256":[5,3,173],"257":[2,1,24],"258":[3,2,97],"259":[5,2,153],"260":[4,2,196],"261":[3,1,60],"262":[4,3,61],"263":[4,4,154],"264":[5,4,108],"265":[2,9,103],"266":[2,11,73],"267":[4,3,38],"268":[4,4,166],"269":[4,4,34],"270":[2,1,74],"271":[3,1,38],"272":[7,3,167],"273":[7,3,165],"274":[3,1,26],"275":[1,3,2],"276":[1,3,2],"277":[3,1,26],"278":[3,3,39],"279":[3,5,57],"280":[3,5,62],"281":[5,5,36],"282":[7,5,141],"283":[3,3,92],"284":[3,1,27],"285":[1,1,46],"286":[3,1,73],"287":[2,1,88],"288":[3,1,7],"289":[1,3,26],"290":[1,3,38],"291":[1,3,34],"292":[1,3,16],"293":[1,3,11],"294":[1,3,12],"295":[1,3,12],"296":[6,3,17],"297":[6,3,17],"298":[1,3,13],"299":[1,3,16],"300":[1,3,13],"301":[7,3,10],"302":[7,3,10],"303":[6,3,7],"304":[6,3,7],"305":[6,3,7],"306":[1,3,10],"307":[1,3,12],"308":[1,3,23],"309":[1,3,29],"310":[1,3,22],"311":[1,3,16],"312":[1,3,11],"313":[1,3,14],"314":[1,3,14],"315":[1,3,26],"316":[1,3,20],"317":[1,3,16],"318":[1,3,12],"319":[1,3,19],"320":[1,3,16],"321":[1,3,11],"322":[5,3,11],"323":[6,3,14],"324":[1,3,10],"325":[1,3,31],"326":[1,3,5],"327":[1,3,17],"328":[1,3,10],"329":[1,3,8],"330":[1,3,15],"331":[1,3,16],"332":[1,3,15],"333":[1,3,20],"334":[1,3,14],"335":[1,3,14],"336":[1,3,14],"337":[1,3,8],"338":[1,3,32],"339":[1,3,38],"340":[1,3,13],"341":[1,3,27],"342":[1,3,27],"343":[1,3,25],"344":[1,3,16],"345":[1,3,8],"346":[1,3,6],"347":[1,3,13],"348":[5,3,9],"349":[5,3,9],"350":[5,3,9],"351":[5,3,9],"352":[6,3,9],"353":[6,3,9],"354":[5,3,9],"355":[5,3,9],"356":[5,3,9],"357":[5,3,9],"358":[5,3,9],"359":[5,3,9],"360":[5,3,9],"361":[5,3,9],"362":[5,3,9],"363":[5,3,9],"364":[5,3,9],"365":[6,3,9],"366":[5,3,9],"367":[1,3,19],"368":[1,3,23],"369":[1,3,5],"370":[1,3,5],"371":[1,3,17],"372":[1,3,13],"373":[1,3,12],"374":[1,3,12],"375":[1,3,12],"376":[1,3,50],"377":[1,3,9],"378":[1,3,26],"379":[5,3,13],"380":[5,3,13],"381":[5,3,13],"382":[5,3,13],"383":[5,3,13],"384":[5,3,13],"385":[5,3,13],"386":[5,3,13],"387":[5,3,13],"388":[6,3,16],"389":[6,3,16],"390":[6,3,16],"391":[6,3,16],"392":[6,3,16],"393":[6,3,16],"394":[6,3,16],"395":[5,3,13],"396":[5,3,13],"397":[5,3,13],"398":[5,3,13],"399":[5,3,13],"400":[5,3,13],"401":[1,3,12],"402":[1,3,14],"403":[1,3,13],"404":[1,3,11],"405":[1,3,14],"406":[1,3,7],"407":[1,3,7],"408":[1,3,7],"409":[1,3,7],"410":[1,3,7],"411":[1,3,7],"412":[1,3,7],"413":[1,3,7],"414":[1,3,7],"415":[1,3,7],"416":[1,3,7],"417":[1,3,7],"418":[1,3,12],"419":[1,3,12],"420":[1,3,14],"421":[1,3,14],"422":[1,3,12],"423":[1,3,13],"424":[1,3,14],"425":[1,3,13],"426":[1,3,16],"427":[1,3,11],"428":[1,3,13],"429":[1,3,12],"430":[1,3,14],"431":[1,3,48],"432":[1,3,7],"433":[1,3,12],"434":[1,3,12],"435":[1,3,12],"436":[1,3,12],"437":[1,3,13],"438":[1,3,13],"439":[1,3,12],"440":[1,3,14],"441":[1,3,13],"442":[1,3,8],"443":[5,3,9],"444":[1,3,16],"445":[5,3,21],"446":[1,3,15],"447":[1,3,13],"448":[1,3,9],"449":[6,3,5],"450":[5,3,5],"451":[5,3,5],"452":[5,3,5],"453":[1,3,17],"454":[1,3,27],"455":[1,3,33],"456":[1,3,15],"457":[1,3,61],"458":[1,3,19],"459":[1,3,39],"460":[1,3,11],"461":[1,3,44],"462":[5,3,10],"463":[5,3,10],"464":[1,3,13],"465":[1,3,5],"466":[1,3,9],"467":[1,3,10],"468":[1,3,9],"469":[1,3,10],"470":[1,3,16],"471":[1,3,10],"472":[1,3,13],"473":[1,3,18],"474":[1,3,13],"475":[1,3,14],"476":[1,3,26],"477":[5,3,8],"478":[1,3,13],"479":[5,3,13],"480":[5,3,13],"481":[5,3,13],"482":[5,3,13],"483":[1,3,20],"484":[1,3,15],"485":[1,3,12],"486":[1,3,14],"487":[5,3,10],"488":[5,3,10],"489":[5,3,10],"490":[1,3,13],"491":[1,3,13],"492":[1,3,5],"493":[1,3,16],"494":[1,3,20],"495":[1,3,16],"496":[1,3,12],"497":[1,3,9],"498":[6,3,9],"499":[5,3,5],"500":[5,3,5],"501":[1,3,12],"502":[1,3,11],"503":[1,3,13],"504":[1,3,19],"505":[1,3,16],"506":[5,3,5],"507":[5,3,5],"508":[5,3,5],"509":[7,3,9],"510":[6,3,8],"511":[5,3,5],"512":[5,3,5],"513":[6,3,8],"514":[6,3,8],"515":[5,3,5],"516":[5,3,5],"517":[5,3,5],"518":[5,3,5],"519":[5,3,5],"520":[5,3,5],"521":[5,3,5],"522":[1,3,13],"523":[5,3,5],"524":[5,3,5],"525":[5,3,5],"526":[5,3,5],"527":[5,3,5],"528":[5,3,5],"529":[5,3,5],"530":[1,3,11],"531":[1,3,11],"532":[1,3,8],"533":[1,3,9],"534":[1,3,13],"535":[1,3,12],"536":[1,3,16],"537":[1,3,12],"538":[1,3,13],"539":[1,3,12],"540":[1,3,15],"541":[1,3,11],"542":[1,3,12],"543":[1,3,14],"544":[1,3,17],"545":[1,3,14],"546":[1,3,15],"547":[1,3,14],"548":[1,3,15],"549":[1,3,90],"550":[1,3,20],"551":[1,3,16],"552":[1,3,13],"553":[1,3,14],"554":[1,3,12],"555":[1,3,30],"556":[1,3,15],"557":[1,3,13],"558":[1,3,12],"559":[1,3,15],"560":[1,3,14],"561":[1,3,16],"562":[1,3,14],"563":[1,3,16],"564":[1,3,8],"565":[1,3,13],"566":[5,3,10],"567":[5,3,10],"568":[5,3,10],"569":[1,3,19],"570":[1,3,12],"571":[1,3,16],"572":[1,3,12],"573":[1,3,15],"574":[1,3,16],"575":[5,3,5],"576":[5,3,5],"577":[5,3,5],"578":[5,3,13],"579":[5,3,13],"580":[1,3,13],"581":[1,3,13],"582":[1,3,13],"583":[1,3,11],"584":[1,3,17],"585":[1,3,11],"586":[1,3,11],"587":[1,3,11],"588":[6,3,7],"589":[6,3,7],"590":[6,3,7],"591":[6,3,7],"592":[7,3,9],"593":[6,3,6],"594":[5,3,5],"595":[5,3,5],"596":[5,3,5],"597":[6,3,8],"598":[6,3,8],"599":[1,3,5],"600":[1,3,17],"601":[1,3,5],"602":[1,3,13],"603":[1,3,13],"604":[1,3,5],"605":[1,3,10],"606":[1,3,9],"607":[1,3,14],"608":[1,3,27],"609":[1,3,15],"610":[1,3,25],"611":[1,3,13],"612":[1,3,16],"613":[6,3,15],"614":[6,3,15],"615":[1,3,18],"616":[1,3,12],"617":[1,3,16],"618":[1,3,13],"619":[1,3,12],"620":[1,3,18],"621":[1,3,16],"622":[1,3,16],"623":[1,3,14],"624":[1,3,12],"625":[1,3,23],"626":[1,3,18],"627":[1,3,13],"628":[1,3,10],"629":[1,3,12],"630":[1,3,21],"631":[1,3,9],"632":[1,3,10],"633":[1,3,14],"634":[1,3,66],"635":[1,3,11],"636":[1,3,32],"637":[5,3,5],"638":[6,3,8],"639":[5,3,5],"640":[5,3,5],"641":[5,3,5],"642":[5,3,5],"643":[5,3,5],"644":[5,3,4],"645":[5,3,7],"646":[5,3,5],"647":[5,3,5],"648":[5,3,14],"649":[6,3,15],"650":[1,3,11],"651":[1,3,11],"652":[1,3,11],"653":[1,3,9],"654":[1,3,16],"655":[1,3,8],"656":[1,3,15],"657":[1,3,5],"658":[1,3,5],"659":[1,3,5],"660":[1,3,5],"661":[1,3,5],"662":[1,3,5],"663":[1,3,5],"664":[5,1,39],"665":[2,5,163],"666":[1,5,47],"667":[3,5,141],"668":[2,6,60],"669":[1,1,60],"670":[2,1,53],"671":[1,1,66],"672":[1,1,20],"673":[2,2,41],"674":[3,2,54],"675":[2,2,14],"676":[3,2,28],"677":[5,1,26],"678":[2,1,6],"679":[3,3,7],"680":[3,3,27],"681":[3,3,15],"682":[8,3,16],"683":[4,3,28],"684":[2,3,25],"685":[1,3,24],"686":[1,3,30],"687":[5,3,26],"688":[2,1,10],"689":[5,1,24],"690":[4,10,23],"691":[4,6,16],"692":[5,6,28],"693":[4,6,47],"694":[2,1,17],"695":[2,1,21],"696":[5,1,24],"697":[1,1,33],"698":[3,1,64],"699":[2,1,57],"700":[1,1,27],"701":[2,1,57],"702":[1,3,53],"703":[1,3,53],"704":[1,3,46],"705":[1,3,48],"706":[1,3,45],"707":[1,3,39],"708":[1,3,48],"709":[1,3,49],"710":[1,3,43],"711":[1,3,43],"712":[1,3,49],"713":[1,3,49],"714":[1,3,41],"715":[1,1,33],"716":[1,1,133],"717":[1,1,32],"718":[1,2,19],"719":[1,2,34],"720":[1,2,28],"721":[1,2,29],"722":[1,1,54],"723":[1,2,13],"724":[1,2,18],"725":[1,1,17],"726":[1,2,15],"727":[1,2,13],"728":[1,2,22],"729":[1,2,37],"730":[1,2,26],"731":[1,2,16],"732":[1,1,25],"733":[1,2,50],"734":[1,2,48],"735":[1,2,14],"736":[1,2,19],"737":[1,2,19],"738":[1,2,20],"739":[1,2,18],"740":[1,2,20],"741":[1,2,26],"742":[1,2,21],"743":[1,2,22],"744":[1,2,27],"745":[1,1,1],"746":[1,2,12],"747":[1,2,15],"748":[1,2,15],"749":[1,1,32],"750":[1,2,33],"751":[1,2,13],"752":[1,2,18],"753":[1,1,34],"754":[1,2,22],"755":[1,2,59],"756":[1,1,22],"757":[1,2,36],"758":[1,2,10],"759":[1,1,32],"760":[1,2,22],"761":[1,2,12],"762":[1,2,20],"763":[1,2,17],"764":[2,1,38],"765":[2,1,1],"766":[2,2,46],"767":[2,2,61],"768":[1,2,25],"769":[2,3,65],"770":[1,2,23],"771":[1,2,90],"772":[1,2,85],"773":[4,3,15],"774":[4,3,20],"775":[1,2,145],"776":[2,3,32],"777":[1,2,50]},"averageFieldLength":[2.548843187660673,2.852185089974292,42.35989717223655],"storedFields":{"0":{"title":"Code Snippets","titles":[]},"1":{"title":"How it works","titles":["Code Snippets"]},"2":{"title":"Snippet Sources","titles":["Code Snippets","How it works"]},"3":{"title":"Fetching Snippets","titles":["Code Snippets","How it works"]},"4":{"title":"Using Snippets in Markdown","titles":["Code Snippets","How it works"]},"5":{"title":"Example","titles":["Code Snippets"]},"6":{"title":"How to hot reload Iroha in a Docker container","titles":[]},"7":{"title":"Wiping previous blockchain state (recommit genesis)","titles":["How to hot reload Iroha in a Docker container"]},"8":{"title":"Use custom configuration files","titles":["How to hot reload Iroha in a Docker container"]},"9":{"title":"Use custom environment variables","titles":["How to hot reload Iroha in a Docker container"]},"10":{"title":"Metrics","titles":[]},"11":{"title":"How to use metrics","titles":["Metrics"]},"12":{"title":"/metrics endpoint","titles":["Metrics"]},"13":{"title":"Iroha on bare metal","titles":[]},"14":{"title":"Prerequisites","titles":["Iroha on bare metal"]},"15":{"title":"Setup","titles":["Iroha on bare metal"]},"16":{"title":"Setup: Environment variables","titles":["Iroha on bare metal","Setup"]},"17":{"title":"Setup: Files","titles":["Iroha on bare metal","Setup"]},"18":{"title":"First run of Iroha on bare metal","titles":["Iroha on bare metal"]},"19":{"title":"Deploy a minimal BFT network","titles":["Iroha on bare metal"]},"20":{"title":"Using Environment Variables","titles":["Iroha on bare metal","Deploy a minimal BFT network"]},"21":{"title":"Using Files","titles":["Iroha on bare metal","Deploy a minimal BFT network"]},"22":{"title":"Real-world deployment","titles":["Iroha on bare metal"]},"23":{"title":"Accounts","titles":[]},"24":{"title":"Assets","titles":[]},"25":{"title":"Value Types","titles":["Assets"]},"26":{"title":"Asset Structure","titles":["Assets"]},"27":{"title":"Instructions","titles":["Assets"]},"28":{"title":"Consensus","titles":[]},"29":{"title":"Data Model","titles":[]},"30":{"title":"Domains","titles":[]},"31":{"title":"Events","titles":[]},"32":{"title":"Pipeline Events","titles":["Events"]},"33":{"title":"Data Events","titles":["Events"]},"34":{"title":"Time Events","titles":["Events"]},"35":{"title":"Trigger Execution Events","titles":["Events"]},"36":{"title":"Expressions, Conditionals, Logic","titles":[]},"37":{"title":"Filters","titles":[]},"38":{"title":"Data Filters","titles":["Filters"]},"39":{"title":"How Iroha works","titles":[]},"40":{"title":"Iroha Special Instructions","titles":[]},"41":{"title":"Summary","titles":["Iroha Special Instructions"]},"42":{"title":"(Un)Register","titles":["Iroha Special Instructions"]},"43":{"title":"Mint/Burn","titles":["Iroha Special Instructions"]},"44":{"title":"Transfer","titles":["Iroha Special Instructions"]},"45":{"title":"Grant/Revoke","titles":["Iroha Special Instructions"]},"46":{"title":"SetKeyValue/RemoveKeyValue","titles":["Iroha Special Instructions"]},"47":{"title":"NewParameter/SetParameter","titles":["Iroha Special Instructions"]},"48":{"title":"ExecuteTrigger","titles":["Iroha Special Instructions"]},"49":{"title":"Composite instructions","titles":["Iroha Special Instructions"]},"50":{"title":"Metadata","titles":[]},"51":{"title":"MetadataChanged","titles":["Metadata"]},"52":{"title":"Store Asset","titles":["Metadata"]},"53":{"title":"Working with metadata","titles":["Metadata"]},"54":{"title":"Queries","titles":["Metadata"]},"55":{"title":"Permissions","titles":["Metadata"]},"56":{"title":"Permissions","titles":[]},"57":{"title":"Permission Tokens","titles":["Permissions"]},"58":{"title":"Pre-configured Permission Tokens","titles":["Permissions","Permission Tokens"]},"59":{"title":"Permission Groups (Roles)","titles":["Permissions"]},"60":{"title":"Register a new role","titles":["Permissions","Permission Groups (Roles)"]},"61":{"title":"Grant a role","titles":["Permissions","Permission Groups (Roles)"]},"62":{"title":"Permission Validators","titles":["Permissions"]},"63":{"title":"Runtime Validators","titles":["Permissions","Permission Validators"]},"64":{"title":"Supported Queries","titles":["Permissions"]},"65":{"title":"Queries","titles":[]},"66":{"title":"Create a query","titles":["Queries"]},"67":{"title":"Pagination","titles":["Queries"]},"68":{"title":"Filters","titles":["Queries"]},"69":{"title":"Sorting","titles":["Queries"]},"70":{"title":"Reference","titles":["Queries"]},"71":{"title":"Transactions","titles":[]},"72":{"title":"Triggers","titles":[]},"73":{"title":"The Anatomy of a Trigger","titles":["Triggers"]},"74":{"title":"Trigger.id","titles":["Triggers","The Anatomy of a Trigger"]},"75":{"title":"Trigger.action","titles":["Triggers","The Anatomy of a Trigger"]},"76":{"title":"Action.executable","titles":["Triggers","The Anatomy of a Trigger","Trigger.action"]},"77":{"title":"Action.repeats","titles":["Triggers","The Anatomy of a Trigger","Trigger.action"]},"78":{"title":"Action.technical_account","titles":["Triggers","The Anatomy of a Trigger","Trigger.action"]},"79":{"title":"Action.filter","titles":["Triggers","The Anatomy of a Trigger","Trigger.action"]},"80":{"title":"Action.metadata","titles":["Triggers","The Anatomy of a Trigger","Trigger.action"]},"81":{"title":"How Triggers Work","titles":["Triggers"]},"82":{"title":"Scope","titles":["Triggers","How Triggers Work"]},"83":{"title":"Domain-scoped Triggers","titles":["Triggers","How Triggers Work","Scope"]},"84":{"title":"Repetition Schema","titles":["Triggers","How Triggers Work"]},"85":{"title":"Types of Triggers","titles":["Triggers"]},"86":{"title":"Data Triggers","titles":["Triggers","Types of Triggers"]},"87":{"title":"Time Triggers","titles":["Triggers","Types of Triggers"]},"88":{"title":"Scheduled Triggers","titles":["Triggers","Types of Triggers","Time Triggers"]},"89":{"title":"Pre-commit Triggers","titles":["Triggers","Types of Triggers","Time Triggers"]},"90":{"title":"By-call Triggers","titles":["Triggers","Types of Triggers"]},"91":{"title":"Event Triggers by Example","titles":["Triggers"]},"92":{"title":"1. Register accounts","titles":["Triggers","Event Triggers by Example"]},"93":{"title":"2. Register a trigger","titles":["Triggers","Event Triggers by Example"]},"94":{"title":"3. Define an event filter","titles":["Triggers","Event Triggers by Example"]},"95":{"title":"4. Create a Trigger instance","titles":["Triggers","Event Triggers by Example"]},"96":{"title":"5. Create a transaction","titles":["Triggers","Event Triggers by Example"]},"97":{"title":"How it works","titles":["Triggers","Event Triggers by Example"]},"98":{"title":"Supported ISI","titles":["Triggers"]},"99":{"title":"Supported Queries","titles":["Triggers"]},"100":{"title":"WASM","titles":[]},"101":{"title":"Working with WASM","titles":["WASM"]},"102":{"title":"Simple Rust Smart Contract Example","titles":["WASM"]},"103":{"title":"1. Create a new project","titles":["WASM","Simple Rust Smart Contract Example"]},"104":{"title":"2. Write a smart contract","titles":["WASM","Simple Rust Smart Contract Example"]},"105":{"title":"Advanced Smart Contracts: Optimising for Size","titles":["WASM"]},"106":{"title":"Remove debugging info","titles":["WASM","Advanced Smart Contracts: Optimising for Size"]},"107":{"title":"Work under a no_std environment","titles":["WASM","Advanced Smart Contracts: Optimising for Size"]},"108":{"title":"Re-compile libcore","titles":["WASM","Advanced Smart Contracts: Optimising for Size"]},"109":{"title":"Use tools to optimise WASM size","titles":["WASM","Advanced Smart Contracts: Optimising for Size"]},"110":{"title":"Conclusion","titles":["WASM","Advanced Smart Contracts: Optimising for Size"]},"111":{"title":"World","titles":[]},"112":{"title":"World State View (WSV)","titles":["World"]},"113":{"title":"Client Configuration","titles":[]},"114":{"title":"Generation","titles":["Client Configuration"]},"115":{"title":"Public and Private Keys","titles":["Client Configuration"]},"116":{"title":"User account","titles":["Client Configuration"]},"117":{"title":"Basic Authentication Credentials","titles":["Client Configuration"]},"118":{"title":"Iroha Public Addresses","titles":["Client Configuration"]},"119":{"title":"TORII_API_URL","titles":["Client Configuration","Iroha Public Addresses"]},"120":{"title":"TORII_TELEMETRY_URL","titles":["Client Configuration","Iroha Public Addresses"]},"121":{"title":"Transaction Limits","titles":["Client Configuration"]},"122":{"title":"Transaction TTL and Timeout","titles":["Client Configuration"]},"123":{"title":"Transaction Nonce","titles":["Client Configuration"]},"124":{"title":"Configuration Types","titles":[]},"125":{"title":"Option<..>","titles":["Configuration Types"]},"126":{"title":"Option<Option<..>>","titles":["Configuration Types"]},"127":{"title":"Sumeragi: default null values","titles":["Configuration Types"]},"128":{"title":"Genesis Block","titles":[]},"129":{"title":"Generation","titles":["Genesis Block"]},"130":{"title":"Generate default genesis block","titles":["Genesis Block","Generation"]},"131":{"title":"Configuration","titles":["Genesis Block"]},"132":{"title":"Keys for Network Deployment","titles":[]},"133":{"title":"Setting Keys For a New Network","titles":["Keys for Network Deployment"]},"134":{"title":"1. Generate New Key Pairs","titles":["Keys for Network Deployment","Setting Keys For a New Network"]},"135":{"title":"2. Update Keys For Peers","titles":["Keys for Network Deployment","Setting Keys For a New Network"]},"136":{"title":"3. Register a Non-Genesis Account","titles":["Keys for Network Deployment"]},"137":{"title":"Choosing Between the Store and Metadata Assets","titles":[]},"138":{"title":"Public and Private Blockchains","titles":[]},"139":{"title":"Permissions","titles":["Public and Private Blockchains"]},"140":{"title":"Peers","titles":["Public and Private Blockchains"]},"141":{"title":"Registering accounts","titles":["Public and Private Blockchains"]},"142":{"title":"Configuration and Management","titles":[]},"143":{"title":"Peer Configuration","titles":[]},"144":{"title":"Generation","titles":["Peer Configuration"]},"145":{"title":"Public and private keys","titles":["Peer Configuration"]},"146":{"title":"Trusted Peers","titles":["Peer Configuration"]},"147":{"title":"Iroha Public Addresses","titles":["Peer Configuration"]},"148":{"title":"API_URL","titles":["Peer Configuration","Iroha Public Addresses"]},"149":{"title":"P2P_ADDR","titles":["Peer Configuration","Iroha Public Addresses"]},"150":{"title":"TELEMETRY_URL","titles":["Peer Configuration","Iroha Public Addresses"]},"151":{"title":"Genesis","titles":["Peer Configuration"]},"152":{"title":"Logger","titles":["Peer Configuration"]},"153":{"title":"MAX_LOG_LEVEL","titles":["Peer Configuration","Logger"]},"154":{"title":"LOG_FILE_PATH","titles":["Peer Configuration","Logger"]},"155":{"title":"Kura","titles":["Peer Configuration"]},"156":{"title":"Peer Management","titles":[]},"157":{"title":"Public Blockchain","titles":["Peer Management"]},"158":{"title":"Private Blockchain","titles":["Peer Management"]},"159":{"title":"Registering peers","titles":["Peer Management","Private Blockchain"]},"160":{"title":"1. Grant the user permissions","titles":["Peer Management","Private Blockchain","Registering peers"]},"161":{"title":"2. Set up a peer","titles":["Peer Management","Private Blockchain","Registering peers"]},"162":{"title":"3. Submit the instruction","titles":["Peer Management","Private Blockchain","Registering peers"]},"163":{"title":"Unregistering peers","titles":["Peer Management","Private Blockchain"]},"164":{"title":"Sample Configuration Files","titles":[]},"165":{"title":"Bash Guide","titles":[]},"166":{"title":"0. A brief primer on CLI applications","titles":["Bash Guide"]},"167":{"title":"1. Iroha 2 Client Setup","titles":["Bash Guide"]},"168":{"title":"2. Configuring Iroha 2","titles":["Bash Guide"]},"169":{"title":"3. Registering a Domain","titles":["Bash Guide"]},"170":{"title":"4. Registering an Account","titles":["Bash Guide"]},"171":{"title":"5. Registering and minting assets","titles":["Bash Guide"]},"172":{"title":"6. Transferring assets","titles":["Bash Guide"]},"173":{"title":"7. Burning assets","titles":["Bash Guide"]},"174":{"title":"8. Visualizing outputs","titles":["Bash Guide"]},"175":{"title":"Build Iroha 2 Client","titles":[]},"176":{"title":"Install the Rust Toolchain","titles":["Build Iroha 2 Client"]},"177":{"title":"Build Iroha Client","titles":["Build Iroha 2 Client"]},"178":{"title":"Get Started","titles":[]},"179":{"title":"Install Iroha 2","titles":[]},"180":{"title":"Choose Version","titles":["Install Iroha 2"]},"181":{"title":"Install Prerequisites","titles":["Install Iroha 2"]},"182":{"title":"Install OpenSSL","titles":["Install Iroha 2","Install Prerequisites"]},"183":{"title":"Install Iroha from GitHub","titles":["Install Iroha 2"]},"184":{"title":"What\'s next","titles":["Install Iroha 2"]},"185":{"title":"JavaScript/TypeScript Guide","titles":[]},"186":{"title":"1. Client Installation","titles":["JavaScript/TypeScript Guide"]},"187":{"title":"2. Client Configuration","titles":["JavaScript/TypeScript Guide"]},"188":{"title":"3. Registering a Domain","titles":["JavaScript/TypeScript Guide"]},"189":{"title":"4. Registering an Account","titles":["JavaScript/TypeScript Guide"]},"190":{"title":"5. Registering and minting assets","titles":["JavaScript/TypeScript Guide"]},"191":{"title":"6. Transferring assets","titles":["JavaScript/TypeScript Guide"]},"192":{"title":"7. Querying for Domains, Accounts and Assets","titles":["JavaScript/TypeScript Guide"]},"193":{"title":"8. Visualizing outputs in Web UI","titles":["JavaScript/TypeScript Guide"]},"194":{"title":"Demo","titles":["JavaScript/TypeScript Guide","8. Visualizing outputs in Web UI"]},"195":{"title":"9. Subscribing to Block Stream","titles":["JavaScript/TypeScript Guide"]},"196":{"title":"Kotlin/Java Guide","titles":[]},"197":{"title":"1. Iroha 2 Client Setup","titles":["Kotlin/Java Guide"]},"198":{"title":"2. Configuring Iroha 2","titles":["Kotlin/Java Guide"]},"199":{"title":"3. Querying and Registering Domains","titles":["Kotlin/Java Guide"]},"200":{"title":"4. Registering an Account","titles":["Kotlin/Java Guide"]},"201":{"title":"5. Registering and minting assets","titles":["Kotlin/Java Guide"]},"202":{"title":"6. Transferring assets","titles":["Kotlin/Java Guide"]},"203":{"title":"7. Burning assets","titles":["Kotlin/Java Guide"]},"204":{"title":"8. Visualizing outputs","titles":["Kotlin/Java Guide"]},"205":{"title":"9. Samples in pure Java","titles":["Kotlin/Java Guide"]},"206":{"title":"Python 3 Guide","titles":[]},"207":{"title":"1. Iroha 2 Client Setup","titles":["Python 3 Guide"]},"208":{"title":"2. Configuring Iroha 2","titles":["Python 3 Guide"]},"209":{"title":"3. Registering a Domain","titles":["Python 3 Guide"]},"210":{"title":"4. Registering an Account","titles":["Python 3 Guide"]},"211":{"title":"5. Registering and minting assets","titles":["Python 3 Guide"]},"212":{"title":"6. Visualizing outputs","titles":["Python 3 Guide"]},"213":{"title":"Quick Start with Docker","titles":[]},"214":{"title":"Docker Options","titles":["Quick Start with Docker"]},"215":{"title":"Rust Guide","titles":[]},"216":{"title":"1. Iroha 2 Client Setup","titles":["Rust Guide"]},"217":{"title":"2. Configuring Iroha 2","titles":["Rust Guide"]},"218":{"title":"3. Registering a Domain","titles":["Rust Guide"]},"219":{"title":"4. Registering an Account","titles":["Rust Guide"]},"220":{"title":"5. Registering and minting assets","titles":["Rust Guide"]},"221":{"title":"6. Burning assets","titles":["Rust Guide"]},"222":{"title":"7. Visualising outputs","titles":["Rust Guide"]},"223":{"title":"Introduction","titles":[]},"224":{"title":"Preamble","titles":["Introduction"]},"225":{"title":"Navigation","titles":["Introduction"]},"226":{"title":"Tutorial Updates","titles":["Introduction"]},"227":{"title":"Iroha 2","titles":[]},"228":{"title":"Learn More","titles":["Iroha 2"]},"229":{"title":"Iroha 2 vs. Iroha 1","titles":[]},"230":{"title":"Fault Tolerance","titles":["Iroha 2 vs. Iroha 1"]},"231":{"title":"Minimalist Code Base","titles":["Iroha 2 vs. Iroha 1"]},"232":{"title":"Flexibility","titles":["Iroha 2 vs. Iroha 1"]},"233":{"title":"Smart Contracts","titles":["Iroha 2 vs. Iroha 1"]},"234":{"title":"Static and Dynamic Linking","titles":["Iroha 2 vs. Iroha 1"]},"235":{"title":"Testing","titles":["Iroha 2 vs. Iroha 1"]},"236":{"title":"CSD/RTGS linkages Proof of concept","titles":[]},"237":{"title":"Generating Cryptographic Keys","titles":[]},"238":{"title":"Generating Cryptographic Keys with Kagami","titles":["Generating Cryptographic Keys"]},"239":{"title":"Examples","titles":["Generating Cryptographic Keys","Generating Cryptographic Keys with Kagami"]},"240":{"title":"Other Operations with Kagami","titles":["Generating Cryptographic Keys"]},"241":{"title":"1. Building kagami","titles":["Generating Cryptographic Keys","Other Operations with Kagami"]},"242":{"title":"2. Installing the source-built kagami into /bin","titles":["Generating Cryptographic Keys","Other Operations with Kagami"]},"243":{"title":"3. Moving kagami to the .local/bin directory","titles":["Generating Cryptographic Keys","Other Operations with Kagami"]},"244":{"title":"Making the <username>/.local/bin directory available to the shell","titles":["Generating Cryptographic Keys","Other Operations with Kagami","3. Moving kagami to the .local/bin directory"]},"245":{"title":"Security","titles":[]},"246":{"title":"Navigation","titles":["Security",null]},"247":{"title":"Operational Security","titles":[]},"248":{"title":"Recommended OPSEC Measures","titles":["Operational Security"]},"249":{"title":"Using Browsers","titles":["Operational Security"]},"250":{"title":"Recovery Plan","titles":["Operational Security"]},"251":{"title":"Password Security","titles":[]},"252":{"title":"Password Strength","titles":["Password Security"]},"253":{"title":"Password Vulnerabilities","titles":["Password Security"]},"254":{"title":"Public Key Cryptography","titles":[]},"255":{"title":"Encryption and Signatures","titles":["Public Key Cryptography"]},"256":{"title":"Keys on the Client Side","titles":["Public Key Cryptography"]},"257":{"title":"Security Principles","titles":[]},"258":{"title":"General Security Principles","titles":["Security Principles"]},"259":{"title":"Security Principles for Individual Users","titles":["Security Principles"]},"260":{"title":"Security Principles for Organisations","titles":["Security Principles"]},"261":{"title":"Storing Cryptographic Keys","titles":[]},"262":{"title":"Storing Cryptographic Keys Digitally","titles":["Storing Cryptographic Keys"]},"263":{"title":"Using SSH and SSH Agent","titles":["Storing Cryptographic Keys","Storing Cryptographic Keys Digitally"]},"264":{"title":"Adding a Password Manager Program","titles":["Storing Cryptographic Keys","Storing Cryptographic Keys Digitally"]},"265":{"title":"Configuring KeePassXC","titles":["Storing Cryptographic Keys","Storing Cryptographic Keys Digitally","Adding a Password Manager Program"]},"266":{"title":"Expected Results","titles":["Storing Cryptographic Keys","Storing Cryptographic Keys Digitally","Adding a Password Manager Program","Configuring KeePassXC"]},"267":{"title":"Storing Cryptographic Keys Physically","titles":["Storing Cryptographic Keys"]},"268":{"title":"Using a Hardware Key","titles":["Storing Cryptographic Keys","Storing Cryptographic Keys Physically"]},"269":{"title":"Using a Mnemonic Phrase","titles":["Storing Cryptographic Keys","Storing Cryptographic Keys Physically"]},"270":{"title":"Receive support","titles":[]},"271":{"title":"Troubleshooting Configuration Issues","titles":[]},"272":{"title":"Outdated genesis on a Docker-compose setup","titles":["Troubleshooting Configuration Issues"]},"273":{"title":"Multihash Format of Private and Public Keys","titles":["Troubleshooting Configuration Issues"]},"274":{"title":"Troubleshooting Deployment Issues","titles":[]},"275":{"title":"Docker","titles":["Troubleshooting Deployment Issues"]},"276":{"title":"Kubernetes","titles":["Troubleshooting Deployment Issues"]},"277":{"title":"Troubleshooting Installation Issues","titles":[]},"278":{"title":"Troubleshooting Rust Toolchain","titles":["Troubleshooting Installation Issues"]},"279":{"title":"Check Rust version","titles":["Troubleshooting Installation Issues","Troubleshooting Rust Toolchain"]},"280":{"title":"Check installation location","titles":["Troubleshooting Installation Issues","Troubleshooting Rust Toolchain"]},"281":{"title":"Check the default Rust version","titles":["Troubleshooting Installation Issues","Troubleshooting Rust Toolchain"]},"282":{"title":"Check if there are other Rust versions","titles":["Troubleshooting Installation Issues","Troubleshooting Rust Toolchain"]},"283":{"title":"Troubleshooting Python toolchain","titles":["Troubleshooting Installation Issues"]},"284":{"title":"Troubleshooting Integration Issues","titles":[]},"285":{"title":"Troubleshooting","titles":[]},"286":{"title":"Check the keys","titles":["Troubleshooting"]},"287":{"title":"Compatibility Matrix","titles":[]},"288":{"title":"Data Model Schema","titles":[]},"289":{"title":"Account","titles":["Data Model Schema"]},"290":{"title":"AccountEvent","titles":["Data Model Schema"]},"291":{"title":"AccountEventFilter","titles":["Data Model Schema"]},"292":{"title":"AccountFilter","titles":["Data Model Schema"]},"293":{"title":"AccountId","titles":["Data Model Schema"]},"294":{"title":"AccountPermissionChanged","titles":["Data Model Schema"]},"295":{"title":"AccountRoleChanged","titles":["Data Model Schema"]},"296":{"title":"Action<TriggeringFilterBox, Executable>","titles":["Data Model Schema"]},"297":{"title":"Action<TriggeringFilterBox, OptimizedExecutable>","titles":["Data Model Schema"]},"298":{"title":"Add","titles":["Data Model Schema"]},"299":{"title":"Algorithm","titles":["Data Model Schema"]},"300":{"title":"And","titles":["Data Model Schema"]},"301":{"title":"Array<Interval<u16>, 8>","titles":["Data Model Schema"]},"302":{"title":"Array<Interval<u8>, 4>","titles":["Data Model Schema"]},"303":{"title":"Array<u16, 8>","titles":["Data Model Schema"]},"304":{"title":"Array<u8, 32>","titles":["Data Model Schema"]},"305":{"title":"Array<u8, 4>","titles":["Data Model Schema"]},"306":{"title":"Asset","titles":["Data Model Schema"]},"307":{"title":"AssetChanged","titles":["Data Model Schema"]},"308":{"title":"AssetDefinition","titles":["Data Model Schema"]},"309":{"title":"AssetDefinitionEvent","titles":["Data Model Schema"]},"310":{"title":"AssetDefinitionEventFilter","titles":["Data Model Schema"]},"311":{"title":"AssetDefinitionFilter","titles":["Data Model Schema"]},"312":{"title":"AssetDefinitionId","titles":["Data Model Schema"]},"313":{"title":"AssetDefinitionOwnerChanged","titles":["Data Model Schema"]},"314":{"title":"AssetDefinitionTotalQuantityChanged","titles":["Data Model Schema"]},"315":{"title":"AssetEvent","titles":["Data Model Schema"]},"316":{"title":"AssetEventFilter","titles":["Data Model Schema"]},"317":{"title":"AssetFilter","titles":["Data Model Schema"]},"318":{"title":"AssetId","titles":["Data Model Schema"]},"319":{"title":"AssetValue","titles":["Data Model Schema"]},"320":{"title":"AssetValueType","titles":["Data Model Schema"]},"321":{"title":"AtIndex","titles":["Data Model Schema"]},"322":{"title":"BatchedResponse<Value>","titles":["Data Model Schema"]},"323":{"title":"BatchedResponse<Vec<VersionedSignedTransaction>>","titles":["Data Model Schema"]},"324":{"title":"BinaryOpIncompatibleNumericValueTypesError","titles":["Data Model Schema"]},"325":{"title":"BlockHeader","titles":["Data Model Schema"]},"326":{"title":"BlockMessage","titles":["Data Model Schema"]},"327":{"title":"BlockPayload","titles":["Data Model Schema"]},"328":{"title":"BlockRejectionReason","titles":["Data Model Schema"]},"329":{"title":"BlockSubscriptionRequest","titles":["Data Model Schema"]},"330":{"title":"BurnBox","titles":["Data Model Schema"]},"331":{"title":"Conditional","titles":["Data Model Schema"]},"332":{"title":"ConfigurationEvent","titles":["Data Model Schema"]},"333":{"title":"Container","titles":["Data Model Schema"]},"334":{"title":"Contains","titles":["Data Model Schema"]},"335":{"title":"ContainsAll","titles":["Data Model Schema"]},"336":{"title":"ContainsAny","titles":["Data Model Schema"]},"337":{"title":"ContextValue","titles":["Data Model Schema"]},"338":{"title":"DataEntityFilter","titles":["Data Model Schema"]},"339":{"title":"DataEvent","titles":["Data Model Schema"]},"340":{"title":"Divide","titles":["Data Model Schema"]},"341":{"title":"Domain","titles":["Data Model Schema"]},"342":{"title":"DomainEvent","titles":["Data Model Schema"]},"343":{"title":"DomainEventFilter","titles":["Data Model Schema"]},"344":{"title":"DomainFilter","titles":["Data Model Schema"]},"345":{"title":"DomainId","titles":["Data Model Schema"]},"346":{"title":"Duration","titles":["Data Model Schema"]},"347":{"title":"Equal","titles":["Data Model Schema"]},"348":{"title":"EvaluatesTo<AccountId>","titles":["Data Model Schema"]},"349":{"title":"EvaluatesTo<AssetDefinitionId>","titles":["Data Model Schema"]},"350":{"title":"EvaluatesTo<AssetId>","titles":["Data Model Schema"]},"351":{"title":"EvaluatesTo<DomainId>","titles":["Data Model Schema"]},"352":{"title":"EvaluatesTo<HashOf<VersionedSignedBlock>>","titles":["Data Model Schema"]},"353":{"title":"EvaluatesTo<HashOf<VersionedSignedTransaction>>","titles":["Data Model Schema"]},"354":{"title":"EvaluatesTo<IdBox>","titles":["Data Model Schema"]},"355":{"title":"EvaluatesTo<Level>","titles":["Data Model Schema"]},"356":{"title":"EvaluatesTo<Name>","titles":["Data Model Schema"]},"357":{"title":"EvaluatesTo<NumericValue>","titles":["Data Model Schema"]},"358":{"title":"EvaluatesTo<Parameter>","titles":["Data Model Schema"]},"359":{"title":"EvaluatesTo<RegistrableBox>","titles":["Data Model Schema"]},"360":{"title":"EvaluatesTo<RoleId>","titles":["Data Model Schema"]},"361":{"title":"EvaluatesTo<String>","titles":["Data Model Schema"]},"362":{"title":"EvaluatesTo<TriggerId>","titles":["Data Model Schema"]},"363":{"title":"EvaluatesTo<UpgradableBox>","titles":["Data Model Schema"]},"364":{"title":"EvaluatesTo<Value>","titles":["Data Model Schema"]},"365":{"title":"EvaluatesTo<Vec<Value>>","titles":["Data Model Schema"]},"366":{"title":"EvaluatesTo<bool>","titles":["Data Model Schema"]},"367":{"title":"EvaluationError","titles":["Data Model Schema"]},"368":{"title":"Event","titles":["Data Model Schema"]},"369":{"title":"EventMessage","titles":["Data Model Schema"]},"370":{"title":"EventSubscriptionRequest","titles":["Data Model Schema"]},"371":{"title":"Executable","titles":["Data Model Schema"]},"372":{"title":"ExecuteTriggerBox","titles":["Data Model Schema"]},"373":{"title":"ExecuteTriggerEvent","titles":["Data Model Schema"]},"374":{"title":"ExecuteTriggerEventFilter","titles":["Data Model Schema"]},"375":{"title":"ExecutionTime","titles":["Data Model Schema"]},"376":{"title":"Expression","titles":["Data Model Schema"]},"377":{"title":"FailBox","titles":["Data Model Schema"]},"378":{"title":"FilterBox","titles":["Data Model Schema"]},"379":{"title":"FilterOpt<AccountEventFilter>","titles":["Data Model Schema"]},"380":{"title":"FilterOpt<AccountFilter>","titles":["Data Model Schema"]},"381":{"title":"FilterOpt<AssetDefinitionEventFilter>","titles":["Data Model Schema"]},"382":{"title":"FilterOpt<AssetDefinitionFilter>","titles":["Data Model Schema"]},"383":{"title":"FilterOpt<AssetEventFilter>","titles":["Data Model Schema"]},"384":{"title":"FilterOpt<AssetFilter>","titles":["Data Model Schema"]},"385":{"title":"FilterOpt<DataEntityFilter>","titles":["Data Model Schema"]},"386":{"title":"FilterOpt<DomainEventFilter>","titles":["Data Model Schema"]},"387":{"title":"FilterOpt<DomainFilter>","titles":["Data Model Schema"]},"388":{"title":"FilterOpt<OriginFilter<AccountEvent>>","titles":["Data Model Schema"]},"389":{"title":"FilterOpt<OriginFilter<AssetDefinitionEvent>>","titles":["Data Model Schema"]},"390":{"title":"FilterOpt<OriginFilter<AssetEvent>>","titles":["Data Model Schema"]},"391":{"title":"FilterOpt<OriginFilter<DomainEvent>>","titles":["Data Model Schema"]},"392":{"title":"FilterOpt<OriginFilter<PeerEvent>>","titles":["Data Model Schema"]},"393":{"title":"FilterOpt<OriginFilter<RoleEvent>>","titles":["Data Model Schema"]},"394":{"title":"FilterOpt<OriginFilter<TriggerEvent>>","titles":["Data Model Schema"]},"395":{"title":"FilterOpt<PeerEventFilter>","titles":["Data Model Schema"]},"396":{"title":"FilterOpt<PeerFilter>","titles":["Data Model Schema"]},"397":{"title":"FilterOpt<RoleEventFilter>","titles":["Data Model Schema"]},"398":{"title":"FilterOpt<RoleFilter>","titles":["Data Model Schema"]},"399":{"title":"FilterOpt<TriggerEventFilter>","titles":["Data Model Schema"]},"400":{"title":"FilterOpt<TriggerFilter>","titles":["Data Model Schema"]},"401":{"title":"FindAccountById","titles":["Data Model Schema"]},"402":{"title":"FindAccountKeyValueByIdAndKey","titles":["Data Model Schema"]},"403":{"title":"FindAccountsByDomainId","titles":["Data Model Schema"]},"404":{"title":"FindAccountsByName","titles":["Data Model Schema"]},"405":{"title":"FindAccountsWithAsset","titles":["Data Model Schema"]},"406":{"title":"FindAllAccounts","titles":["Data Model Schema"]},"407":{"title":"FindAllActiveTriggerIds","titles":["Data Model Schema"]},"408":{"title":"FindAllAssets","titles":["Data Model Schema"]},"409":{"title":"FindAllAssetsDefinitions","titles":["Data Model Schema"]},"410":{"title":"FindAllBlockHeaders","titles":["Data Model Schema"]},"411":{"title":"FindAllBlocks","titles":["Data Model Schema"]},"412":{"title":"FindAllDomains","titles":["Data Model Schema"]},"413":{"title":"FindAllParameters","titles":["Data Model Schema"]},"414":{"title":"FindAllPeers","titles":["Data Model Schema"]},"415":{"title":"FindAllRoleIds","titles":["Data Model Schema"]},"416":{"title":"FindAllRoles","titles":["Data Model Schema"]},"417":{"title":"FindAllTransactions","titles":["Data Model Schema"]},"418":{"title":"FindAssetById","titles":["Data Model Schema"]},"419":{"title":"FindAssetDefinitionById","titles":["Data Model Schema"]},"420":{"title":"FindAssetDefinitionKeyValueByIdAndKey","titles":["Data Model Schema"]},"421":{"title":"FindAssetKeyValueByIdAndKey","titles":["Data Model Schema"]},"422":{"title":"FindAssetQuantityById","titles":["Data Model Schema"]},"423":{"title":"FindAssetsByAccountId","titles":["Data Model Schema"]},"424":{"title":"FindAssetsByAssetDefinitionId","titles":["Data Model Schema"]},"425":{"title":"FindAssetsByDomainId","titles":["Data Model Schema"]},"426":{"title":"FindAssetsByDomainIdAndAssetDefinitionId","titles":["Data Model Schema"]},"427":{"title":"FindAssetsByName","titles":["Data Model Schema"]},"428":{"title":"FindBlockHeaderByHash","titles":["Data Model Schema"]},"429":{"title":"FindDomainById","titles":["Data Model Schema"]},"430":{"title":"FindDomainKeyValueByIdAndKey","titles":["Data Model Schema"]},"431":{"title":"FindError","titles":["Data Model Schema"]},"432":{"title":"FindPermissionTokenSchema","titles":["Data Model Schema"]},"433":{"title":"FindPermissionTokensByAccountId","titles":["Data Model Schema"]},"434":{"title":"FindRoleByRoleId","titles":["Data Model Schema"]},"435":{"title":"FindRolesByAccountId","titles":["Data Model Schema"]},"436":{"title":"FindTotalAssetQuantityByAssetDefinitionId","titles":["Data Model Schema"]},"437":{"title":"FindTransactionByHash","titles":["Data Model Schema"]},"438":{"title":"FindTransactionsByAccountId","titles":["Data Model Schema"]},"439":{"title":"FindTriggerById","titles":["Data Model Schema"]},"440":{"title":"FindTriggerKeyValueByIdAndKey","titles":["Data Model Schema"]},"441":{"title":"FindTriggersByDomainId","titles":["Data Model Schema"]},"442":{"title":"Fixed","titles":["Data Model Schema"]},"443":{"title":"FixedPoint<i64>","titles":["Data Model Schema"]},"444":{"title":"ForwardCursor","titles":["Data Model Schema"]},"445":{"title":"GenericPredicateBox<ValuePredicate>","titles":["Data Model Schema"]},"446":{"title":"GrantBox","titles":["Data Model Schema"]},"447":{"title":"Greater","titles":["Data Model Schema"]},"448":{"title":"Hash","titles":["Data Model Schema"]},"449":{"title":"HashOf<MerkleTree<VersionedSignedTransaction>>","titles":["Data Model Schema"]},"450":{"title":"HashOf<VersionedSignedBlock>","titles":["Data Model Schema"]},"451":{"title":"HashOf<VersionedSignedTransaction>","titles":["Data Model Schema"]},"452":{"title":"HashOf<WasmSmartContract>","titles":["Data Model Schema"]},"453":{"title":"HashValue","titles":["Data Model Schema"]},"454":{"title":"IdBox","titles":["Data Model Schema"]},"455":{"title":"IdentifiableBox","titles":["Data Model Schema"]},"456":{"title":"If","titles":["Data Model Schema"]},"457":{"title":"InstructionBox","titles":["Data Model Schema"]},"458":{"title":"InstructionEvaluationError","titles":["Data Model Schema"]},"459":{"title":"InstructionExecutionError","titles":["Data Model Schema"]},"460":{"title":"InstructionExecutionFail","titles":["Data Model Schema"]},"461":{"title":"InstructionType","titles":["Data Model Schema"]},"462":{"title":"Interval<u16>","titles":["Data Model Schema"]},"463":{"title":"Interval<u8>","titles":["Data Model Schema"]},"464":{"title":"InvalidParameterError","titles":["Data Model Schema"]},"465":{"title":"IpfsPath","titles":["Data Model Schema"]},"466":{"title":"Ipv4Addr","titles":["Data Model Schema"]},"467":{"title":"Ipv4Predicate","titles":["Data Model Schema"]},"468":{"title":"Ipv6Addr","titles":["Data Model Schema"]},"469":{"title":"Ipv6Predicate","titles":["Data Model Schema"]},"470":{"title":"IsAssetDefinitionOwner","titles":["Data Model Schema"]},"471":{"title":"LengthLimits","titles":["Data Model Schema"]},"472":{"title":"Less","titles":["Data Model Schema"]},"473":{"title":"Level","titles":["Data Model Schema"]},"474":{"title":"Limits","titles":["Data Model Schema"]},"475":{"title":"LogBox","titles":["Data Model Schema"]},"476":{"title":"MathError","titles":["Data Model Schema"]},"477":{"title":"MerkleTree<VersionedSignedTransaction>","titles":["Data Model Schema"]},"478":{"title":"Metadata","titles":["Data Model Schema"]},"479":{"title":"MetadataChanged<AccountId>","titles":["Data Model Schema"]},"480":{"title":"MetadataChanged<AssetDefinitionId>","titles":["Data Model Schema"]},"481":{"title":"MetadataChanged<AssetId>","titles":["Data Model Schema"]},"482":{"title":"MetadataChanged<DomainId>","titles":["Data Model Schema"]},"483":{"title":"MetadataError","titles":["Data Model Schema"]},"484":{"title":"MintBox","titles":["Data Model Schema"]},"485":{"title":"MintabilityError","titles":["Data Model Schema"]},"486":{"title":"Mintable","titles":["Data Model Schema"]},"487":{"title":"Mismatch<AssetDefinitionId>","titles":["Data Model Schema"]},"488":{"title":"Mismatch<AssetValueType>","titles":["Data Model Schema"]},"489":{"title":"Mismatch<Value>","titles":["Data Model Schema"]},"490":{"title":"Mod","titles":["Data Model Schema"]},"491":{"title":"Multiply","titles":["Data Model Schema"]},"492":{"title":"Name","titles":["Data Model Schema"]},"493":{"title":"NewAccount","titles":["Data Model Schema"]},"494":{"title":"NewAssetDefinition","titles":["Data Model Schema"]},"495":{"title":"NewDomain","titles":["Data Model Schema"]},"496":{"title":"NewParameterBox","titles":["Data Model Schema"]},"497":{"title":"NewRole","titles":["Data Model Schema"]},"498":{"title":"NonTrivial<GenericPredicateBox<ValuePredicate>>","titles":["Data Model Schema"]},"499":{"title":"NonZero<u32>","titles":["Data Model Schema"]},"500":{"title":"NonZero<u64>","titles":["Data Model Schema"]},"501":{"title":"Not","titles":["Data Model Schema"]},"502":{"title":"NotificationEvent","titles":["Data Model Schema"]},"503":{"title":"NotificationEventFilter","titles":["Data Model Schema"]},"504":{"title":"NumericValue","titles":["Data Model Schema"]},"505":{"title":"OptimizedExecutable","titles":["Data Model Schema"]},"506":{"title":"Option<DomainId>","titles":["Data Model Schema"]},"507":{"title":"Option<Duration>","titles":["Data Model Schema"]},"508":{"title":"Option<Hash>","titles":["Data Model Schema"]},"509":{"title":"Option<HashOf<MerkleTree<VersionedSignedTransaction>>>","titles":["Data Model Schema"]},"510":{"title":"Option<HashOf<VersionedSignedBlock>>","titles":["Data Model Schema"]},"511":{"title":"Option<InstructionBox>","titles":["Data Model Schema"]},"512":{"title":"Option<IpfsPath>","titles":["Data Model Schema"]},"513":{"title":"Option<NonZero<u32>>","titles":["Data Model Schema"]},"514":{"title":"Option<NonZero<u64>>","titles":["Data Model Schema"]},"515":{"title":"Option<PipelineEntityKind>","titles":["Data Model Schema"]},"516":{"title":"Option<PipelineStatusKind>","titles":["Data Model Schema"]},"517":{"title":"Option<String>","titles":["Data Model Schema"]},"518":{"title":"Option<TimeInterval>","titles":["Data Model Schema"]},"519":{"title":"Option<TransactionRejectionReason>","titles":["Data Model Schema"]},"520":{"title":"Option<TriggerCompletedOutcomeType>","titles":["Data Model Schema"]},"521":{"title":"Option<TriggerId>","titles":["Data Model Schema"]},"522":{"title":"Or","titles":["Data Model Schema"]},"523":{"title":"OriginFilter<AccountEvent>","titles":["Data Model Schema"]},"524":{"title":"OriginFilter<AssetDefinitionEvent>","titles":["Data Model Schema"]},"525":{"title":"OriginFilter<AssetEvent>","titles":["Data Model Schema"]},"526":{"title":"OriginFilter<DomainEvent>","titles":["Data Model Schema"]},"527":{"title":"OriginFilter<PeerEvent>","titles":["Data Model Schema"]},"528":{"title":"OriginFilter<RoleEvent>","titles":["Data Model Schema"]},"529":{"title":"OriginFilter<TriggerEvent>","titles":["Data Model Schema"]},"530":{"title":"Pair","titles":["Data Model Schema"]},"531":{"title":"Parameter","titles":["Data Model Schema"]},"532":{"title":"ParameterId","titles":["Data Model Schema"]},"533":{"title":"Peer","titles":["Data Model Schema"]},"534":{"title":"PeerEvent","titles":["Data Model Schema"]},"535":{"title":"PeerEventFilter","titles":["Data Model Schema"]},"536":{"title":"PeerFilter","titles":["Data Model Schema"]},"537":{"title":"PeerId","titles":["Data Model Schema"]},"538":{"title":"PermissionRemoved","titles":["Data Model Schema"]},"539":{"title":"PermissionToken","titles":["Data Model Schema"]},"540":{"title":"PermissionTokenSchema","titles":["Data Model Schema"]},"541":{"title":"PermissionTokenSchemaUpdateEvent","titles":["Data Model Schema"]},"542":{"title":"PipelineEntityKind","titles":["Data Model Schema"]},"543":{"title":"PipelineEvent","titles":["Data Model Schema"]},"544":{"title":"PipelineEventFilter","titles":["Data Model Schema"]},"545":{"title":"PipelineRejectionReason","titles":["Data Model Schema"]},"546":{"title":"PipelineStatus","titles":["Data Model Schema"]},"547":{"title":"PipelineStatusKind","titles":["Data Model Schema"]},"548":{"title":"PublicKey","titles":["Data Model Schema"]},"549":{"title":"QueryBox","titles":["Data Model Schema"]},"550":{"title":"QueryExecutionFail","titles":["Data Model Schema"]},"551":{"title":"QueryPayload","titles":["Data Model Schema"]},"552":{"title":"RaiseTo","titles":["Data Model Schema"]},"553":{"title":"RawGenesisBlock","titles":["Data Model Schema"]},"554":{"title":"RegisterBox","titles":["Data Model Schema"]},"555":{"title":"RegistrableBox","titles":["Data Model Schema"]},"556":{"title":"RemoveKeyValueBox","titles":["Data Model Schema"]},"557":{"title":"Repeats","titles":["Data Model Schema"]},"558":{"title":"RepetitionError","titles":["Data Model Schema"]},"559":{"title":"RevokeBox","titles":["Data Model Schema"]},"560":{"title":"Role","titles":["Data Model Schema"]},"561":{"title":"RoleEvent","titles":["Data Model Schema"]},"562":{"title":"RoleEventFilter","titles":["Data Model Schema"]},"563":{"title":"RoleFilter","titles":["Data Model Schema"]},"564":{"title":"RoleId","titles":["Data Model Schema"]},"565":{"title":"Schedule","titles":["Data Model Schema"]},"566":{"title":"SemiInterval<Fixed>","titles":["Data Model Schema"]},"567":{"title":"SemiInterval<u128>","titles":["Data Model Schema"]},"568":{"title":"SemiInterval<u32>","titles":["Data Model Schema"]},"569":{"title":"SemiRange","titles":["Data Model Schema"]},"570":{"title":"SequenceBox","titles":["Data Model Schema"]},"571":{"title":"SetKeyValueBox","titles":["Data Model Schema"]},"572":{"title":"SetParameterBox","titles":["Data Model Schema"]},"573":{"title":"Signature","titles":["Data Model Schema"]},"574":{"title":"SignatureCheckCondition","titles":["Data Model Schema"]},"575":{"title":"SignatureOf<BlockPayload>","titles":["Data Model Schema"]},"576":{"title":"SignatureOf<QueryPayload>","titles":["Data Model Schema"]},"577":{"title":"SignatureOf<TransactionPayload>","titles":["Data Model Schema"]},"578":{"title":"SignaturesOf<BlockPayload>","titles":["Data Model Schema"]},"579":{"title":"SignaturesOf<TransactionPayload>","titles":["Data Model Schema"]},"580":{"title":"SignedBlock","titles":["Data Model Schema"]},"581":{"title":"SignedQuery","titles":["Data Model Schema"]},"582":{"title":"SignedTransaction","titles":["Data Model Schema"]},"583":{"title":"SizeError","titles":["Data Model Schema"]},"584":{"title":"SocketAddr","titles":["Data Model Schema"]},"585":{"title":"SocketAddrHost","titles":["Data Model Schema"]},"586":{"title":"SocketAddrV4","titles":["Data Model Schema"]},"587":{"title":"SocketAddrV6","titles":["Data Model Schema"]},"588":{"title":"SortedMap<AccountId, Account>","titles":["Data Model Schema"]},"589":{"title":"SortedMap<AssetDefinitionId, AssetDefinition>","titles":["Data Model Schema"]},"590":{"title":"SortedMap<AssetDefinitionId, NumericValue>","titles":["Data Model Schema"]},"591":{"title":"SortedMap<AssetId, Asset>","titles":["Data Model Schema"]},"592":{"title":"SortedMap<Name, EvaluatesTo<Value>>","titles":["Data Model Schema"]},"593":{"title":"SortedMap<Name, Value>","titles":["Data Model Schema"]},"594":{"title":"SortedVec<PermissionToken>","titles":["Data Model Schema"]},"595":{"title":"SortedVec<PublicKey>","titles":["Data Model Schema"]},"596":{"title":"SortedVec<RoleId>","titles":["Data Model Schema"]},"597":{"title":"SortedVec<SignatureOf<BlockPayload>>","titles":["Data Model Schema"]},"598":{"title":"SortedVec<SignatureOf<TransactionPayload>>","titles":["Data Model Schema"]},"599":{"title":"String","titles":["Data Model Schema"]},"600":{"title":"StringPredicate","titles":["Data Model Schema"]},"601":{"title":"StringWithJson","titles":["Data Model Schema"]},"602":{"title":"Subtract","titles":["Data Model Schema"]},"603":{"title":"TimeEvent","titles":["Data Model Schema"]},"604":{"title":"TimeEventFilter","titles":["Data Model Schema"]},"605":{"title":"TimeInterval","titles":["Data Model Schema"]},"606":{"title":"TransactionLimitError","titles":["Data Model Schema"]},"607":{"title":"TransactionLimits","titles":["Data Model Schema"]},"608":{"title":"TransactionPayload","titles":["Data Model Schema"]},"609":{"title":"TransactionQueryOutput","titles":["Data Model Schema"]},"610":{"title":"TransactionRejectionReason","titles":["Data Model Schema"]},"611":{"title":"TransactionValue","titles":["Data Model Schema"]},"612":{"title":"TransferBox","titles":["Data Model Schema"]},"613":{"title":"Trigger<TriggeringFilterBox, Executable>","titles":["Data Model Schema"]},"614":{"title":"Trigger<TriggeringFilterBox, OptimizedExecutable>","titles":["Data Model Schema"]},"615":{"title":"TriggerBox","titles":["Data Model Schema"]},"616":{"title":"TriggerCompletedEvent","titles":["Data Model Schema"]},"617":{"title":"TriggerCompletedEventFilter","titles":["Data Model Schema"]},"618":{"title":"TriggerCompletedOutcome","titles":["Data Model Schema"]},"619":{"title":"TriggerCompletedOutcomeType","titles":["Data Model Schema"]},"620":{"title":"TriggerEvent","titles":["Data Model Schema"]},"621":{"title":"TriggerEventFilter","titles":["Data Model Schema"]},"622":{"title":"TriggerFilter","titles":["Data Model Schema"]},"623":{"title":"TriggerId","titles":["Data Model Schema"]},"624":{"title":"TriggerNumberOfExecutionsChanged","titles":["Data Model Schema"]},"625":{"title":"TriggeringFilterBox","titles":["Data Model Schema"]},"626":{"title":"TypeError","titles":["Data Model Schema"]},"627":{"title":"UnregisterBox","titles":["Data Model Schema"]},"628":{"title":"UpgradableBox","titles":["Data Model Schema"]},"629":{"title":"UpgradeBox","titles":["Data Model Schema"]},"630":{"title":"ValidationFail","titles":["Data Model Schema"]},"631":{"title":"Validator","titles":["Data Model Schema"]},"632":{"title":"ValidatorEvent","titles":["Data Model Schema"]},"633":{"title":"ValidatorMode","titles":["Data Model Schema"]},"634":{"title":"Value","titles":["Data Model Schema"]},"635":{"title":"ValueOfKey","titles":["Data Model Schema"]},"636":{"title":"ValuePredicate","titles":["Data Model Schema"]},"637":{"title":"Vec<Event>","titles":["Data Model Schema"]},"638":{"title":"Vec<GenericPredicateBox<ValuePredicate>>","titles":["Data Model Schema"]},"639":{"title":"Vec<InstructionBox>","titles":["Data Model Schema"]},"640":{"title":"Vec<Name>","titles":["Data Model Schema"]},"641":{"title":"Vec<PeerId>","titles":["Data Model Schema"]},"642":{"title":"Vec<PublicKey>","titles":["Data Model Schema"]},"643":{"title":"Vec<TransactionValue>","titles":["Data Model Schema"]},"644":{"title":"Vec<Value>","titles":["Data Model Schema"]},"645":{"title":"Vec<Vec<InstructionBox>>","titles":["Data Model Schema"]},"646":{"title":"Vec<VersionedSignedTransaction>","titles":["Data Model Schema"]},"647":{"title":"Vec<u8>","titles":["Data Model Schema"]},"648":{"title":"VersionedBatchedResponse<Value>","titles":["Data Model Schema"]},"649":{"title":"VersionedBatchedResponse<Vec<VersionedSignedTransaction>>","titles":["Data Model Schema"]},"650":{"title":"VersionedSignedBlock","titles":["Data Model Schema"]},"651":{"title":"VersionedSignedQuery","titles":["Data Model Schema"]},"652":{"title":"VersionedSignedTransaction","titles":["Data Model Schema"]},"653":{"title":"WasmExecutionFail","titles":["Data Model Schema"]},"654":{"title":"WasmInternalRepr","titles":["Data Model Schema"]},"655":{"title":"WasmSmartContract","titles":["Data Model Schema"]},"656":{"title":"Where","titles":["Data Model Schema"]},"657":{"title":"bool","titles":["Data Model Schema"]},"658":{"title":"i64","titles":["Data Model Schema"]},"659":{"title":"u128","titles":["Data Model Schema"]},"660":{"title":"u16","titles":["Data Model Schema"]},"661":{"title":"u32","titles":["Data Model Schema"]},"662":{"title":"u64","titles":["Data Model Schema"]},"663":{"title":"u8","titles":["Data Model Schema"]},"664":{"title":"Foreign Function Interfaces (FFI)","titles":[]},"665":{"title":"Why FFI","titles":["Foreign Function Interfaces (FFI)"]},"666":{"title":"Example","titles":["Foreign Function Interfaces (FFI)"]},"667":{"title":"FFI Binding Generation","titles":["Foreign Function Interfaces (FFI)"]},"668":{"title":"Name Mangling","titles":["Foreign Function Interfaces (FFI)","FFI Binding Generation"]},"669":{"title":"Glossary","titles":[]},"670":{"title":"Blockchain ledgers","titles":["Glossary"]},"671":{"title":"Peer","titles":["Glossary"]},"672":{"title":"Asset","titles":["Glossary"]},"673":{"title":"Fungible assets","titles":["Glossary","Asset"]},"674":{"title":"Non-fungible assets","titles":["Glossary","Asset"]},"675":{"title":"Mintable assets","titles":["Glossary","Asset"]},"676":{"title":"Non-mintable assets","titles":["Glossary","Asset"]},"677":{"title":"Byzantine fault-tolerance (BFT)","titles":["Glossary"]},"678":{"title":"Iroha Components","titles":["Glossary"]},"679":{"title":"Sumeragi (Emperor)","titles":["Glossary","Iroha Components"]},"680":{"title":"Torii (Gate)","titles":["Glossary","Iroha Components"]},"681":{"title":"Kura (Warehouse)","titles":["Glossary","Iroha Components"]},"682":{"title":"Kagami(Teacher and Exemplar and/or looking glass)","titles":["Glossary","Iroha Components"]},"683":{"title":"Merkle tree (hash tree)","titles":["Glossary","Iroha Components"]},"684":{"title":"Smart contracts","titles":["Glossary","Iroha Components"]},"685":{"title":"Triggers","titles":["Glossary","Iroha Components"]},"686":{"title":"Versioning","titles":["Glossary","Iroha Components"]},"687":{"title":"Hijiri (peer reputation system)","titles":["Glossary","Iroha Components"]},"688":{"title":"Iroha Modules","titles":["Glossary"]},"689":{"title":"Iroha Special Instructions (ISI)","titles":["Glossary"]},"690":{"title":"Utility Iroha Special Instructions","titles":["Glossary","Iroha Special Instructions (ISI)","Hijiri (peer reputation system)"]},"691":{"title":"Core Iroha Special Instructions","titles":["Glossary","Iroha Special Instructions (ISI)"]},"692":{"title":"Domain-specific Iroha Special Instructions","titles":["Glossary","Iroha Special Instructions (ISI)"]},"693":{"title":"Custom Iroha Special Instruction","titles":["Glossary","Iroha Special Instructions (ISI)"]},"694":{"title":"Iroha Query","titles":["Glossary"]},"695":{"title":"View change","titles":["Glossary"]},"696":{"title":"World state view (WSV)","titles":["Glossary"]},"697":{"title":"Leader","titles":["Glossary"]},"698":{"title":"Iroha Special Instructions","titles":[]},"699":{"title":"Naming Conventions","titles":[]},"700":{"title":"Permissions","titles":[]},"701":{"title":"Permission Tokens","titles":["Permissions"]},"702":{"title":"CanMintUserAssetDefinitions","titles":["Permissions","Permission Tokens"]},"703":{"title":"CanBurnAssetWithDefinition","titles":["Permissions","Permission Tokens"]},"704":{"title":"CanBurnUserAssets","titles":["Permissions","Permission Tokens"]},"705":{"title":"CanUnregisterAssetWithDefinition","titles":["Permissions","Permission Tokens"]},"706":{"title":"CanTransferUserAssets","titles":["Permissions","Permission Tokens"]},"707":{"title":"CanTransferOnlyFixedNumberOfTimesPerPeriod","titles":["Permissions","Permission Tokens"]},"708":{"title":"CanSetKeyValueInUserAssets","titles":["Permissions","Permission Tokens"]},"709":{"title":"CanRemoveKeyValueInUserAssets","titles":["Permissions","Permission Tokens"]},"710":{"title":"CanSetKeyValueInUserMetadata","titles":["Permissions","Permission Tokens"]},"711":{"title":"CanRemoveKeyValueInUserMetadata","titles":["Permissions","Permission Tokens"]},"712":{"title":"CanSetKeyValueInAssetDefinition","titles":["Permissions","Permission Tokens"]},"713":{"title":"CanRemoveKeyValueInAssetDefinition","titles":["Permissions","Permission Tokens"]},"714":{"title":"CanRegisterDomains","titles":["Permissions","Permission Tokens"]},"715":{"title":"Queries","titles":[]},"716":{"title":"Conventions","titles":["Queries"]},"717":{"title":"Role","titles":["Queries"]},"718":{"title":"FindAllRoles","titles":["Queries","Role"]},"719":{"title":"FindAllRoleIds","titles":["Queries","Role"]},"720":{"title":"FindRoleByRoleId","titles":["Queries","Role"]},"721":{"title":"FindRolesByAccountId","titles":["Queries","Role"]},"722":{"title":"Permission","titles":["Queries"]},"723":{"title":"FindAllPermissionTokenDefinitions","titles":["Queries","Permission"]},"724":{"title":"FindPermissionTokensByAccountId","titles":["Queries","Permission"]},"725":{"title":"Account","titles":["Queries"]},"726":{"title":"FindAllAccounts","titles":["Queries","Account"]},"727":{"title":"FindAccountById","titles":["Queries","Account"]},"728":{"title":"FindAccountKeyValueByIdAndKey","titles":["Queries","Account"]},"729":{"title":"FindAccountsByName","titles":["Queries","Account"]},"730":{"title":"FindAccountsByDomainId","titles":["Queries","Account"]},"731":{"title":"FindAccountsWithAsset","titles":["Queries","Account"]},"732":{"title":"Asset","titles":["Queries"]},"733":{"title":"FindAllAssets","titles":["Queries","Asset"]},"734":{"title":"FindAllAssetDefinitions","titles":["Queries","Asset"]},"735":{"title":"FindAssetById","titles":["Queries","Asset"]},"736":{"title":"FindAssetsByName","titles":["Queries","Asset"]},"737":{"title":"FindAssetsByAccountId","titles":["Queries","Asset"]},"738":{"title":"FindAssetsByAssetDefinitionId","titles":["Queries","Asset"]},"739":{"title":"FindAssetsByDomainId","titles":["Queries","Asset"]},"740":{"title":"FindAssetsByDomainIdAndAssetDefinitionId","titles":["Queries","Asset"]},"741":{"title":"FindAssetQuantityById","titles":["Queries","Asset"]},"742":{"title":"FindAssetKeyValueByIdAndKey","titles":["Queries","Asset"]},"743":{"title":"FindAssetDefinitionKeyValueByIdAndKey","titles":["Queries","Asset"]},"744":{"title":"FindTotalAssetQuantityByAssetDefinitionId","titles":["Queries","Asset"]},"745":{"title":"Block","titles":["Queries"]},"746":{"title":"FindAllBlocks","titles":["Queries","Block"]},"747":{"title":"FindAllBlockHeaders","titles":["Queries","Block"]},"748":{"title":"FindBlockHeaderByHash","titles":["Queries","Block"]},"749":{"title":"Domain","titles":["Queries"]},"750":{"title":"FindAllDomains","titles":["Queries","Domain"]},"751":{"title":"FindDomainById","titles":["Queries","Domain"]},"752":{"title":"FindDomainKeyValueByIdAndKey","titles":["Queries","Domain"]},"753":{"title":"Peer","titles":["Queries"]},"754":{"title":"FindAllPeers","titles":["Queries","Peer"]},"755":{"title":"FindAllParameters","titles":["Queries","Peer"]},"756":{"title":"Transaction","titles":["Queries"]},"757":{"title":"FindTransactionsByAccountId","titles":["Queries","Transaction"]},"758":{"title":"FindTransactionByHash","titles":["Queries","Transaction"]},"759":{"title":"Trigger","titles":["Queries"]},"760":{"title":"FindAllActiveTriggerIds","titles":["Queries","Trigger"]},"761":{"title":"FindTriggerById","titles":["Queries","Trigger"]},"762":{"title":"FindTriggerKeyValueByIdAndKey","titles":["Queries","Trigger"]},"763":{"title":"FindTriggersByDomainId","titles":["Queries","Trigger"]},"764":{"title":"API Specification","titles":[]},"765":{"title":"Torii Endpoints","titles":[]},"766":{"title":"API Version","titles":["Torii Endpoints"]},"767":{"title":"Blocks Stream","titles":["Torii Endpoints"]},"768":{"title":"Events","titles":["Torii Endpoints"]},"769":{"title":"Transaction Events","titles":["Torii Endpoints","Events"]},"770":{"title":"Health","titles":["Torii Endpoints"]},"771":{"title":"Metrics","titles":["Torii Endpoints"]},"772":{"title":"Query","titles":["Torii Endpoints"]},"773":{"title":"Account Not Found 404","titles":["Torii Endpoints","Query"]},"774":{"title":"Asset Not Found 404","titles":["Torii Endpoints","Query"]},"775":{"title":"Status","titles":["Torii Endpoints"]},"776":{"title":"Sub-routing","titles":["Torii Endpoints","Status"]},"777":{"title":"Transaction","titles":["Torii Endpoints"]}},"dirtCount":0,"index":[["߀",{"2":{"249":1}}],["ዐ",{"2":{"249":1}}],["о",{"2":{"249":1}}],["θ",{"2":{"249":1}}],["qtwebengine",{"2":{"249":2}}],["qt",{"2":{"249":2}}],["qutebrowser",{"2":{"249":1}}],["quit",{"2":{"212":1}}],["quite",{"2":{"125":1,"168":1,"173":1,"203":1,"221":1,"665":1}}],["quickly",{"2":{"214":1,"258":1,"270":1}}],["quick",{"0":{"213":1},"1":{"214":1},"2":{"88":1,"177":1,"231":1,"286":1}}],["quantities",{"2":{"137":1,"341":1,"744":1}}],["quantity=",{"2":{"171":2,"173":2}}],["quantity",{"2":{"24":1,"25":1,"40":1,"128":4,"164":4,"171":4,"172":2,"190":2,"192":2,"201":4,"202":4,"205":4,"211":3,"220":2,"221":1,"319":1,"320":1,"741":2,"744":1}}],["quadratically",{"2":{"82":1}}],["questions",{"2":{"270":1}}],["question",{"2":{"218":1,"268":1}}],["queried",{"2":{"64":1}}],["queries",{"0":{"54":1,"64":1,"65":1,"99":1,"715":1},"1":{"66":1,"67":1,"68":1,"69":1,"70":1,"716":1,"717":1,"718":1,"719":1,"720":1,"721":1,"722":1,"723":1,"724":1,"725":1,"726":1,"727":1,"728":1,"729":1,"730":1,"731":1,"732":1,"733":1,"734":1,"735":1,"736":1,"737":1,"738":1,"739":1,"740":1,"741":1,"742":1,"743":1,"744":1,"745":1,"746":1,"747":1,"748":1,"749":1,"750":1,"751":1,"752":1,"753":1,"754":1,"755":1,"756":1,"757":1,"758":1,"759":1,"760":1,"761":1,"762":1,"763":1},"2":{"36":1,"39":4,"54":1,"62":1,"64":2,"65":3,"67":1,"69":1,"70":1,"97":1,"99":2,"104":1,"137":5,"178":1,"187":4,"193":1,"204":1,"222":1,"680":1,"694":1,"715":2,"716":4,"725":2,"749":1,"772":4}}],["queryfailed",{"2":{"630":1}}],["queryfilter",{"2":{"199":4,"200":4,"201":4}}],["querypayload",{"0":{"551":1,"576":1},"2":{"581":2}}],["queryexecutionfail",{"0":{"550":1},"2":{"459":1,"630":1,"772":5}}],["queryandextractor",{"2":{"205":12}}],["querybuilder",{"2":{"199":4,"200":2,"201":2,"202":2,"205":12}}],["querybox",{"0":{"549":1},"2":{"66":5,"104":2,"188":4,"192":8,"376":1,"551":1}}],["querying",{"0":{"192":1,"199":1},"2":{"161":1,"199":1,"728":1}}],["query",{"0":{"66":1,"694":1,"772":1},"1":{"773":1,"774":1},"2":{"62":1,"66":7,"67":3,"68":1,"69":2,"83":1,"104":4,"143":2,"164":2,"168":1,"170":1,"171":1,"186":1,"187":5,"188":3,"199":10,"200":3,"201":2,"202":9,"203":2,"205":16,"220":1,"376":1,"444":1,"459":1,"551":1,"669":1,"716":6,"734":1,"741":2,"750":2,"756":1,"760":1,"772":3}}],["queue",{"2":{"28":3,"143":6,"164":6,"212":1,"775":6}}],["queen",{"2":{"24":1,"86":1}}],["quot",{"2":{"0":2,"3":2,"4":8,"6":6,"16":24,"18":336,"20":32,"45":2,"52":16,"53":24,"57":44,"60":4,"66":4,"67":2,"88":2,"92":32,"93":4,"103":20,"104":4,"105":20,"113":96,"117":20,"119":8,"120":8,"121":8,"122":8,"128":540,"143":332,"146":64,"150":4,"153":4,"154":2,"163":2,"164":968,"167":12,"169":8,"170":160,"171":48,"173":12,"174":104,"186":4,"187":8,"192":40,"193":128,"197":52,"198":4,"199":36,"200":12,"201":20,"202":32,"203":8,"205":32,"208":4,"209":4,"210":4,"211":18,"212":20,"216":32,"217":32,"218":12,"219":16,"220":14,"222":8,"239":68,"244":8,"282":8,"283":4,"287":6,"702":16,"703":16,"704":16,"705":12,"706":16,"708":16,"709":16,"710":4,"711":4,"712":16,"713":16,"714":4,"766":4,"770":4,"771":68,"775":36,"776":8}}],["⌃",{"2":{"213":1}}],["⠿",{"2":{"213":8}}],["╰───┴──────────────────────────────╯",{"2":{"193":1}}],["╰───┴──────────────────────────────╯╭───┬──────────────────────────────╮",{"2":{"193":1}}],["╰───┴───────────────────┴──────╯",{"2":{"186":1}}],["╰───┴───────────────────┴──────╯╭───┬───────────────────┬──────╮",{"2":{"186":1}}],["├───┼──────────────────────────────┤",{"2":{"193":2}}],["├───┼───────────────────┼──────┤",{"2":{"186":2}}],["╭───┬──────────────────────────────╮",{"2":{"193":1}}],["╭───┬───────────────────┬──────╮",{"2":{"186":1}}],["│",{"2":{"186":56,"193":54}}],["^5",{"2":{"185":1}}],["^^^^^^^^^^^^^^^^^^^",{"2":{"5":2}}],["937000000",{"2":{"775":2,"776":2}}],["96^",{"2":{"252":1}}],["96",{"2":{"252":2,"253":1}}],["90",{"2":{"202":2,"203":2}}],["9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e",{"2":{"199":2}}],["9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",{"2":{"20":2,"113":2,"164":2,"174":2,"193":2,"217":2}}],["9",{"0":{"195":1,"205":1},"2":{"290":1,"291":1,"339":1,"376":1,"431":1,"443":1,"455":1,"457":1,"459":1,"461":1,"549":1,"634":1}}],["944269f27e1ed8882c6c8c74bd641bc3551ef5651320f4e1e1be11a470b4e3c3",{"2":{"174":2}}],["zsh",{"2":{"244":1}}],["zshrc$",{"2":{"244":1}}],["zshrc",{"2":{"16":1,"244":4}}],["zero",{"2":{"84":1,"406":1,"407":1,"408":1,"409":1,"410":1,"411":1,"412":1,"413":1,"414":1,"415":1,"416":1,"417":1,"432":1}}],["z",{"2":{"67":2,"89":1,"105":4,"108":4,"716":2}}],["−1",{"2":{"43":1}}],["|world|",{"2":{"29":2}}],["||",{"2":{"29":2}}],["||account",{"2":{"29":2}}],["||asset",{"2":{"29":2}}],["||definition",{"2":{"29":2}}],["|domain",{"2":{"29":2}}],["|",{"2":{"29":88,"176":2,"193":2,"213":52}}],["↩︎",{"2":{"28":1,"99":2,"110":3,"141":1,"177":1,"195":1,"777":5}}],["£1",{"2":{"24":4}}],["+inf",{"2":{"771":2}}],["+nightly",{"2":{"108":2}}],["++",{"2":{"29":1}}],["+",{"2":{"20":2,"29":56,"192":8,"198":2,"213":4}}],["yield",{"2":{"273":1}}],["yields",{"2":{"252":1}}],["yubikey",{"2":{"250":1,"268":5}}],["yac",{"2":{"230":2}}],["yarn",{"2":{"186":3}}],["yamlversion",{"2":{"18":1}}],["yaml",{"2":{"18":1,"186":2}}],["year",{"2":{"253":2}}],["years",{"2":{"238":1,"253":2}}],["yes",{"2":{"103":1,"253":1}}],["yet",{"2":{"88":1,"103":1,"166":1,"209":1,"777":1}}],["yellow",{"2":{"52":2}}],["y",{"2":{"39":1,"89":1,"716":5,"773":1,"774":5}}],["yml",{"2":{"18":2,"21":1,"214":2,"216":1,"272":1}}],["you",{"2":{"6":6,"10":1,"11":2,"13":3,"14":4,"16":14,"17":2,"18":9,"19":1,"20":2,"21":3,"22":4,"24":2,"27":1,"28":2,"29":2,"36":6,"39":3,"40":2,"41":8,"42":5,"43":2,"44":1,"47":1,"52":2,"54":1,"58":1,"62":1,"65":1,"67":3,"68":2,"69":3,"72":3,"78":5,"79":1,"80":1,"83":3,"84":2,"85":1,"88":9,"89":3,"90":6,"97":4,"98":1,"99":1,"101":10,"103":2,"104":1,"105":3,"106":3,"108":1,"109":2,"110":1,"114":1,"116":2,"118":1,"119":1,"120":5,"121":1,"123":1,"124":1,"125":1,"128":5,"129":2,"130":1,"131":1,"132":1,"134":2,"135":4,"136":3,"137":14,"138":2,"141":6,"143":2,"144":1,"146":4,"148":2,"150":4,"151":5,"153":3,"154":6,"155":1,"156":2,"160":1,"161":2,"162":1,"163":2,"164":1,"166":12,"167":2,"168":10,"169":2,"170":5,"171":8,"172":1,"174":2,"175":1,"176":8,"177":2,"178":8,"180":3,"181":1,"182":2,"183":6,"185":1,"186":16,"187":11,"188":3,"189":8,"190":2,"191":1,"193":4,"195":1,"197":7,"198":3,"199":1,"200":1,"207":4,"208":5,"209":2,"210":8,"211":3,"212":1,"213":2,"214":3,"216":12,"217":5,"218":3,"219":8,"220":6,"221":1,"222":1,"223":1,"224":4,"225":2,"227":1,"228":1,"232":1,"233":2,"234":1,"237":1,"238":3,"242":1,"244":2,"245":1,"246":1,"248":1,"249":6,"250":2,"252":1,"253":3,"254":1,"256":4,"258":3,"259":2,"261":3,"263":4,"265":3,"268":1,"269":2,"270":4,"271":2,"272":11,"273":6,"274":1,"277":1,"278":1,"279":5,"280":5,"281":2,"282":7,"283":4,"284":1,"285":2,"286":2,"665":6,"667":2,"669":1,"699":2,"715":2,"716":3,"717":1,"722":1,"729":1,"733":3,"734":2,"764":1}}],["yourself",{"2":{"178":1,"189":1,"210":1,"216":1,"218":1,"219":1}}],["your",{"2":{"6":2,"10":2,"16":5,"17":1,"18":1,"22":4,"42":1,"65":1,"79":1,"82":1,"84":1,"88":7,"89":2,"100":1,"101":1,"103":1,"106":2,"109":2,"110":1,"128":1,"132":2,"135":2,"136":1,"137":4,"138":1,"141":2,"143":1,"154":1,"157":1,"161":1,"162":1,"168":1,"170":2,"176":1,"186":1,"197":3,"199":1,"200":1,"201":2,"207":1,"211":1,"212":1,"213":1,"216":3,"217":3,"218":1,"223":1,"230":1,"237":1,"238":1,"242":1,"244":2,"245":1,"246":3,"247":2,"248":8,"249":9,"250":4,"251":1,"252":3,"253":2,"254":2,"255":2,"258":4,"259":1,"260":1,"261":4,"262":2,"263":10,"264":2,"265":2,"268":3,"270":1,"272":1,"278":1,"279":1,"280":1,"282":3,"283":4,"665":2,"667":1}}],["3rd",{"2":{"693":1}}],["38",{"2":{"549":1}}],["37",{"2":{"549":1,"775":2}}],["36",{"2":{"549":1}}],["35",{"2":{"549":1}}],["35940",{"2":{"252":1}}],["30",{"2":{"549":1}}],["300∗109300",{"2":{"253":1}}],["300",{"2":{"253":1}}],["30000",{"2":{"143":2,"164":2}}],["31",{"2":{"253":1,"549":1,"775":2}}],["33",{"2":{"230":1,"549":1,"677":1}}],["34",{"2":{"220":3,"549":1}}],["32768",{"2":{"143":2,"164":2}}],["32",{"0":{"304":1},"2":{"25":1,"84":1,"101":1,"211":1,"273":1,"304":1,"448":1,"549":1}}],["3bac34cda9e3763fa069c1198312d1ec73b53023b8180c822ac355435edc4a24cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1",{"2":{"18":2}}],["3",{"0":{"94":1,"136":1,"162":1,"169":1,"188":1,"199":1,"206":1,"209":1,"218":1,"243":1},"1":{"207":1,"208":1,"209":1,"210":1,"211":1,"212":1,"244":1},"2":{"18":2,"108":1,"186":2,"192":4,"193":4,"197":4,"290":1,"291":1,"299":1,"309":1,"310":1,"315":1,"316":1,"319":1,"320":1,"333":1,"338":1,"339":1,"342":1,"343":1,"367":1,"368":1,"376":1,"378":1,"431":1,"445":1,"454":1,"455":1,"457":1,"458":1,"459":1,"461":1,"473":1,"476":1,"483":1,"504":1,"549":1,"550":1,"555":1,"600":1,"610":1,"620":1,"621":1,"625":1,"630":1,"634":1,"636":1,"775":3}}],["3f+1",{"2":{"18":1,"146":1}}],["39",{"2":{"2":12,"5":32,"18":136,"20":4,"52":4,"53":4,"103":4,"146":4,"167":4,"176":4,"186":8,"187":100,"188":68,"189":32,"190":84,"191":48,"192":68,"193":224,"195":16,"219":4,"220":4,"244":8,"549":1,"710":2,"711":2}}],["662",{"2":{"253":3}}],["698491z",{"2":{"213":2}}],["65",{"2":{"197":2}}],["65536",{"2":{"143":4,"164":4}}],["602",{"2":{"252":1}}],["608",{"2":{"252":1,"253":3}}],["60",{"2":{"176":1,"279":4}}],["60000",{"2":{"143":2,"164":2}}],["6",{"0":{"172":1,"191":1,"202":1,"212":1,"221":1},"2":{"16":1,"193":2,"197":6,"253":1,"290":1,"291":1,"309":1,"310":1,"338":1,"339":1,"376":1,"431":1,"454":1,"455":1,"457":1,"459":1,"461":1,"476":1,"549":1,"555":1,"634":1,"636":1}}],["647",{"2":{"252":1}}],["64",{"2":{"6":5,"25":1,"252":1,"273":1,"775":1}}],["xor",{"2":{"704":10,"705":6,"706":10,"708":14,"709":14,"712":14,"713":14}}],["xkcd",{"2":{"253":2,"269":1,"278":1}}],["xdg",{"2":{"166":1,"217":1}}],["x",{"2":{"16":2,"39":1,"89":1,"188":8,"192":12,"716":5,"775":2}}],["x86",{"2":{"6":5}}],["~",{"2":{"16":10,"17":8,"22":2,"166":1,"168":2,"177":2,"183":5,"207":4,"216":11,"243":2,"244":10,"280":1,"282":4}}],["29",{"2":{"549":1}}],["292",{"2":{"252":1}}],["28",{"2":{"549":1}}],["282ed9f3cf92811c3818dbc4ae594ed59dc1a2f78e4241e31924e101d6b1fb831c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b",{"2":{"18":2}}],["23",{"2":{"549":1}}],["23000000",{"2":{"143":2,"164":2}}],["22",{"2":{"549":1,"634":1}}],["21",{"2":{"549":1,"634":1}}],["216499",{"2":{"11":2}}],["25",{"2":{"269":1,"549":1,"771":2}}],["2fa",{"2":{"259":2}}],["27",{"2":{"549":1}}],["270",{"2":{"253":3}}],["27t12",{"2":{"213":52}}],["249",{"2":{"253":3}}],["24",{"2":{"253":1,"549":1}}],["248",{"2":{"252":1}}],["269",{"2":{"252":1}}],["26^",{"2":{"252":1}}],["26",{"2":{"252":2,"253":1,"549":1,"771":2}}],["2669bb1099477b970e1d7d7c54e345a64a54213fcfba2465cbcd6d4e5091a71db678073cfae6e247a58b442661c7da0e13bac5031cbc6343ef566b8718d47d04",{"2":{"239":10}}],["20",{"2":{"549":1,"634":1}}],["200∗103200",{"2":{"253":1}}],["200",{"2":{"253":1,"766":1,"770":1,"771":1,"772":1,"777":1}}],["2000",{"2":{"143":2,"164":2}}],["2080",{"2":{"253":1}}],["20703",{"2":{"252":1}}],["2048",{"2":{"239":2}}],["2022",{"2":{"279":4}}],["2023",{"2":{"213":52}}],["2021",{"2":{"103":2,"177":1}}],["2",{"0":{"93":1,"104":1,"135":1,"161":1,"167":1,"168":2,"175":1,"179":1,"187":1,"197":1,"198":2,"207":1,"208":2,"216":1,"217":2,"227":1,"229":1,"242":1},"1":{"176":1,"177":1,"180":1,"181":1,"182":1,"183":1,"184":1,"228":1,"230":1,"231":1,"232":1,"233":1,"234":1,"235":1},"2":{"13":1,"16":1,"17":1,"28":1,"52":1,"55":1,"62":1,"63":1,"69":1,"72":1,"89":1,"107":1,"132":1,"134":1,"158":1,"164":1,"167":1,"168":1,"170":1,"171":1,"174":1,"175":2,"176":2,"177":1,"178":6,"179":1,"183":1,"184":1,"185":2,"186":5,"190":1,"193":5,"197":5,"198":2,"199":1,"201":1,"204":1,"205":2,"207":2,"213":8,"216":4,"218":1,"222":1,"223":2,"225":2,"226":1,"228":2,"229":4,"230":5,"231":1,"232":2,"233":3,"234":1,"235":1,"237":2,"241":1,"245":1,"246":2,"247":2,"249":1,"252":5,"262":2,"270":1,"271":1,"274":1,"277":1,"284":1,"290":1,"291":1,"299":1,"309":1,"310":1,"315":1,"316":1,"319":1,"320":1,"332":1,"333":1,"338":1,"339":1,"342":1,"343":1,"367":1,"368":1,"376":1,"378":1,"431":1,"445":1,"454":1,"455":1,"457":1,"458":1,"459":1,"461":1,"473":1,"476":1,"483":1,"486":1,"504":1,"546":1,"547":1,"549":1,"550":1,"555":1,"561":1,"562":1,"569":1,"584":1,"600":1,"610":1,"620":1,"621":1,"625":1,"626":1,"630":1,"634":1,"636":1,"698":1,"699":1,"700":1,"701":2,"707":4,"717":1,"771":8,"772":1,"775":2}}],["57",{"2":{"279":2}}],["572457z",{"2":{"213":2}}],["5c",{"2":{"268":4}}],["52∗103052",{"2":{"252":1}}],["52",{"2":{"252":2}}],["524288000",{"2":{"143":2,"164":2}}],["54572974",{"2":{"771":2}}],["549",{"2":{"253":3}}],["540",{"2":{"253":3}}],["540651z",{"2":{"213":2}}],["543188z",{"2":{"213":2}}],["542906z",{"2":{"213":2}}],["542379z",{"2":{"213":2}}],["5$",{"2":{"172":1}}],["5000",{"2":{"707":4}}],["500",{"2":{"143":2,"164":2}}],["512",{"2":{"143":2,"164":2}}],["569289z",{"2":{"213":2}}],["56",{"2":{"88":2}}],["594190z",{"2":{"213":2}}],["59",{"2":{"88":1}}],["551356z",{"2":{"213":2}}],["55",{"2":{"88":2}}],["5",{"0":{"96":1,"171":1,"190":1,"201":1,"211":1,"220":1},"2":{"11":2,"172":1,"186":2,"192":2,"193":2,"205":4,"290":1,"291":1,"309":1,"310":1,"315":1,"316":1,"338":1,"339":1,"342":1,"343":1,"376":1,"431":1,"454":1,"455":1,"457":1,"459":1,"461":1,"476":1,"549":1,"555":1,"610":1,"634":1,"636":1,"771":6,"775":4,"776":2}}],["7c",{"2":{"775":4}}],["7737e0b5c",{"2":{"279":2}}],["776",{"2":{"252":1}}],["7687b1433fb6731e6dc635a376b3eb3b5fcd1e02c9775c1642e7fd5da035ec75",{"2":{"239":2}}],["763216z",{"2":{"213":2}}],["760437z",{"2":{"213":2}}],["755802z",{"2":{"213":2}}],["755503z",{"2":{"213":2}}],["754934z",{"2":{"213":2}}],["754613z",{"2":{"213":2}}],["753230z",{"2":{"213":2}}],["742",{"2":{"252":1}}],["749155z",{"2":{"213":2}}],["748879z",{"2":{"213":2}}],["746975z",{"2":{"213":2}}],["730201z",{"2":{"213":2}}],["722029z",{"2":{"213":2}}],["7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",{"2":{"199":2}}],["719683z",{"2":{"213":2}}],["707759z",{"2":{"213":2}}],["701895z",{"2":{"213":2}}],["701624z",{"2":{"213":2}}],["700998z",{"2":{"213":2}}],["7",{"0":{"173":1,"192":1,"203":1,"222":1},"2":{"11":2,"18":1,"146":1,"193":2,"290":1,"291":1,"339":1,"376":1,"431":1,"454":1,"455":1,"457":1,"459":1,"461":1,"476":1,"549":1,"634":1,"636":1}}],["887",{"2":{"253":3}}],["835",{"2":{"253":3}}],["83",{"2":{"253":1}}],["834",{"2":{"253":2}}],["896",{"2":{"252":1}}],["899",{"2":{"252":1}}],["874",{"2":{"252":1}}],["80",{"2":{"203":2,"235":1}}],["808x",{"2":{"18":1}}],["8083",{"2":{"18":6}}],["8082",{"2":{"18":6}}],["8081",{"2":{"18":6,"187":5,"193":2}}],["8080",{"2":{"11":2,"18":6,"20":2,"113":2,"119":2,"148":1,"164":2,"170":2,"174":2,"187":7,"193":2,"199":2}}],["86400000",{"2":{"143":2,"164":2}}],["818x",{"2":{"18":1}}],["8183",{"2":{"18":6}}],["8182",{"2":{"18":6}}],["8181",{"2":{"18":6}}],["8180",{"2":{"11":1,"18":6,"20":2,"113":2,"120":4,"150":3,"164":2,"170":2,"174":2,"199":2}}],["8",{"0":{"174":1,"193":1,"204":1,"301":1,"303":1},"1":{"194":1},"2":{"18":2,"253":1,"273":1,"290":1,"291":1,"301":1,"303":1,"339":1,"376":1,"431":1,"454":1,"455":1,"457":1,"459":1,"461":1,"468":1,"469":1,"549":1,"634":1,"699":1,"771":1}}],["0c",{"2":{"775":2}}],["04",{"2":{"279":4}}],["040",{"2":{"252":1}}],["01",{"2":{"279":2,"771":2}}],["015",{"2":{"252":1}}],["06",{"2":{"220":1}}],["08",{"2":{"213":52,"775":2}}],["025",{"2":{"771":2}}],["02",{"2":{"213":52}}],["0s",{"2":{"213":8}}],["0n",{"2":{"195":2}}],["037",{"2":{"252":1}}],["03",{"2":{"88":3,"279":2}}],["038ae16b219da35aa036335ed0a43c28a2cc737150112c78a7b8034b9d99c9023f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255",{"2":{"18":2}}],["059",{"2":{"252":1}}],["05",{"2":{"88":2,"771":2}}],["005",{"2":{"771":2}}],["00",{"2":{"88":2}}],["00031",{"2":{"253":2}}],["0000000000000000000000000000000035d9120a174e35e966dd92de90b2446d4b060c8b72018b3917a1c97d7e93eaec",{"2":{"239":2}}],["000",{"2":{"53":2,"61":2,"71":2,"253":1}}],["0−1",{"2":{"43":1}}],["0",{"0":{"166":1},"2":{"11":6,"18":4,"20":14,"21":2,"43":2,"53":2,"84":1,"92":8,"103":4,"113":8,"119":4,"120":8,"150":6,"164":8,"168":4,"170":8,"174":10,"185":3,"186":2,"187":24,"193":12,"197":12,"198":2,"199":8,"202":2,"213":12,"216":18,"249":1,"279":4,"290":1,"291":1,"299":1,"309":1,"310":1,"315":1,"316":1,"319":1,"320":1,"328":1,"332":1,"333":1,"338":1,"339":1,"342":1,"343":1,"367":1,"368":1,"371":1,"375":1,"376":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"431":1,"445":1,"453":1,"454":1,"455":1,"457":1,"458":1,"459":1,"461":1,"464":1,"473":1,"476":1,"483":1,"485":1,"486":1,"502":1,"503":1,"504":1,"505":1,"534":1,"535":1,"542":1,"545":1,"546":1,"547":1,"549":1,"550":1,"555":1,"557":1,"561":1,"562":1,"569":1,"574":1,"584":1,"600":1,"610":1,"615":1,"618":1,"619":1,"620":1,"621":1,"625":1,"626":1,"628":1,"630":1,"632":1,"633":1,"634":1,"636":1,"666":2,"699":1,"702":2,"703":2,"771":42}}],["48",{"2":{"775":1}}],["4810",{"2":{"775":1}}],["456",{"2":{"253":1}}],["4568",{"2":{"253":2}}],["466",{"2":{"252":1}}],["43∗102143",{"2":{"252":1}}],["43",{"2":{"213":52,"252":2}}],["428",{"2":{"252":1}}],["42",{"2":{"190":2,"211":3,"220":2}}],["404",{"0":{"773":1,"774":1},"2":{"772":1}}],["403",{"2":{"772":1}}],["401",{"2":{"772":1,"777":1}}],["400",{"2":{"772":2,"777":1}}],["4000",{"2":{"143":2,"164":2}}],["40",{"2":{"549":1,"775":2}}],["4090",{"2":{"253":3}}],["4096",{"2":{"113":2,"121":2,"128":8,"143":10,"164":20,"170":2,"174":2}}],["44",{"2":{"128":2,"164":2}}],["4194304",{"2":{"113":2,"121":2,"128":2,"143":2,"164":6,"170":2}}],["4",{"0":{"95":1,"170":1,"189":1,"200":1,"210":1,"219":1,"302":1,"305":1},"2":{"6":1,"18":2,"88":1,"143":4,"146":1,"164":4,"186":2,"193":2,"213":3,"214":1,"220":1,"253":1,"272":1,"290":1,"291":1,"302":1,"305":1,"309":1,"310":1,"315":1,"316":1,"333":1,"338":1,"339":1,"342":1,"343":1,"368":1,"376":1,"378":1,"431":1,"454":1,"455":1,"457":1,"459":1,"461":1,"466":1,"467":1,"473":1,"476":1,"483":1,"549":1,"550":1,"555":1,"610":1,"630":1,"634":1,"636":1,"766":1,"775":2,"777":1}}],["`websocket",{"2":{"767":1}}],["`window`",{"2":{"187":2}}],["`xor",{"2":{"705":4}}],["`rose",{"2":{"702":2,"703":2}}],["`iroha",{"2":{"219":2}}],["```",{"2":{"201":1}}],["```domain",{"2":{"201":1}}],["`alice",{"2":{"704":4,"706":4,"708":4,"709":4,"712":4,"713":4}}],["`at",{"2":{"192":2}}],["`asset",{"2":{"192":2}}],["`account",{"2":{"192":2}}],["`http",{"2":{"193":4}}],["`https",{"2":{"5":2}}],["`has",{"2":{"192":4}}],["`domain",{"2":{"192":2}}],["`versionedsignedtransaction`",{"2":{"187":2}}],["`executable`",{"2":{"187":2}}],["`canregisterdomains`",{"2":{"714":2}}],["`canremovekeyvalueinassetdefinition`",{"2":{"713":2}}],["`canremovekeyvalueinusermetadata`",{"2":{"711":2}}],["`canremovekeyvalueinuserassets`",{"2":{"709":2}}],["`cansetkeyvalueinassetdefinition`",{"2":{"712":2}}],["`cansetkeyvalueinusermetadata`",{"2":{"710":2}}],["`cansetkeyvalueinuserassets`",{"2":{"708":2}}],["`cantransferonlyfixednumberoftimesperperiod`",{"2":{"707":2}}],["`cantransferuserassets`",{"2":{"706":2}}],["`canunregisterassetwithdefinition`",{"2":{"705":2}}],["`canburnuserassets`",{"2":{"704":2}}],["`canburnassetwithdefinition`",{"2":{"703":2}}],["`canmintuserassetdefinitions`",{"2":{"702":2}}],["`cargo",{"2":{"6":1}}],["`client`",{"2":{"187":2}}],["`",{"2":{"187":2,"192":10,"217":6}}],["`typeerror",{"2":{"187":2}}],["`typical",{"2":{"141":1}}],["`fetch`",{"2":{"187":2}}],["`undicifetch`",{"2":{"187":2}}],["`store`",{"2":{"52":2}}],["vpn",{"2":{"249":1,"258":2}}],["vulnerabilities",{"0":{"253":1},"2":{"246":1,"248":2,"249":1,"260":2}}],["vulnerability",{"2":{"234":2,"268":2}}],["vueuse",{"2":{"193":2}}],["vuemain",{"2":{"193":1}}],["vueapp",{"2":{"193":1}}],["vuecomponents",{"2":{"193":2}}],["vue",{"2":{"193":31}}],["v2",{"2":{"216":1,"248":1}}],["v1",{"2":{"176":1,"195":2,"648":1,"649":1,"650":1,"651":1,"652":1}}],["v0",{"2":{"174":2}}],["vs",{"0":{"229":1},"1":{"230":1,"231":1,"232":1,"233":1,"234":1,"235":1},"2":{"105":2,"273":1}}],["void",{"2":{"205":12}}],["voting",{"2":{"28":2}}],["volumes",{"2":{"18":8}}],["vary",{"2":{"674":1}}],["varying",{"2":{"287":1}}],["var",{"2":{"198":26}}],["variety",{"2":{"65":1,"86":1,"94":1,"134":1,"138":1,"249":1,"264":1}}],["variations",{"2":{"238":1}}],["variants",{"2":{"241":1,"290":1,"291":1,"299":1,"309":1,"310":1,"315":1,"316":1,"319":1,"320":1,"328":1,"332":1,"333":1,"338":1,"339":1,"342":1,"343":1,"367":1,"368":1,"371":1,"375":1,"376":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"431":1,"445":1,"453":1,"454":1,"455":1,"457":1,"458":1,"459":1,"461":1,"464":1,"473":1,"476":1,"483":1,"485":1,"486":1,"502":1,"503":1,"504":1,"505":1,"534":1,"535":1,"542":1,"545":1,"546":1,"547":1,"549":1,"550":1,"555":1,"557":1,"561":1,"562":1,"569":1,"574":1,"584":1,"600":1,"610":1,"615":1,"618":1,"619":1,"620":1,"621":1,"625":1,"626":1,"628":1,"630":1,"632":1,"633":1,"634":1,"636":1,"648":1,"649":1,"650":1,"651":1,"652":1}}],["variant",{"2":{"38":1,"89":1,"94":1,"218":1,"238":1,"244":1,"290":2,"291":2,"299":2,"309":2,"310":2,"315":2,"316":2,"319":2,"320":2,"328":2,"332":2,"333":2,"338":2,"339":2,"342":2,"343":2,"367":2,"368":2,"371":2,"375":2,"376":2,"378":2,"379":2,"380":2,"381":2,"382":2,"383":2,"384":2,"385":2,"386":2,"387":2,"388":2,"389":2,"390":2,"391":2,"392":2,"393":2,"394":2,"395":2,"396":2,"397":2,"398":2,"399":2,"400":2,"431":2,"445":2,"453":2,"454":2,"455":2,"457":2,"458":2,"459":2,"461":2,"464":2,"473":2,"476":2,"483":2,"485":2,"486":2,"502":2,"503":2,"504":2,"505":2,"534":2,"535":2,"542":2,"545":2,"546":2,"547":2,"549":2,"550":2,"555":2,"557":2,"561":2,"562":2,"569":2,"574":2,"584":2,"600":2,"610":2,"615":2,"618":2,"619":2,"620":2,"621":2,"625":2,"626":2,"628":2,"630":2,"632":2,"633":2,"634":2,"636":2,"648":2,"649":2,"650":2,"651":2,"652":2}}],["variable",{"2":{"16":1,"126":2,"127":1,"146":1,"166":1,"244":4,"256":1,"283":1}}],["variables",{"0":{"9":1,"16":1,"20":1},"2":{"6":1,"9":1,"13":2,"16":1,"125":4,"135":2,"151":1,"166":1,"198":1,"200":1,"286":1,"716":1}}],["various",{"2":{"13":1,"29":1,"56":1,"57":1,"94":2,"129":1,"246":1,"249":1}}],["vast",{"2":{"86":1}}],["valuable",{"2":{"672":1,"674":1}}],["valuing",{"2":{"249":1}}],["valueofkey",{"0":{"635":1},"2":{"333":2}}],["valuetype",{"2":{"211":2}}],["valuepredicate",{"0":{"445":1,"498":1,"636":1,"638":1},"2":{"199":4,"200":2,"201":2,"321":1,"333":2,"445":4,"498":1,"551":1,"635":1,"638":1}}],["values",{"0":{"127":1},"2":{"25":1,"50":1,"55":1,"57":2,"62":2,"122":1,"124":1,"125":3,"127":2,"137":1,"143":1,"190":1,"346":1,"656":1,"719":1}}],["value",{"0":{"25":1,"322":1,"364":1,"365":1,"489":1,"592":1,"593":1,"634":1,"644":1,"648":1},"2":{"25":1,"46":1,"50":1,"51":1,"52":3,"53":8,"54":1,"92":8,"93":2,"97":2,"125":4,"128":10,"134":1,"137":3,"164":10,"168":1,"170":20,"171":6,"188":4,"189":4,"190":15,"191":6,"192":2,"193":20,"199":6,"200":2,"201":7,"202":10,"203":4,"205":50,"209":3,"210":2,"211":14,"220":5,"221":2,"238":1,"252":1,"272":1,"289":1,"290":1,"291":1,"292":1,"293":1,"294":1,"295":1,"296":1,"297":1,"298":1,"299":1,"300":1,"301":1,"302":1,"303":1,"304":1,"305":1,"306":2,"307":1,"308":2,"309":1,"310":1,"311":1,"312":1,"313":1,"314":1,"315":1,"316":1,"317":1,"318":1,"319":1,"320":1,"321":1,"322":2,"323":1,"324":1,"325":1,"327":1,"328":1,"330":2,"331":1,"332":1,"333":1,"334":3,"335":3,"336":3,"337":2,"338":1,"339":1,"340":1,"341":1,"342":1,"343":1,"344":1,"345":1,"347":3,"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"367":1,"368":1,"371":1,"372":1,"373":1,"374":1,"375":1,"376":2,"377":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"401":1,"402":1,"403":1,"404":1,"405":1,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":1,"425":1,"426":1,"427":1,"428":1,"429":1,"430":1,"431":1,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":1,"441":1,"444":1,"445":1,"446":2,"447":1,"453":1,"454":1,"455":1,"456":3,"457":1,"458":1,"459":1,"460":1,"461":1,"462":1,"463":1,"464":1,"470":1,"471":1,"472":1,"473":1,"474":1,"475":1,"476":1,"477":1,"478":2,"479":3,"480":3,"481":3,"482":3,"483":1,"484":2,"485":1,"486":1,"487":1,"488":1,"489":3,"490":1,"491":1,"493":1,"494":2,"495":1,"496":1,"497":1,"501":1,"502":1,"503":1,"504":1,"505":1,"522":1,"530":1,"531":2,"532":1,"533":1,"534":1,"535":1,"536":1,"537":1,"538":1,"539":1,"540":1,"541":1,"542":1,"543":1,"544":1,"545":1,"546":1,"547":1,"548":1,"549":1,"550":1,"551":1,"552":1,"553":1,"554":1,"555":1,"556":1,"557":1,"558":1,"559":2,"560":1,"561":1,"562":1,"563":1,"564":1,"565":1,"566":1,"567":1,"568":1,"569":1,"570":1,"571":3,"572":1,"573":1,"574":1,"578":1,"579":1,"580":1,"581":1,"582":1,"583":1,"584":1,"585":1,"586":1,"587":1,"588":1,"589":1,"590":1,"591":1,"592":2,"593":2,"594":1,"595":1,"596":1,"597":1,"598":1,"600":1,"602":1,"603":1,"605":1,"606":1,"607":1,"608":2,"609":1,"610":1,"611":2,"612":2,"613":1,"614":1,"615":1,"616":1,"617":1,"618":1,"619":1,"620":1,"621":1,"622":1,"623":1,"624":1,"625":1,"626":2,"627":1,"628":1,"629":1,"630":1,"631":1,"632":1,"633":1,"634":2,"635":1,"636":1,"637":1,"638":1,"639":1,"640":1,"641":1,"642":1,"643":1,"644":2,"645":1,"646":1,"647":1,"648":2,"649":1,"650":1,"651":1,"652":1,"653":1,"654":1,"656":3,"666":4,"673":1,"674":2,"701":6,"708":11,"709":11,"710":7,"711":7,"712":11,"713":11,"716":1,"728":2,"732":1,"733":2,"734":1,"742":2,"743":2,"744":1,"752":2,"762":1,"772":1,"776":2}}],["val",{"2":{"197":2,"198":6,"199":29,"200":3,"201":2,"202":5,"204":4,"531":1}}],["validity",{"2":{"178":1,"255":1,"665":1,"670":1}}],["valid",{"2":{"53":2,"63":1,"219":2,"220":2,"238":1,"273":1}}],["validate",{"2":{"63":1,"171":1,"683":1}}],["validatormode",{"0":{"633":1},"2":{"553":1}}],["validatorevent",{"0":{"632":1},"2":{"339":1}}],["validator",{"0":{"631":1},"2":{"62":2,"63":2,"128":4,"141":1,"164":4,"339":1,"553":1,"628":2,"633":1,"634":2,"671":1,"699":1}}],["validators",{"0":{"62":1,"63":1},"1":{"63":1},"2":{"33":1,"62":4,"63":6,"89":1,"111":1,"141":2}}],["validationfail",{"0":{"630":1},"2":{"367":1,"610":1}}],["validations",{"2":{"174":1}}],["validation",{"2":{"32":1,"63":2,"367":1,"610":1,"753":1,"769":1}}],["validating",{"2":{"32":1,"174":4,"193":4,"546":1,"547":1,"769":4}}],["v",{"2":{"20":1,"168":2,"193":6,"279":2}}],["vfr",{"2":{"17":4,"207":2,"216":2}}],["viruses",{"2":{"258":1}}],["virtual",{"2":{"258":1,"263":1}}],["victim",{"2":{"253":1}}],["vivaldi",{"2":{"249":1}}],["violations",{"2":{"249":1}}],["vigilant",{"2":{"248":1}}],["vital",{"2":{"237":1}}],["vite",{"2":{"193":2}}],["vitepress",{"2":{"4":1}}],["visit",{"2":{"197":1}}],["visible",{"2":{"169":1}}],["visualising",{"0":{"222":1},"2":{"193":1,"204":1,"222":1}}],["visualize",{"2":{"178":1,"209":1,"225":1}}],["visualizing",{"0":{"174":1,"193":1,"204":1,"212":1},"1":{"194":1}}],["viewing",{"2":{"670":1}}],["views",{"2":{"193":1}}],["viewer",{"2":{"154":1}}],["view",{"0":{"112":1,"695":1,"696":1},"2":{"28":4,"34":1,"65":1,"69":1,"98":1,"112":1,"174":1,"325":1,"669":2,"692":1,"694":2,"697":1,"750":1,"755":1,"771":8,"775":6}}],["via",{"2":{"16":1,"36":1,"39":1,"45":1,"50":1,"56":1,"71":1,"97":1,"125":1,"126":1,"127":2,"151":1,"176":1,"186":1,"195":1,"216":2,"227":1,"236":1,"248":1,"263":1,"268":1,"271":1,"274":1,"277":1,"284":1,"285":1,"286":1,"667":2,"689":1,"697":1,"767":1}}],["vet",{"2":{"231":1}}],["vecpublickey",{"2":{"189":4}}],["vecinstruction",{"2":{"188":4,"193":4}}],["vec",{"0":{"323":1,"365":1,"637":1,"638":1,"639":1,"640":1,"641":1,"642":1,"643":1,"644":1,"645":2,"646":1,"647":1,"649":1},"2":{"53":4,"61":2,"67":1,"71":2,"72":1,"76":1,"92":2,"93":2,"104":10,"107":2,"188":2,"192":6,"218":4,"323":1,"325":1,"327":2,"334":1,"335":2,"336":2,"371":1,"477":1,"498":1,"505":1,"540":1,"548":1,"553":2,"570":1,"573":1,"574":2,"594":1,"595":1,"596":1,"597":1,"598":1,"634":2,"637":1,"638":1,"639":1,"640":1,"641":1,"642":1,"643":1,"644":1,"645":2,"646":1,"647":1,"649":1,"654":1,"655":1,"667":1,"716":3,"718":1,"719":1,"720":1,"721":1,"723":1,"724":1,"726":1,"729":1,"730":1,"731":1,"733":1,"734":1,"736":1,"737":1,"738":1,"739":1,"740":1,"746":1,"747":1,"750":1,"754":1,"755":1,"757":1,"760":1,"763":1}}],["vectorization",{"2":{"105":2}}],["vectors",{"2":{"50":1}}],["vector",{"2":{"49":1}}],["versatile",{"2":{"268":1}}],["versioning",{"0":{"686":1},"2":{"669":1}}],["versionedrejectedtransaction",{"2":{"757":2}}],["versionedcommittedblock",{"2":{"746":1}}],["versionedbatchedresponse",{"0":{"648":1,"649":1},"2":{"772":1}}],["versionedsignedquery",{"0":{"651":1},"2":{"772":1}}],["versionedsignedqueryrequest",{"2":{"187":4}}],["versionedsignedblock",{"0":{"352":1,"450":1,"510":1,"650":1},"2":{"325":1,"326":1,"428":1,"431":1,"453":1,"510":1,"609":1,"634":1}}],["versionedsignedtransaction",{"0":{"323":1,"353":1,"449":1,"451":1,"477":1,"509":1,"646":1,"649":1,"652":1},"2":{"205":20,"323":1,"325":1,"431":1,"437":1,"453":1,"477":1,"509":1,"611":1,"646":1,"649":1,"757":2,"777":1}}],["versionpython",{"2":{"283":1}}],["versions",{"0":{"282":1},"2":{"21":1,"127":1,"178":1,"180":2,"197":1,"207":1,"225":1,"229":1,"272":1,"279":3,"282":2,"686":1}}],["version",{"0":{"180":1,"279":1,"281":1,"766":1},"2":{"6":1,"55":1,"103":2,"161":1,"168":5,"179":1,"180":7,"185":1,"197":4,"207":2,"216":8,"229":1,"230":1,"242":1,"248":1,"249":5,"272":2,"279":4,"280":2,"281":2,"282":1,"283":7,"686":1,"764":1,"766":3}}],["verified",{"2":{"217":2}}],["verification",{"2":{"208":1,"670":1}}],["verifying",{"2":{"255":1}}],["verify",{"2":{"170":1,"248":1,"249":1,"255":1,"256":2,"259":2,"273":1,"683":1}}],["verbose",{"2":{"100":1,"120":1,"150":1}}],["verdict",{"2":{"62":6,"63":2}}],["verdicts",{"2":{"62":2}}],["very",{"2":{"18":1,"42":1,"50":1,"100":1,"104":1,"166":1,"187":1,"220":1,"229":1,"233":1,"665":1}}],["ve",{"2":{"18":1,"40":2,"65":1,"91":1,"94":1,"101":1,"107":1,"135":1,"166":1,"176":1,"186":2,"193":1,"216":1,"273":1}}],["vendored",{"2":{"6":3,"234":1}}],["$$",{"2":{"252":2}}],["$$entropy=log",{"2":{"252":2}}],["$it",{"2":{"202":4,"203":2}}],["$whiterabbitasset",{"2":{"202":6}}],["$whiterabbit",{"2":{"202":2}}],["$madhatterasset",{"2":{"201":2,"202":4,"203":2}}],["$madhatter",{"2":{"200":2,"202":2}}],["$assetdefinition$asset",{"2":{"201":2,"202":2}}],["$assetdefinition",{"2":{"201":2}}],["$asset",{"2":{"201":2}}],["$account",{"2":{"200":2,"202":2}}],["$alias",{"2":{"198":2}}],["$domain",{"2":{"199":2}}],["$default",{"2":{"198":2}}],["$path",{"2":{"16":4}}],["$",{"2":{"5":2,"6":2,"7":1,"16":23,"17":1,"20":8,"74":1,"83":1,"167":2,"169":1,"170":4,"171":1,"173":1,"186":14,"192":22,"193":4,"199":4,"200":4,"201":4,"202":2,"203":2,"207":6,"239":14,"244":13,"279":6,"280":2,"282":6,"699":1}}],["=105",{"2":{"252":1}}],["=75",{"2":{"252":1}}],["=log",{"2":{"252":2}}],["=2",{"2":{"216":8}}],["=https",{"2":{"176":2}}],["===",{"2":{"188":2}}],["==",{"2":{"94":1,"212":4}}],["=",{"2":{"5":6,"52":12,"53":24,"60":6,"61":4,"66":6,"67":6,"71":4,"92":8,"93":12,"94":6,"95":4,"103":14,"104":6,"105":10,"187":14,"188":10,"189":6,"190":6,"191":16,"192":24,"193":40,"195":6,"197":6,"198":32,"199":32,"200":14,"201":22,"202":10,"203":4,"204":2,"205":82,"208":4,"209":6,"210":8,"211":8,"212":4,"216":24,"217":16,"218":10,"219":8,"220":8,"221":2,"222":6,"666":2,"702":8,"703":8,"704":10,"705":10,"706":10,"707":6,"708":10,"709":10,"710":4,"711":4,"712":10,"713":10,"714":8,"716":1}}],["k8s",{"2":{"286":1}}],["kubernetes",{"0":{"276":1},"2":{"671":1}}],["kura",{"0":{"155":1,"681":1},"2":{"6":1,"143":2,"155":2,"164":2,"213":4,"669":1}}],["kde",{"2":{"249":1}}],["kp",{"2":{"217":4}}],["kwargs",{"2":{"211":1}}],["kt",{"2":{"199":1,"200":1}}],["kts",{"2":{"197":1}}],["kanji",{"2":{"238":1}}],["kakuyaku",{"2":{"193":4}}],["kagami$",{"2":{"241":1}}],["kagami",{"0":{"238":1,"240":1,"241":1,"242":1,"243":1,"682":1},"1":{"239":1,"241":1,"242":1,"243":1,"244":2},"2":{"114":3,"130":7,"134":6,"144":3,"170":10,"237":1,"238":6,"239":14,"241":3,"242":3,"243":3,"244":5,"246":1,"669":1}}],["kotlinsendtransaction",{"2":{"203":1}}],["kotlinsuspend",{"2":{"200":2,"201":3,"202":2,"203":1}}],["kotlinquery",{"2":{"201":1}}],["kotlinopen",{"2":{"199":1}}],["kotlinval",{"2":{"199":1,"200":1,"201":2,"202":1}}],["kotlinfun",{"2":{"199":1}}],["kotlinimport",{"2":{"199":1,"204":1}}],["kotlinpackage",{"2":{"198":1}}],["kotlinplugins",{"2":{"197":1}}],["kotlinx",{"2":{"197":8,"199":4}}],["kotlin",{"0":{"196":1},"1":{"197":1,"198":1,"199":1,"200":1,"201":1,"202":1,"203":1,"204":1,"205":1},"2":{"27":1,"29":1,"42":1,"43":1,"178":2,"197":4,"198":1,"201":1,"204":1,"225":1,"287":1}}],["kotlincargo",{"2":{"14":2}}],["kind",{"2":{"32":1,"40":1,"65":2,"79":1,"80":1,"81":1,"94":1,"193":4,"218":1,"273":1,"543":1,"544":2,"658":1,"659":1,"660":1,"661":1,"662":1,"663":1,"716":1}}],["kinds",{"2":{"10":1,"100":1,"148":1}}],["king",{"2":{"24":1}}],["kept",{"2":{"256":1}}],["kevin",{"2":{"253":1}}],["keen",{"2":{"97":1,"220":1}}],["keeping",{"2":{"670":1}}],["keepassx",{"2":{"264":1}}],["keepassxc",{"0":{"265":1},"1":{"266":1},"2":{"252":2,"264":6,"265":6,"266":3,"268":4}}],["keepass",{"2":{"264":2}}],["keeps",{"2":{"252":1,"254":1}}],["keep",{"2":{"5":2,"16":1,"183":1,"209":1,"226":1,"230":1,"235":1,"248":1,"249":2,"250":1,"252":4,"258":3,"263":2,"670":1,"699":1}}],["keyed",{"2":{"728":1,"742":1,"743":1,"752":1}}],["keyvaluetx",{"2":{"205":8}}],["keys",{"0":{"115":1,"132":1,"133":1,"135":1,"145":1,"237":1,"238":1,"256":1,"261":1,"262":1,"267":1,"273":1,"286":1},"1":{"133":1,"134":2,"135":2,"136":1,"238":1,"239":2,"240":1,"241":1,"242":1,"243":1,"244":1,"262":1,"263":2,"264":2,"265":2,"266":2,"267":1,"268":2,"269":2},"2":{"57":2,"65":1,"115":2,"125":1,"126":1,"132":2,"134":5,"135":4,"137":2,"143":2,"145":2,"146":1,"151":3,"161":1,"170":9,"178":1,"187":1,"217":2,"229":1,"237":4,"238":2,"239":2,"246":4,"248":4,"250":4,"254":1,"256":4,"261":2,"262":3,"263":4,"264":4,"266":3,"267":2,"268":3,"271":1,"273":1,"285":1,"286":6}}],["keypairfromhex",{"2":{"199":2}}],["keypairimport",{"2":{"199":1}}],["keypair",{"2":{"53":2,"187":10,"193":2,"198":8,"199":19,"200":10,"201":18,"202":10,"203":8,"205":28,"217":2}}],["keyboard",{"2":{"39":1,"249":1}}],["key=",{"2":{"9":2,"20":4,"170":4,"193":2}}],["key",{"0":{"134":1,"254":1,"268":1},"1":{"255":1,"256":1},"2":{"9":3,"18":59,"20":1,"21":2,"22":2,"25":1,"41":1,"46":1,"50":1,"51":1,"52":3,"53":14,"54":1,"55":1,"61":2,"69":2,"71":2,"97":1,"113":4,"115":3,"126":2,"127":4,"128":8,"132":1,"134":6,"135":8,"137":2,"141":2,"143":14,"145":3,"146":8,"151":5,"164":20,"170":19,"174":4,"187":9,"189":5,"193":4,"200":4,"205":4,"208":1,"210":9,"216":2,"217":8,"219":11,"238":11,"239":25,"246":2,"248":1,"249":1,"250":4,"254":8,"255":9,"256":9,"261":2,"263":8,"264":2,"265":7,"266":3,"268":5,"269":1,"273":9,"286":2,"402":1,"420":1,"421":1,"430":1,"440":1,"479":1,"480":1,"481":1,"482":1,"537":1,"556":1,"571":1,"573":1,"588":1,"589":1,"590":1,"591":1,"592":1,"593":1,"635":1,"682":1,"701":6,"702":4,"703":4,"708":11,"709":11,"710":7,"711":7,"712":11,"713":11,"732":1,"754":1,"762":1,"772":2}}],["knowing",{"2":{"252":1}}],["known",{"2":{"101":1,"135":1,"146":1,"238":1,"249":4,"269":1,"733":1,"734":1,"750":1,"754":1}}],["knowledge",{"2":{"79":1,"197":1,"216":1,"223":1}}],["know",{"2":{"5":1,"18":1,"28":1,"135":1,"146":1,"153":1,"163":1,"176":1,"187":1,"189":1,"197":2,"208":1,"210":1,"211":1,"212":1,"216":2,"219":1,"220":2,"256":1,"734":1}}],["jewellery",{"2":{"674":1}}],["jetbrains",{"2":{"197":4}}],["j",{"2":{"238":1,"239":2}}],["jdk15on",{"2":{"197":2}}],["jitpack",{"2":{"197":2}}],["jp",{"2":{"197":2,"198":16,"199":24,"204":8,"205":40}}],["jvm",{"2":{"197":5}}],["janek",{"2":{"268":1}}],["japanese",{"2":{"155":1,"238":1}}],["javatest",{"2":{"205":2}}],["javapackage",{"2":{"205":1}}],["java",{"0":{"196":1,"205":1},"1":{"197":1,"198":1,"199":1,"200":1,"201":1,"202":1,"203":1,"204":1,"205":1},"2":{"27":1,"29":1,"42":1,"43":1,"136":1,"178":1,"197":11,"198":8,"199":4,"205":12,"225":1,"287":1}}],["javascript",{"0":{"185":1},"1":{"186":1,"187":1,"188":1,"189":1,"190":1,"191":1,"192":1,"193":1,"194":1,"195":1},"2":{"5":4,"27":1,"29":1,"42":1,"43":1,"136":1,"178":2,"186":4,"187":1,"188":3,"193":1,"225":1,"249":1,"287":1,"716":1,"775":1}}],["joining",{"2":{"157":1}}],["join",{"2":{"140":1,"146":1,"156":1,"158":1,"193":2}}],["jupiter",{"2":{"205":4}}],["junit",{"2":{"205":4}}],["judge",{"2":{"62":7}}],["justice",{"2":{"222":1}}],["just",{"2":{"16":1,"18":1,"67":1,"72":1,"81":1,"88":2,"97":2,"101":4,"102":1,"128":1,"146":1,"154":1,"163":1,"168":1,"171":1,"176":1,"183":1,"187":1,"211":1,"213":1,"216":1,"218":1,"220":2,"222":1,"232":1,"242":1,"250":1,"256":1,"272":1,"280":1,"282":1,"716":2}}],["json55",{"2":{"776":1}}],["json44",{"2":{"776":1}}],["jsonscale",{"2":{"775":1}}],["json`",{"2":{"217":2}}],["jsoncrypto",{"2":{"193":1}}],["jsonclient",{"2":{"164":1}}],["jsongenesis",{"2":{"164":1}}],["json$",{"2":{"114":1,"130":1,"134":1,"144":1,"167":1,"168":1,"207":1,"216":1}}],["json",{"2":{"8":2,"13":2,"16":8,"17":2,"21":1,"22":1,"42":1,"57":3,"113":1,"114":1,"115":1,"117":1,"119":1,"120":2,"121":1,"122":1,"125":1,"128":3,"130":5,"131":1,"132":3,"134":3,"135":1,"136":1,"138":1,"141":1,"143":2,"144":1,"145":1,"146":1,"150":1,"154":2,"161":1,"164":7,"166":1,"167":4,"168":11,"170":3,"186":2,"193":4,"198":1,"207":4,"208":8,"216":3,"217":5,"231":1,"238":3,"239":1,"272":1,"766":2,"770":2,"775":7,"776":2}}],["js",{"2":{"5":6,"185":1,"186":2,"187":2,"190":1}}],["ntlm",{"2":{"253":3}}],["nvidia®",{"2":{"253":2}}],["npmrc",{"2":{"186":1}}],["npmrc$",{"2":{"186":1}}],["npm",{"2":{"185":1,"186":20}}],["nfts",{"2":{"137":2}}],["nuances",{"2":{"251":1}}],["numerical",{"2":{"220":1,"636":1}}],["numeric",{"2":{"190":2,"191":2,"238":1,"634":1}}],["numericvalue",{"0":{"357":1,"504":1,"590":1},"2":{"190":4,"191":4,"298":2,"314":1,"324":2,"340":2,"341":1,"447":2,"472":2,"490":2,"491":2,"552":2,"590":1,"602":2,"634":1,"741":1,"744":1}}],["numbers",{"2":{"50":1,"88":1,"253":3,"273":1,"280":1,"732":1}}],["number",{"2":{"11":34,"25":1,"28":2,"43":1,"57":1,"84":1,"98":6,"113":2,"121":3,"137":6,"143":2,"146":4,"151":2,"164":4,"170":4,"174":2,"193":2,"195":2,"211":1,"216":1,"230":1,"238":1,"246":1,"249":1,"252":7,"272":1,"273":1,"607":1,"699":2,"707":3,"767":2,"771":6,"772":2,"775":13}}],["null",{"0":{"127":1},"2":{"125":3,"126":1,"127":1,"128":10,"143":28,"164":38,"174":2,"188":2,"192":6,"193":6,"199":2,"200":2,"201":2,"406":1,"407":1,"408":1,"409":1,"410":1,"411":1,"412":1,"413":1,"414":1,"415":1,"416":1,"417":1,"432":1,"665":1}}],["nginx",{"2":{"117":1}}],["n",{"2":{"84":3,"130":3,"773":2,"774":4}}],["nanos",{"2":{"775":4,"776":2}}],["navigation",{"0":{"225":1,"246":1}}],["navigate",{"2":{"170":1,"177":1,"250":1}}],["nature",{"2":{"769":1}}],["naturally",{"2":{"157":1}}],["natural",{"2":{"28":1,"273":2}}],["natively",{"2":{"187":1}}],["native",{"2":{"186":1,"187":4,"193":2,"716":1,"775":1}}],["nailed",{"2":{"104":1}}],["narrow",{"2":{"65":1}}],["naming",{"0":{"699":1},"2":{"42":1,"699":1}}],["namelength",{"2":{"464":1}}],["namely",{"2":{"127":1,"212":1}}],["name=",{"2":{"211":4}}],["named",{"2":{"50":1,"104":1,"188":1,"189":1,"219":1,"220":1,"670":1}}],["names",{"2":{"42":1,"57":3,"67":1,"74":1,"171":1,"668":1}}],["name",{"0":{"356":1,"492":1,"592":1,"593":1,"640":1,"668":1},"2":{"36":1,"52":2,"53":2,"74":2,"84":1,"90":1,"93":2,"103":2,"104":4,"106":1,"143":2,"164":2,"169":2,"170":22,"171":17,"174":4,"186":2,"187":6,"188":4,"189":5,"190":12,"191":8,"192":14,"193":11,"198":2,"199":6,"200":2,"201":2,"205":20,"216":2,"218":1,"219":1,"220":1,"256":1,"289":1,"290":1,"291":1,"292":1,"293":3,"294":2,"295":1,"296":1,"297":1,"298":1,"299":1,"300":1,"306":1,"307":1,"308":1,"309":1,"310":1,"311":1,"312":3,"313":1,"314":1,"315":1,"316":1,"317":1,"318":1,"319":1,"320":1,"321":1,"322":1,"323":1,"324":1,"325":1,"327":1,"328":1,"330":1,"331":1,"332":1,"333":2,"334":1,"335":1,"336":1,"337":3,"338":1,"339":1,"340":1,"341":1,"342":1,"343":1,"344":1,"345":3,"347":1,"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"367":1,"368":1,"371":1,"372":1,"373":1,"374":1,"375":1,"376":1,"377":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"401":1,"402":2,"403":1,"404":3,"405":1,"418":1,"419":1,"420":2,"421":2,"422":1,"423":1,"424":1,"425":1,"426":1,"427":3,"428":1,"429":1,"430":2,"431":3,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":2,"441":1,"444":1,"445":1,"446":1,"447":1,"453":1,"454":2,"455":1,"456":1,"457":1,"458":1,"459":1,"460":1,"461":1,"462":1,"463":1,"464":1,"470":1,"471":1,"472":1,"473":1,"474":1,"475":1,"476":1,"478":2,"479":2,"480":2,"481":2,"482":2,"483":3,"484":1,"485":1,"486":1,"487":1,"488":1,"489":1,"490":1,"491":1,"493":1,"494":1,"495":1,"496":1,"497":1,"501":1,"502":1,"503":1,"504":1,"505":1,"522":1,"530":1,"531":1,"532":3,"533":1,"534":1,"535":1,"536":1,"537":1,"538":2,"539":2,"540":2,"541":1,"542":1,"543":1,"544":1,"545":1,"546":1,"547":1,"548":1,"549":1,"550":1,"551":1,"552":1,"553":1,"554":1,"555":1,"556":2,"557":1,"558":1,"559":1,"560":1,"561":1,"562":1,"563":1,"564":3,"565":1,"566":1,"567":1,"568":1,"569":1,"570":1,"571":2,"572":1,"573":1,"574":1,"578":1,"579":1,"580":1,"581":1,"582":1,"583":1,"584":1,"585":1,"586":1,"587":1,"592":1,"593":1,"600":1,"602":1,"603":1,"605":1,"606":1,"607":1,"608":2,"609":1,"610":1,"611":1,"612":1,"613":1,"614":1,"615":1,"616":1,"617":1,"618":1,"619":1,"620":1,"621":1,"622":1,"623":3,"624":1,"625":1,"626":1,"627":1,"628":1,"629":1,"630":1,"631":1,"632":1,"633":1,"634":3,"635":2,"636":1,"640":1,"648":1,"649":1,"650":1,"651":1,"652":1,"653":1,"654":1,"656":2,"667":1,"668":18,"699":1,"720":1,"728":2,"729":4,"736":2,"742":2,"743":2,"752":2,"762":1,"776":1}}],["nightly",{"2":{"281":1}}],["nightmare",{"2":{"158":1}}],["nine",{"2":{"25":1}}],["nice",{"2":{"21":1,"88":1,"92":1,"222":1,"273":1}}],["necessity",{"2":{"263":1}}],["necessary",{"2":{"72":1,"101":1,"110":1,"170":1,"178":1,"188":1,"199":1,"217":1,"247":1,"249":1,"253":1,"260":1,"265":1,"268":1,"665":1,"692":1,"756":1}}],["necessarily",{"2":{"65":1,"137":1,"199":1,"233":1,"243":1}}],["net",{"2":{"197":2,"199":2}}],["networktojoin",{"2":{"198":2}}],["networking",{"2":{"168":2}}],["networks",{"2":{"65":1,"136":1,"254":1,"258":2,"263":1,"267":1,"697":1}}],["network",{"0":{"19":1,"132":1,"133":1},"1":{"20":1,"21":1,"133":1,"134":2,"135":2,"136":1},"2":{"10":1,"18":1,"20":1,"21":3,"22":3,"28":1,"39":1,"42":1,"65":2,"106":1,"132":1,"135":4,"137":2,"138":1,"143":2,"156":1,"159":1,"160":2,"161":2,"162":1,"163":2,"164":2,"167":1,"168":2,"170":5,"171":3,"174":2,"190":1,"198":8,"213":26,"216":2,"220":1,"224":1,"230":3,"246":2,"256":2,"258":2,"263":1,"671":1,"677":2,"686":1,"697":1,"734":1,"735":1,"736":1,"755":1,"769":2,"771":2}}],["nexus",{"2":{"186":3}}],["next",{"0":{"184":1},"2":{"88":2,"89":1,"94":1,"97":2,"99":1,"108":1,"166":1,"199":1,"212":1,"220":1,"243":1,"697":1}}],["near",{"2":{"103":1}}],["never",{"2":{"43":1,"88":1,"127":1,"128":1,"211":1,"248":1,"256":2,"259":1,"273":1}}],["negligent",{"2":{"260":1}}],["negligible",{"2":{"39":1}}],["negativevalue",{"2":{"476":1}}],["negative",{"2":{"43":2,"190":1,"211":2}}],["neither",{"2":{"16":1,"89":1,"166":1,"716":1}}],["needless",{"2":{"222":1}}],["needs",{"2":{"20":1,"21":1,"28":1,"57":3,"94":1,"135":1,"141":1,"146":1,"186":1,"216":1,"217":2,"238":1,"256":1,"260":1,"267":1,"268":1}}],["needed",{"2":{"18":1,"126":1,"137":1,"272":1}}],["need",{"2":{"6":1,"11":1,"14":1,"18":2,"20":1,"21":1,"22":1,"24":1,"39":1,"41":2,"42":1,"43":1,"56":1,"65":1,"67":1,"69":1,"83":1,"93":1,"94":4,"97":1,"100":1,"101":2,"102":1,"103":2,"110":1,"116":1,"128":1,"138":1,"141":2,"160":2,"166":1,"170":2,"171":1,"176":1,"181":1,"186":7,"187":6,"188":2,"189":2,"190":3,"193":1,"201":1,"207":1,"211":2,"212":2,"218":1,"219":1,"220":2,"224":1,"252":1,"256":2,"257":1,"263":2,"272":2,"283":1,"665":3,"667":1,"716":1,"734":1}}],["news",{"2":{"670":1}}],["newnetwork",{"2":{"198":4}}],["newly",{"2":{"170":1,"226":1}}],["newrole",{"0":{"497":1},"2":{"128":2,"164":2,"455":2,"555":1}}],["newassetdefinition",{"0":{"494":1},"2":{"128":4,"164":4,"455":2,"555":1}}],["newaccount",{"0":{"493":1},"2":{"42":1,"92":6,"104":2,"128":6,"164":6,"189":6,"219":2,"455":2,"555":1}}],["newdomain",{"0":{"495":1},"2":{"128":4,"164":4,"188":6,"193":6,"455":2,"555":1}}],["newparameterbox",{"0":{"496":1},"2":{"457":1}}],["newparameter",{"0":{"47":1},"2":{"40":1,"41":1,"47":1,"128":22,"164":22,"457":1,"461":1,"698":1}}],["newer",{"2":{"6":1,"178":1,"229":1}}],["new",{"0":{"60":1,"103":1,"133":1,"134":1},"1":{"134":1,"135":1},"2":{"5":1,"7":1,"9":2,"16":1,"17":1,"21":1,"28":1,"31":1,"40":1,"42":1,"52":12,"53":20,"57":1,"60":9,"61":4,"66":2,"67":2,"71":5,"72":1,"78":2,"84":1,"92":40,"93":12,"94":4,"95":2,"96":4,"97":2,"101":1,"103":3,"104":10,"128":7,"129":1,"134":2,"136":1,"137":2,"141":1,"143":3,"146":1,"160":1,"161":1,"162":1,"164":2,"168":1,"169":1,"170":7,"171":1,"187":4,"188":8,"189":6,"190":3,"193":11,"197":1,"199":2,"200":4,"201":5,"202":1,"205":36,"210":1,"216":1,"217":4,"218":4,"219":6,"220":10,"221":4,"229":2,"230":2,"238":1,"256":3,"265":4,"272":2,"286":1,"313":1,"541":1,"667":1,"695":1,"698":1,"702":8,"703":8,"704":8,"705":4,"706":8,"707":4,"708":8,"709":8,"710":4,"711":4,"712":8,"713":8,"714":12,"716":1,"764":1}}],["nobody",{"2":{"163":1,"212":1}}],["nontrivial",{"0":{"498":1},"2":{"445":2}}],["nonzero",{"0":{"499":1,"500":1,"513":1,"514":1},"2":{"329":1,"444":1,"513":1,"514":1,"608":2}}],["nonillion",{"2":{"252":1}}],["nonce",{"0":{"123":1},"2":{"113":2,"123":1,"164":2,"170":2,"174":2,"608":1}}],["none",{"2":{"28":1,"174":4,"188":2,"193":4}}],["non",{"0":{"136":1,"674":1,"676":1},"2":{"24":2,"25":1,"39":1,"43":2,"103":1,"136":1,"141":1,"190":3,"201":3,"211":2,"212":1,"220":2,"227":1,"253":1,"674":1,"676":1}}],["no",{"0":{"107":1},"2":{"18":1,"22":1,"39":2,"62":3,"74":2,"103":1,"104":5,"106":1,"107":1,"125":2,"146":1,"190":1,"195":1,"209":1,"218":2,"219":2,"220":3,"229":1,"231":2,"238":3,"239":1,"252":1,"259":1,"263":1,"282":1,"667":1}}],["now",{"2":{"18":1,"20":1,"22":1,"78":1,"91":1,"94":2,"97":2,"99":1,"104":1,"156":1,"166":1,"168":1,"170":2,"171":2,"187":2,"188":1,"189":1,"206":1,"207":1,"210":1,"212":2,"217":2,"219":1,"220":2,"253":2,"664":1}}],["normal",{"2":{"87":1,"97":1,"214":1,"238":1,"239":4}}],["normally",{"2":{"11":1,"88":1,"106":1,"176":1,"217":2}}],["nor",{"2":{"16":1,"89":1,"166":1,"716":1}}],["nodefetch",{"2":{"187":5}}],["nodeniesandatleastoneallow",{"2":{"62":1}}],["nodenies",{"2":{"62":1}}],["nodes",{"2":{"21":2,"230":3}}],["node",{"2":{"5":2,"106":1,"157":1,"185":1,"186":10,"187":11,"241":1,"671":1,"753":2}}],["notation",{"2":{"716":2}}],["notable",{"2":{"105":2}}],["notably",{"2":{"103":1}}],["notpermitted",{"2":{"630":1}}],["nothing",{"2":{"256":1}}],["notificationeventfilter",{"0":{"503":1},"2":{"378":1}}],["notificationevent",{"0":{"502":1},"2":{"368":1}}],["notification",{"2":{"368":1,"378":1}}],["notify",{"2":{"208":1,"260":1,"690":1}}],["notices",{"2":{"220":1}}],["notice",{"2":{"220":1,"273":1}}],["noticed",{"2":{"97":1}}],["not",{"0":{"501":1,"773":1,"774":1},"2":{"4":1,"11":1,"20":1,"21":1,"24":1,"28":2,"39":1,"42":4,"46":1,"56":2,"62":1,"63":2,"65":2,"87":2,"88":1,"89":2,"94":1,"97":2,"100":1,"104":1,"105":1,"106":1,"116":1,"125":1,"126":2,"128":1,"136":1,"137":4,"139":1,"146":1,"150":1,"154":1,"155":1,"162":1,"166":6,"168":3,"169":1,"180":2,"183":2,"186":1,"187":3,"188":2,"190":1,"199":1,"201":1,"202":2,"208":1,"209":3,"212":1,"213":4,"216":1,"218":2,"219":2,"220":4,"229":1,"230":1,"238":3,"243":2,"247":3,"248":1,"249":3,"250":1,"254":1,"255":1,"256":2,"259":4,"260":1,"263":1,"264":1,"266":1,"271":1,"273":1,"274":1,"277":1,"281":1,"282":2,"283":2,"284":1,"285":1,"286":2,"376":2,"445":1,"486":1,"668":1,"693":2,"716":1,"719":1,"721":1,"722":1,"729":1,"730":1,"733":2,"760":1,"769":1,"777":1}}],["notenoughquantity",{"2":{"476":1}}],["notes",{"2":{"247":1}}],["noted",{"2":{"110":1,"168":1,"208":1}}],["note",{"2":{"4":1,"6":2,"14":1,"16":1,"20":1,"21":1,"22":2,"42":1,"69":1,"78":1,"103":1,"116":1,"127":1,"128":1,"134":3,"135":1,"167":1,"182":1,"186":3,"187":1,"189":1,"193":1,"199":1,"209":1,"211":1,"212":2,"214":1,"217":2,"218":2,"219":1,"220":1,"229":1,"241":1,"244":1,"249":1,"253":1,"263":1,"266":1,"282":1,"667":2,"668":1,"701":1,"719":1,"721":1,"730":1,"733":1,"741":1,"759":1,"764":1}}],["19",{"2":{"376":1,"549":1,"634":1}}],["1933∗109",{"2":{"252":1}}],["1933∗1091",{"2":{"252":1}}],["1933",{"2":{"252":1}}],["18",{"2":{"376":1,"549":1,"634":1,"775":2}}],["17",{"2":{"376":1,"457":1,"461":1,"549":1,"634":1}}],["1729",{"2":{"239":2}}],["15",{"2":{"376":1,"457":1,"461":1,"549":1,"634":1}}],["15000",{"2":{"113":2,"122":2,"164":2,"170":2}}],["1684843456091",{"2":{"203":2}}],["1684843453085",{"2":{"203":2}}],["1684843454049",{"2":{"203":4}}],["1684843451130",{"2":{"203":10}}],["1684843348692",{"2":{"203":2}}],["1684843345604",{"2":{"203":2}}],["1684843344208",{"2":{"203":6}}],["1684843516303",{"2":{"203":10}}],["1684843514251",{"2":{"203":20}}],["1684843513272",{"2":{"203":14}}],["1684843511587",{"2":{"203":48}}],["1684843205383",{"2":{"202":8}}],["1684843203337",{"2":{"202":16}}],["1684843202389",{"2":{"202":14}}],["1684843200289",{"2":{"202":42,"203":2}}],["1684842998891",{"2":{"201":6}}],["1684842997930",{"2":{"201":8,"202":2}}],["1684842996549",{"2":{"201":18,"202":4,"203":2}}],["1684835733686",{"2":{"200":4,"201":2,"202":2,"203":2}}],["1684835731653",{"2":{"200":8,"201":4,"202":4,"203":4}}],["16px",{"2":{"193":2}}],["16",{"2":{"193":2,"252":4,"253":1,"376":1,"457":1,"461":1,"549":1,"634":1}}],["16384000",{"2":{"143":2,"164":2}}],["111",{"2":{"220":1}}],["11",{"2":{"88":5,"376":1,"431":1,"455":1,"457":1,"461":1,"549":1,"634":1}}],["123",{"2":{"220":1}}],["12",{"2":{"88":7,"220":3,"376":1,"431":1,"457":1,"461":1,"549":1,"634":1}}],["128",{"2":{"25":1,"128":2,"143":2,"164":4,"211":1,"252":1}}],["1261a436d36779223d7d6cf20e8b644510e488e6a50bafd77a7485264d27197dfaca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020",{"2":{"18":2}}],["127",{"2":{"11":3,"18":1,"20":7,"21":1,"113":4,"119":2,"120":4,"150":3,"164":4,"170":4,"174":4,"187":12,"193":4,"199":4,"252":1}}],["13",{"2":{"128":2,"164":2,"171":2,"185":1,"216":8,"376":1,"457":1,"461":1,"549":1,"634":1}}],["1340",{"2":{"18":14,"146":2,"213":6}}],["133x",{"2":{"18":1}}],["1339",{"2":{"18":14,"146":2,"213":6}}],["1338",{"2":{"18":14,"146":2,"213":6}}],["1337",{"2":{"18":14,"20":2,"146":2,"213":6}}],["135543",{"2":{"11":2}}],["10^3200∗103",{"2":{"253":1}}],["10^3052∗1030",{"2":{"252":1}}],["10^9300∗109",{"2":{"253":1}}],["10^91",{"2":{"252":1}}],["10^2143∗1021",{"2":{"252":1}}],["10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77",{"2":{"174":4}}],["1048576",{"2":{"143":8,"164":8}}],["10000",{"2":{"143":2,"164":2,"174":2,"199":2}}],["100000",{"2":{"113":2,"122":2,"164":2,"170":2,"174":2}}],["1000",{"2":{"143":8,"164":8,"174":2,"193":2}}],["100",{"2":{"53":2,"61":2,"71":2,"92":2,"143":8,"164":8,"171":4,"187":1,"191":3,"201":2}}],["10",{"2":{"16":1,"18":1,"143":2,"146":1,"164":2,"173":2,"197":2,"198":2,"202":8,"203":6,"205":8,"211":2,"221":2,"290":1,"291":1,"376":1,"431":1,"455":1,"457":1,"459":1,"461":1,"549":1,"634":1,"771":3,"775":1}}],["14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5",{"2":{"170":4}}],["14",{"2":{"11":2,"376":1,"457":1,"461":1,"549":1,"634":1,"775":4}}],["1",{"0":{"92":1,"103":1,"134":1,"160":1,"167":1,"186":1,"197":1,"207":1,"216":1,"229":1,"241":1},"1":{"230":1,"231":1,"232":1,"233":1,"234":1,"235":1},"2":{"5":8,"6":2,"11":3,"18":1,"20":7,"21":1,"28":1,"43":1,"72":1,"88":1,"92":6,"93":2,"103":3,"105":2,"113":4,"119":2,"120":4,"141":1,"143":4,"150":3,"164":8,"168":3,"170":4,"174":6,"176":1,"178":1,"183":1,"186":2,"187":13,"193":6,"197":12,"198":2,"199":4,"207":1,"213":70,"228":2,"229":2,"252":1,"279":6,"290":1,"291":1,"299":1,"309":1,"310":1,"315":1,"316":1,"319":1,"320":1,"332":1,"333":1,"338":1,"339":1,"342":1,"343":1,"367":1,"368":1,"371":1,"375":1,"376":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"431":1,"445":1,"453":1,"454":1,"455":1,"457":1,"458":1,"459":1,"461":1,"464":1,"473":1,"476":1,"483":1,"485":1,"486":1,"503":1,"504":1,"505":1,"534":1,"535":1,"542":1,"545":1,"546":1,"547":1,"549":1,"550":1,"555":1,"557":1,"561":1,"562":1,"569":1,"574":1,"584":1,"600":1,"610":1,"615":1,"618":1,"619":1,"620":1,"621":1,"625":1,"626":1,"630":1,"633":1,"634":1,"636":1,"648":1,"649":1,"650":1,"651":1,"652":1,"766":2,"767":1,"768":2,"771":14,"772":1,"775":1,"777":1}}],["u64",{"0":{"500":1,"514":1,"662":1},"2":{"325":4,"329":1,"346":1,"444":1,"500":1,"504":2,"514":1,"583":1,"607":2,"608":2,"707":2,"775":15}}],["u8",{"0":{"302":1,"304":1,"305":1,"463":1,"647":1,"663":1},"2":{"302":1,"304":1,"305":1,"448":1,"463":2,"466":1,"467":1,"548":1,"573":1,"647":1,"654":1,"655":1,"666":4}}],["u16",{"0":{"301":1,"303":1,"462":1,"660":1},"2":{"301":1,"303":1,"462":2,"468":1,"469":1,"585":1,"586":1,"587":1}}],["u128",{"0":{"567":1,"659":1},"2":{"57":2,"319":1,"504":2,"567":2,"569":2,"636":1,"755":6}}],["utf",{"2":{"273":1,"699":1}}],["utility",{"0":{"690":1},"2":{"669":1,"691":1}}],["utilities",{"2":{"187":1}}],["utilise",{"2":{"259":1}}],["utilises",{"2":{"238":1}}],["utilized",{"2":{"264":1}}],["utilize",{"2":{"250":1}}],["utilizing",{"2":{"245":1,"252":1,"268":1}}],["util",{"2":{"198":2,"205":12}}],["ul",{"2":{"193":8}}],["ui",{"0":{"193":1},"1":{"194":1},"2":{"249":2,"265":1}}],["uint8array",{"2":{"189":2}}],["ux",{"2":{"189":1,"210":1,"219":1,"222":1}}],["ubuntu",{"2":{"182":1}}],["ugly",{"2":{"100":1,"273":1}}],["u32",{"0":{"499":1,"513":1,"568":1,"661":1},"2":{"57":2,"67":4,"77":2,"98":2,"128":4,"164":4,"190":3,"191":2,"202":2,"211":4,"220":2,"221":2,"319":1,"321":1,"346":1,"471":2,"474":2,"499":1,"504":2,"513":1,"557":1,"568":2,"569":2,"608":1,"624":1,"666":4,"699":1,"707":2,"755":2,"775":2}}],["urls",{"2":{"125":1,"187":1}}],["url=",{"2":{"20":4}}],["url",{"0":{"119":1,"120":1,"148":1,"150":1},"2":{"18":17,"21":2,"113":4,"118":2,"119":4,"120":4,"143":8,"147":2,"148":2,"150":2,"164":10,"170":4,"174":4,"187":2,"197":2,"199":6,"217":4}}],["uri",{"2":{"2":1,"217":2}}],["unmatched",{"2":{"286":1}}],["unverified",{"2":{"259":1}}],["unfamiliar",{"2":{"259":1}}],["unfathomable",{"2":{"253":1}}],["unfortunately",{"2":{"108":1,"110":1,"188":1,"211":1,"273":1}}],["unexpected",{"2":{"250":1}}],["unencrypted",{"2":{"247":1,"256":1}}],["ungoogled",{"2":{"249":1}}],["unattended",{"2":{"247":1,"248":2}}],["unauthorized",{"2":{"247":1,"248":1,"251":1,"258":1,"260":2,"262":1,"267":1,"550":1}}],["unable",{"2":{"217":2}}],["unavailable",{"2":{"188":2}}],["unlocked",{"2":{"265":1}}],["unlocks",{"2":{"255":1}}],["unlimitedmetadata",{"2":{"218":6}}],["unlikely",{"2":{"234":1}}],["unlike",{"2":{"207":1,"208":1,"721":1}}],["unless",{"2":{"22":1,"56":1,"63":1,"139":1,"153":1,"249":1,"273":1,"722":1}}],["unnecessary",{"2":{"199":1}}],["unnamed",{"2":{"50":1}}],["unusual",{"2":{"197":1,"216":1,"260":1}}],["undefined",{"2":{"665":1,"667":1}}],["underway",{"2":{"187":1}}],["underscores",{"2":{"171":1,"267":1,"668":1}}],["understandable",{"2":{"250":1}}],["understanding",{"2":{"197":1,"216":1}}],["understand",{"2":{"39":1,"97":1,"100":1,"137":1,"141":1,"178":1,"187":1}}],["under",{"0":{"107":1},"2":{"99":1,"107":1,"157":1,"160":1,"186":1,"208":1,"256":1}}],["underlying",{"2":{"24":1,"25":1,"124":1,"143":1,"190":1,"201":1,"211":1,"220":2}}],["undici",{"2":{"187":5}}],["undicifetch",{"2":{"187":2}}],["untested",{"2":{"180":1}}],["until",{"2":{"16":1,"89":1,"141":1,"157":1,"161":1,"208":1,"212":1}}],["unchanged",{"2":{"125":1}}],["unwrap",{"2":{"104":4}}],["unsupported",{"2":{"458":1}}],["unstable",{"2":{"108":1}}],["unsafe",{"2":{"103":1,"231":1,"665":1,"667":1}}],["unsigned",{"2":{"25":2,"190":1,"211":3}}],["un",{"0":{"42":1},"2":{"41":5,"42":1,"82":3,"83":2,"84":1,"111":1,"146":1,"281":1}}],["unrecognised",{"2":{"57":1}}],["unregisterbox",{"0":{"627":1},"2":{"457":1}}],["unregistered",{"2":{"111":1}}],["unregistering",{"0":{"163":1},"2":{"42":1,"163":1,"705":2}}],["unregister",{"2":{"40":1,"41":1,"98":1,"136":1,"163":1,"457":1,"461":1,"698":1,"701":1,"703":1,"705":5}}],["unreliable",{"2":{"16":1}}],["universe",{"2":{"253":1}}],["universal",{"2":{"77":1,"252":1}}],["universally",{"2":{"16":1}}],["unicode",{"2":{"238":2}}],["unit",{"2":{"105":2,"199":2,"406":1,"407":1,"408":1,"409":1,"410":1,"411":1,"412":1,"413":1,"414":1,"415":1,"416":1,"417":1,"432":1,"749":1,"753":1}}],["units",{"2":{"105":2,"191":1,"673":1}}],["uniquely",{"2":{"243":1}}],["unique",{"2":{"42":2,"132":1,"141":1,"170":1,"249":1,"252":1,"253":1,"255":1,"259":1,"268":1,"674":1}}],["unix",{"2":{"16":1,"57":1,"90":1,"242":1,"266":1}}],["unknown$",{"2":{"108":1}}],["unknown",{"2":{"6":5,"108":3,"259":1,"476":1}}],["upgradablebox",{"0":{"363":1,"628":1},"2":{"629":1}}],["upgrades",{"2":{"686":1}}],["upgradebox",{"0":{"629":1},"2":{"457":1}}],["upgraded",{"2":{"232":1,"632":1}}],["upgrade",{"2":{"178":1,"229":1,"234":2,"278":1,"283":2,"457":1,"461":1,"767":1,"768":1}}],["uppercase",{"2":{"252":1,"253":1}}],["uppermost",{"2":{"127":1}}],["upholding",{"2":{"237":1}}],["updating",{"2":{"226":1,"248":1,"283":1}}],["updates",{"0":{"226":1},"2":{"248":1,"249":1,"283":1,"680":1}}],["updatekeyvalueinstructioncommitted",{"2":{"205":2}}],["updated",{"2":{"146":1,"170":1,"248":1,"260":2,"280":1,"764":1}}],["update",{"0":{"135":1},"2":{"40":1,"41":4,"206":1,"249":1,"250":1,"252":1,"270":1,"272":2,"279":3,"280":2,"283":2,"666":4,"698":1}}],["up$",{"2":{"213":1}}],["uptime",{"2":{"193":4,"771":6,"775":10,"776":2}}],["upon",{"2":{"7":1,"8":1,"9":1,"128":1,"183":1,"693":1}}],["up",{"0":{"161":1},"2":{"6":1,"16":1,"17":1,"20":1,"21":1,"36":1,"42":1,"65":1,"94":1,"97":1,"104":1,"118":1,"135":1,"141":2,"160":1,"161":1,"167":1,"176":1,"178":1,"186":2,"187":1,"195":1,"207":1,"212":1,"213":3,"216":1,"218":1,"222":2,"225":1,"226":1,"230":1,"249":2,"252":1,"253":1,"258":1,"259":3,"260":2,"264":1,"272":1,"281":1,"677":1,"764":1,"767":1,"771":2}}],["usb",{"2":{"247":1,"249":1,"268":1}}],["usable",{"2":{"180":1,"238":1}}],["usage",{"2":{"36":1,"168":4,"194":1,"735":1}}],["usual",{"2":{"166":1,"199":1,"220":1}}],["usually",{"2":{"20":1,"43":1,"100":1,"146":1,"258":1,"280":1,"282":1,"695":1,"749":1}}],["usr",{"2":{"17":2}}],["us",{"2":{"5":1,"95":1,"107":1,"166":1,"187":1,"212":1,"230":1,"252":1,"271":1,"274":1,"277":1,"283":1,"284":1,"285":1}}],["usetask",{"2":{"193":8}}],["useintervalfn",{"2":{"193":4}}],["useful",{"2":{"21":1,"90":1,"141":1,"152":1,"154":2,"160":1,"166":1,"187":1,"209":1,"211":1,"216":1,"217":1,"233":1,"729":1,"755":1}}],["usestalestate",{"2":{"193":4}}],["uses",{"2":{"19":1,"37":1,"193":1,"218":1,"270":1,"667":1,"772":1}}],["username",{"0":{"244":1},"2":{"244":1,"283":1}}],["users",{"0":{"259":1},"2":{"16":1,"39":1,"56":2,"89":1,"136":1,"137":4,"166":1,"170":2,"186":1,"224":1,"229":1,"243":1,"249":2,"250":1,"257":1,"270":1}}],["user",{"0":{"116":1,"160":1},"2":{"13":1,"45":1,"57":1,"89":1,"104":1,"115":1,"126":1,"128":1,"137":5,"143":2,"145":1,"160":2,"164":2,"166":2,"168":2,"170":4,"174":2,"189":1,"190":1,"202":2,"204":1,"210":1,"219":1,"227":1,"243":3,"256":4,"264":1,"265":3,"266":1,"280":1,"283":1,"702":1,"703":1,"704":1,"705":1,"706":1,"707":1,"708":1,"709":1,"710":1,"711":1,"712":1,"713":1,"714":1,"756":1,"771":2}}],["used",{"2":{"6":1,"25":1,"33":1,"36":1,"37":1,"39":1,"42":1,"45":3,"46":1,"48":1,"50":1,"52":1,"57":1,"62":1,"63":1,"69":1,"79":1,"81":1,"85":1,"104":1,"106":1,"110":1,"117":1,"120":1,"124":1,"134":1,"143":1,"149":1,"150":1,"153":1,"166":1,"180":1,"187":1,"197":1,"204":1,"208":1,"211":1,"212":1,"216":1,"217":1,"230":1,"231":1,"236":1,"238":4,"241":1,"248":1,"250":1,"253":1,"255":1,"256":1,"264":3,"265":1,"266":2,"269":1,"665":2,"667":3,"670":1,"673":1,"680":1,"682":1,"683":1,"690":1,"699":2,"716":1,"732":1,"750":1,"755":1,"766":1}}],["use",{"0":{"8":1,"9":1,"11":1,"109":1},"2":{"4":1,"5":3,"6":4,"8":1,"9":1,"11":2,"16":1,"17":1,"22":2,"28":1,"39":3,"40":1,"42":1,"46":1,"52":1,"63":1,"66":1,"67":1,"68":1,"69":1,"78":1,"82":1,"83":1,"85":1,"88":3,"90":3,"94":1,"97":2,"100":1,"101":3,"104":5,"105":4,"109":2,"110":1,"114":1,"117":1,"128":1,"130":1,"131":1,"134":2,"136":1,"137":6,"138":2,"141":2,"144":1,"146":1,"148":1,"153":1,"154":2,"166":2,"167":1,"168":2,"170":2,"176":1,"180":1,"183":1,"186":4,"187":7,"188":2,"193":1,"195":1,"197":2,"199":1,"207":1,"211":1,"214":2,"216":4,"217":6,"218":5,"219":1,"220":1,"222":1,"227":2,"231":2,"232":2,"233":1,"234":1,"238":2,"242":1,"246":2,"247":1,"248":2,"249":6,"250":1,"253":1,"258":3,"268":2,"272":4,"273":1,"280":1,"282":1,"664":1,"665":2,"668":1,"670":1,"698":1,"716":4,"722":1,"756":1,"771":1,"772":4}}],["using",{"0":{"4":1,"20":1,"21":1,"249":1,"263":1,"268":1,"269":1},"2":{"6":2,"10":1,"13":1,"16":1,"28":1,"36":1,"54":1,"56":1,"59":2,"65":3,"83":1,"88":1,"97":6,"100":1,"101":5,"107":1,"109":1,"110":1,"117":1,"120":2,"127":1,"134":2,"137":3,"150":2,"166":2,"168":1,"170":1,"177":1,"180":1,"182":1,"183":1,"186":1,"187":1,"188":1,"200":1,"206":1,"207":2,"216":2,"217":3,"218":2,"220":2,"224":1,"229":1,"233":1,"237":1,"238":1,"241":1,"244":5,"247":1,"249":3,"252":3,"253":5,"256":1,"259":1,"260":1,"263":2,"269":1,"272":1,"283":1,"665":1,"684":1,"693":1}}],["lynn",{"2":{"238":2}}],["luckily",{"2":{"268":1}}],["luck",{"2":{"220":1}}],["ls",{"2":{"168":1}}],["lll",{"2":{"252":1}}],["ll",{"2":{"101":1,"103":1,"128":2,"137":2,"161":1,"164":2,"170":1,"186":1,"212":1,"213":1}}],["le=",{"2":{"771":24}}],["legitimacy",{"2":{"259":1}}],["leisure",{"2":{"158":1}}],["ledgers",{"0":{"670":1},"2":{"670":1}}],["ledger",{"2":{"146":1,"227":1,"233":1,"245":1,"247":1,"670":1}}],["lengthlimits",{"0":{"471":1},"2":{"634":2}}],["lengthy",{"2":{"253":1}}],["length",{"2":{"143":2,"164":2,"252":4,"301":1,"302":1,"303":1,"304":1,"305":1,"605":1}}],["len",{"2":{"143":10,"164":10,"474":1}}],["left",{"2":{"125":1,"137":1,"265":3,"298":1,"300":1,"324":1,"340":1,"347":1,"447":1,"472":1,"490":1,"491":1,"522":1,"530":1,"552":1,"602":1}}],["leftover",{"2":{"108":1}}],["levels",{"2":{"674":1}}],["level",{"0":{"153":1,"355":1,"473":1},"2":{"104":1,"105":2,"120":1,"137":1,"143":2,"150":1,"152":1,"153":2,"164":2,"174":2,"187":1,"224":1,"267":1,"475":2,"634":1,"665":1,"720":1}}],["less",{"0":{"472":1},"2":{"97":1,"101":1,"227":1,"234":1,"376":2}}],["lesser",{"2":{"14":1}}],["lexicographically",{"2":{"65":1,"69":1}}],["leap",{"2":{"253":1}}],["leaving",{"2":{"248":1}}],["leaves",{"2":{"89":1}}],["leave",{"2":{"39":1,"78":1,"222":1,"248":1,"272":1}}],["leaks",{"2":{"250":1}}],["leaked",{"2":{"250":1,"268":1}}],["leaking",{"2":{"249":1}}],["leakage",{"2":{"247":1}}],["leak",{"2":{"246":1}}],["lead",{"2":{"245":1}}],["leader",{"0":{"697":1},"2":{"20":2,"21":2,"669":1,"695":1,"755":2}}],["learning",{"2":{"247":1,"260":1}}],["learn",{"0":{"228":1},"2":{"29":1,"42":1,"80":1,"98":1,"101":1,"120":1,"132":1,"134":1,"150":1,"151":1,"170":2,"178":2,"224":1,"225":1,"233":1,"238":4,"246":2,"247":1,"259":1,"715":1,"716":1,"717":1,"771":1}}],["learned",{"2":{"13":1,"230":1}}],["least",{"2":{"18":2,"20":1,"22":1,"62":2,"212":1,"256":1,"766":1}}],["letters",{"2":{"252":2,"253":2}}],["lets",{"2":{"137":1}}],["let",{"2":{"5":1,"37":1,"39":1,"40":1,"50":1,"52":12,"53":24,"60":6,"61":3,"66":4,"67":5,"71":3,"72":1,"88":1,"91":1,"92":7,"93":11,"94":4,"95":2,"104":7,"105":2,"113":1,"137":3,"141":1,"152":1,"159":1,"167":1,"168":1,"170":1,"187":2,"188":3,"193":1,"199":2,"200":2,"201":2,"202":5,"203":1,"207":1,"212":1,"216":1,"217":16,"218":10,"219":6,"220":7,"221":2,"222":3,"251":1,"252":1,"253":2,"280":1,"664":1,"702":7,"703":7,"704":9,"705":9,"706":9,"707":2,"708":9,"709":9,"710":3,"711":3,"712":9,"713":9,"714":7,"716":1}}],["launch",{"2":{"265":1}}],["layers",{"2":{"262":1}}],["layout",{"2":{"239":2}}],["landscapes",{"2":{"260":1}}],["landscape",{"2":{"250":1}}],["lang=",{"2":{"193":10}}],["languages",{"2":{"665":3}}],["language",{"2":{"27":1,"29":3,"40":1,"42":2,"43":1,"100":1,"101":4,"108":1,"156":1,"166":1,"178":2,"193":1,"204":1,"208":1,"222":2,"225":1,"665":1,"716":1}}],["laptop",{"2":{"248":1}}],["lacks",{"2":{"208":1}}],["labelled",{"2":{"686":1}}],["labeled",{"2":{"238":1}}],["label",{"2":{"193":4}}],["laborious",{"2":{"95":1}}],["larger",{"2":{"154":1}}],["large",{"2":{"90":1,"137":1,"231":1,"270":1}}],["largest",{"2":{"86":1}}],["latin",{"2":{"238":1}}],["latest",{"2":{"180":2,"183":1,"197":3,"247":1,"248":1,"249":1,"272":1,"287":1}}],["late",{"2":{"88":2,"190":3,"220":1}}],["later",{"2":{"16":1,"78":1,"103":1,"104":1,"127":1,"170":1,"183":1,"187":1,"247":1,"253":1,"664":1}}],["latter",{"2":{"16":1,"126":1,"218":2,"282":1}}],["lastupdated",{"2":{"764":1}}],["last",{"2":{"11":8,"84":1,"88":1,"90":1,"197":1,"764":1}}],["lazy",{"2":{"3":1}}],["literal",{"2":{"273":3}}],["literals",{"2":{"220":1,"273":1}}],["little",{"2":{"16":1,"90":1,"100":1,"186":1}}],["li",{"2":{"193":12}}],["lifeline",{"2":{"250":2}}],["life",{"2":{"163":1}}],["live",{"2":{"113":2,"122":3,"128":8,"143":2,"164":12,"170":2,"174":2,"199":4,"200":4,"201":6,"202":6,"203":4,"608":1}}],["limitcheck",{"2":{"610":1}}],["limits",{"0":{"121":1,"474":1},"2":{"101":1,"113":2,"121":1,"143":12,"164":14,"170":2,"583":2,"634":1,"707":2}}],["limitations",{"2":{"82":1}}],["limit",{"2":{"67":4,"143":4,"151":1,"164":4,"252":2,"462":1,"463":1,"566":1,"567":1,"568":1,"772":1}}],["limitedmetadata",{"2":{"634":1}}],["limited",{"2":{"43":1,"141":1,"259":1,"699":1}}],["lightweight",{"2":{"39":1}}],["list$",{"2":{"282":1}}],["listof",{"2":{"198":4,"200":2,"202":2}}],["listed",{"2":{"170":1,"244":1,"264":1}}],["listens",{"2":{"212":1}}],["listeners",{"2":{"689":1,"759":2}}],["listener",{"2":{"193":3,"212":4,"213":8}}],["listenforblocksstream",{"2":{"187":1,"195":2}}],["listenforevents",{"2":{"187":1,"193":2}}],["listening",{"2":{"174":2,"193":3,"212":1,"222":1}}],["listen",{"2":{"18":2,"174":1,"187":3,"193":2,"195":1,"212":3,"213":24,"222":2}}],["list",{"2":{"13":1,"38":1,"40":1,"41":1,"58":1,"70":1,"99":1,"111":1,"120":1,"124":1,"143":1,"146":2,"150":1,"168":2,"169":2,"170":3,"171":2,"187":1,"193":2,"198":4,"199":1,"200":2,"260":1,"282":1}}],["librewolf",{"2":{"249":1}}],["libraries",{"2":{"101":2,"110":1,"136":1,"146":1,"166":1,"216":3,"224":1,"234":1}}],["library",{"2":{"6":1,"101":1,"107":1,"108":2,"110":1,"186":2,"188":1,"207":1,"216":1,"234":1,"241":2,"664":3,"689":1}}],["libssl",{"2":{"182":2}}],["lib",{"2":{"103":4,"104":1}}],["lib$",{"2":{"103":1}}],["libcore",{"0":{"108":1},"2":{"108":1}}],["libc",{"2":{"6":1}}],["lingua",{"2":{"103":1}}],["lines",{"2":{"16":1,"20":1,"199":1,"200":1,"201":2,"203":1,"238":1}}],["line",{"2":{"16":2,"166":2,"167":1,"168":2,"176":1,"193":1,"201":1,"217":1,"222":1,"235":2,"242":1}}],["linux",{"2":{"6":5,"16":3,"20":1,"182":1,"243":1,"264":1,"282":1}}],["linked",{"2":{"76":1,"103":1,"107":1,"127":1,"234":1,"241":1}}],["linkages",{"0":{"236":1},"2":{"236":1}}],["linkage",{"2":{"6":1,"90":1,"103":1}}],["linking",{"0":{"234":1},"2":{"6":3,"234":1,"667":1}}],["links",{"2":{"5":1,"101":1,"216":1,"241":1,"259":1}}],["link",{"2":{"5":1,"105":2,"168":1,"229":1,"241":1,"242":1,"664":1}}],["likely",{"2":{"16":1,"99":2,"104":1,"166":1,"187":1,"218":1,"247":1,"248":1,"252":1,"266":1}}],["like",{"2":{"5":1,"11":1,"16":1,"17":1,"39":2,"42":2,"65":2,"66":1,"72":1,"74":2,"75":1,"92":1,"100":1,"101":1,"102":1,"103":2,"107":1,"117":1,"120":1,"128":1,"137":1,"143":1,"166":2,"168":1,"170":6,"174":1,"186":1,"187":4,"188":1,"193":1,"218":1,"220":2,"222":1,"234":1,"238":1,"241":1,"249":1,"252":1,"253":2,"256":1,"265":1,"270":1,"278":1,"283":1,"665":1,"690":3}}],["lto",{"2":{"105":2}}],["lts",{"2":{"55":1,"180":4,"183":1,"206":1,"272":1}}],["lt",{"0":{"125":1,"126":2,"244":1,"296":1,"297":1,"301":2,"302":2,"303":1,"304":1,"305":1,"322":1,"323":2,"348":1,"349":1,"350":1,"351":1,"352":2,"353":2,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":2,"366":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":2,"389":2,"390":2,"391":2,"392":2,"393":2,"394":2,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"443":1,"445":1,"449":2,"450":1,"451":1,"452":1,"462":1,"463":1,"477":1,"479":1,"480":1,"481":1,"482":1,"487":1,"488":1,"489":1,"498":2,"499":1,"500":1,"506":1,"507":1,"508":1,"509":3,"510":2,"511":1,"512":1,"513":2,"514":2,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1,"566":1,"567":1,"568":1,"575":1,"576":1,"577":1,"578":1,"579":1,"588":1,"589":1,"590":1,"591":1,"592":2,"593":1,"594":1,"595":1,"596":1,"597":2,"598":2,"613":1,"614":1,"637":1,"638":2,"639":1,"640":1,"641":1,"642":1,"643":1,"644":1,"645":2,"646":1,"647":1,"648":1,"649":2},"2":{"4":12,"5":6,"9":2,"36":1,"40":2,"41":1,"53":6,"67":1,"72":1,"76":1,"94":3,"98":4,"104":2,"124":6,"125":2,"126":6,"141":1,"143":3,"162":1,"168":4,"193":128,"198":6,"199":6,"200":6,"201":4,"202":2,"205":28,"218":3,"244":1,"282":3,"289":3,"290":2,"291":1,"292":3,"298":2,"300":2,"301":1,"302":1,"308":1,"309":2,"311":3,"315":2,"317":3,"323":1,"325":6,"327":2,"329":1,"330":2,"331":2,"334":3,"335":4,"336":4,"338":7,"340":2,"341":4,"342":2,"343":2,"344":3,"347":2,"371":1,"372":1,"378":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"401":1,"402":2,"403":1,"404":1,"405":1,"418":1,"419":1,"420":2,"421":2,"422":1,"423":1,"424":1,"425":1,"426":2,"427":1,"428":2,"429":1,"430":2,"431":2,"433":1,"434":1,"435":1,"436":1,"437":2,"438":1,"439":1,"440":2,"441":1,"442":1,"444":3,"445":5,"446":2,"447":2,"448":1,"453":2,"456":3,"466":1,"467":2,"468":1,"469":2,"470":2,"472":2,"475":2,"477":1,"478":1,"484":2,"490":2,"491":2,"493":1,"494":1,"495":1,"496":1,"498":2,"501":1,"505":1,"509":2,"510":1,"513":1,"514":1,"522":2,"536":3,"540":1,"544":3,"548":1,"551":1,"552":2,"553":2,"554":1,"555":1,"556":2,"559":2,"560":1,"563":3,"565":1,"569":3,"570":1,"571":3,"572":1,"573":1,"574":2,"578":2,"579":2,"580":1,"581":1,"582":1,"592":1,"597":1,"598":1,"602":2,"603":1,"608":5,"609":1,"611":1,"612":3,"613":1,"614":1,"615":2,"617":2,"622":3,"623":1,"625":1,"626":3,"627":1,"629":1,"634":1,"636":1,"638":1,"645":1,"648":1,"649":2,"654":2,"655":1,"656":3,"667":1,"716":2,"718":1,"719":1,"720":1,"721":1,"723":1,"724":1,"726":1,"729":1,"730":1,"731":1,"733":1,"734":1,"736":1,"737":1,"738":1,"739":1,"740":1,"746":1,"747":1,"750":1,"754":1,"755":1,"757":5,"760":1,"763":1,"772":1}}],["loss",{"2":{"250":1}}],["lose",{"2":{"248":1,"258":1}}],["loses",{"2":{"135":1}}],["losing",{"2":{"230":1,"250":1}}],["lost",{"2":{"6":1,"250":2,"775":2}}],["lot",{"2":{"106":2,"137":1,"187":1,"220":1}}],["lower",{"2":{"279":1,"280":1}}],["lowercase",{"2":{"252":1}}],["low",{"2":{"104":1,"187":1}}],["longhand",{"2":{"219":4}}],["long",{"2":{"100":1,"101":2,"151":1,"160":1,"180":1,"183":1,"199":2,"202":2,"253":1}}],["longer",{"2":{"14":1,"97":1,"106":1,"238":1}}],["loops",{"2":{"100":1}}],["loop",{"2":{"97":1,"212":1,"222":2,"260":1}}],["lookalike",{"2":{"249":1}}],["looks",{"2":{"74":2,"92":1,"94":1,"100":1,"143":1,"166":1,"168":1,"218":1}}],["looking",{"0":{"682":1},"2":{"41":1,"88":1,"116":2,"128":2,"169":6,"170":13,"171":12,"172":6,"173":4,"188":5,"189":3,"190":5,"199":6,"200":10,"201":24,"202":52,"203":74,"209":2,"210":3,"211":4,"216":1,"218":14,"219":5,"220":3,"669":1}}],["look",{"2":{"18":1,"37":1,"50":1,"72":1,"83":1,"88":1,"94":1,"99":1,"103":1,"105":1,"113":1,"128":1,"137":1,"166":2,"168":1,"170":2,"174":1,"187":4,"197":1,"198":1,"212":1,"228":3,"249":1,"272":1,"273":1,"278":1}}],["loading",{"2":{"217":2,"259":1}}],["loads",{"2":{"208":2}}],["loaded",{"2":{"112":1,"213":4,"696":1}}],["load",{"2":{"67":1,"137":1,"217":3,"734":1}}],["loc",{"2":{"217":4}}],["locked",{"2":{"265":1}}],["lock",{"2":{"39":1,"186":2,"212":1,"248":1,"255":1}}],["localstorage",{"2":{"193":2}}],["localhost",{"2":{"18":2}}],["locally",{"2":{"17":1,"106":1,"213":1}}],["local",{"0":{"243":1,"244":1},"1":{"244":1},"2":{"2":1,"3":1,"214":2,"216":4,"218":1,"243":3,"244":7,"283":1}}],["locations",{"2":{"3":1,"282":1}}],["location",{"0":{"280":1},"2":{"2":1,"137":1,"148":1,"155":2,"193":2,"217":2,"248":1,"250":1,"253":1,"258":1,"282":1}}],["located",{"2":{"0":1,"2":1,"130":1,"170":1}}],["loglevel",{"2":{"634":1}}],["logbox",{"0":{"475":1},"2":{"457":1}}],["logconsumer",{"2":{"198":2}}],["logging",{"2":{"153":1,"154":1,"231":1}}],["logged",{"2":{"153":1}}],["loggerfactory",{"2":{"198":2}}],["logger",{"0":{"152":1},"1":{"153":1,"154":1},"2":{"126":1,"143":2,"152":1,"164":2,"171":1,"174":2,"193":4}}],["log",{"0":{"153":1,"154":1},"2":{"143":4,"152":2,"153":4,"154":5,"164":4,"174":4,"188":2,"192":6,"195":2,"199":2,"457":1,"461":1,"681":1}}],["logographic",{"2":{"238":1}}],["logo",{"2":{"128":8,"164":8,"188":2,"193":2,"308":1,"341":1,"494":1,"495":1}}],["login",{"2":{"113":2,"117":4,"164":2,"170":2,"263":1}}],["logical",{"2":{"65":1,"690":1}}],["logic",{"0":{"36":1},"2":{"36":1,"39":1,"90":1,"97":1,"101":1,"104":1,"126":1,"233":3,"282":1,"680":1,"681":1}}],["logs",{"2":{"21":1,"154":1,"272":2}}],["lorem",{"2":{"2":2,"4":8}}],["gpus",{"2":{"252":1,"253":2}}],["gpg",{"2":{"249":1,"262":1}}],["gnu",{"2":{"243":1}}],["gnu+linux",{"2":{"241":1}}],["g$",{"2":{"186":1}}],["ghosting",{"2":{"163":1}}],["gate",{"0":{"680":1},"2":{"669":1}}],["gateway",{"2":{"263":2}}],["gathering",{"2":{"120":1,"150":1}}],["gathered",{"2":{"120":1,"150":1}}],["gain",{"2":{"260":1}}],["gained",{"2":{"230":1,"248":1}}],["gapped",{"2":{"248":1}}],["gave",{"2":{"189":1,"210":1,"219":1}}],["garden",{"2":{"128":8,"164":8,"199":4,"200":4,"201":6,"202":6,"203":4}}],["gas",{"2":{"89":1}}],["gauge",{"2":{"11":10,"771":10}}],["guesses",{"2":{"268":1}}],["guessable",{"2":{"253":1}}],["gui",{"2":{"249":1}}],["guidelines",{"2":{"247":1,"260":1}}],["guided",{"2":{"176":1}}],["guides",{"2":{"27":1,"29":2,"40":1,"42":1,"43":1,"156":1,"178":3,"225":1}}],["guide",{"0":{"165":1,"185":1,"196":1,"206":1,"215":1},"1":{"166":1,"167":1,"168":1,"169":1,"170":1,"171":1,"172":1,"173":1,"174":1,"186":1,"187":1,"188":1,"189":1,"190":1,"191":1,"192":1,"193":1,"194":1,"195":1,"197":1,"198":1,"199":1,"200":1,"201":1,"202":1,"203":1,"204":1,"205":1,"207":1,"208":1,"209":1,"210":1,"211":1,"212":1,"216":1,"217":1,"218":1,"219":1,"220":1,"221":1,"222":1},"2":{"16":1,"29":1,"42":1,"109":1,"182":1,"185":2,"186":1,"193":2,"216":1,"224":2,"228":1,"265":2,"700":1}}],["guarantee",{"2":{"180":2,"247":1,"259":1}}],["guaranteed",{"2":{"65":1,"230":1,"243":1,"665":1,"769":1,"777":1}}],["grabbed",{"2":{"766":1}}],["gradle",{"2":{"197":1}}],["graphics",{"2":{"166":1}}],["graphical",{"2":{"166":1}}],["granting",{"2":{"59":1,"71":1,"160":1}}],["grantbox",{"0":{"446":1},"2":{"53":2,"61":2,"71":2,"457":1,"702":2,"703":2,"704":2,"705":2,"706":2,"708":2,"709":2,"712":2,"713":2,"714":2}}],["grants",{"2":{"53":2,"57":3,"249":1,"704":2,"705":2,"706":2,"708":2,"709":2,"712":2,"713":2,"714":2}}],["granted",{"2":{"44":1,"45":1,"56":4,"59":1,"60":1,"139":1,"160":1,"161":1,"263":1,"697":1,"710":2,"711":2,"722":1,"724":1}}],["grant",{"0":{"45":1,"61":1,"160":1},"2":{"40":1,"41":3,"45":3,"53":11,"56":1,"59":1,"61":7,"71":7,"128":2,"129":1,"160":2,"164":2,"457":1,"461":1,"698":1,"702":2,"703":2,"704":4,"705":4,"706":4,"708":4,"709":4,"712":4,"713":4,"714":4}}],["gruelling",{"2":{"97":1}}],["grow",{"2":{"252":1}}],["grows",{"2":{"82":1}}],["groups",{"0":{"59":1},"1":{"60":1,"61":1},"2":{"700":1}}],["group",{"2":{"45":1,"170":1,"186":2,"197":2}}],["greater",{"0":{"447":1},"2":{"98":1,"376":2}}],["great",{"2":{"42":1,"136":1,"178":1,"186":1,"187":1,"230":1,"231":1,"716":1}}],["greeted",{"2":{"18":1,"21":1,"213":1}}],["glossary",{"0":{"669":1},"1":{"670":1,"671":1,"672":1,"673":1,"674":1,"675":1,"676":1,"677":1,"678":1,"679":1,"680":1,"681":1,"682":1,"683":1,"684":1,"685":1,"686":1,"687":1,"688":1,"689":1,"690":1,"691":1,"692":1,"693":1,"694":1,"695":1,"696":1,"697":1}}],["glorified",{"2":{"166":1}}],["globally",{"2":{"187":2,"665":1,"726":1}}],["global",{"2":{"98":1,"111":1,"137":1,"216":1,"243":1,"718":1,"719":1}}],["glue",{"2":{"137":1}}],["glued",{"2":{"39":1}}],["glass",{"0":{"682":1},"2":{"116":2,"128":2,"169":6,"170":13,"171":12,"172":6,"173":4,"188":5,"189":3,"190":5,"199":6,"200":10,"201":24,"202":52,"203":74,"209":2,"210":3,"211":4,"218":14,"219":5,"220":3,"669":1}}],["glibc",{"2":{"6":1}}],["google",{"2":{"249":6,"259":1}}],["goods",{"2":{"137":1,"673":1}}],["good™",{"2":{"28":3}}],["good",{"2":{"16":1,"28":1,"94":1,"161":1,"166":1,"177":1,"687":1}}],["gold",{"2":{"227":1}}],["gossip",{"2":{"143":6,"164":6}}],["gotten",{"2":{"91":1,"166":1}}],["got",{"2":{"84":1,"86":2,"88":3,"92":1,"97":3,"195":2,"211":1,"218":1,"220":1,"282":1}}],["gone",{"2":{"84":1}}],["go",{"2":{"67":1,"81":1,"110":1,"116":1,"127":1,"128":1,"141":2,"162":1,"176":1,"216":1,"226":1,"265":1,"278":1}}],["goes",{"2":{"20":1,"21":1,"126":1,"258":1,"285":1,"286":1,"667":1}}],["going",{"2":{"13":3,"18":1,"88":1,"104":1,"110":1,"171":1,"187":1,"211":1,"220":1}}],["gil",{"2":{"212":1}}],["giving",{"2":{"88":1,"248":1,"261":1}}],["given",{"2":{"18":2,"49":3,"62":1,"83":2,"135":1,"137":1,"166":2,"168":2,"190":1,"195":1,"217":1,"272":1,"273":3,"665":1,"667":2,"720":1,"721":1,"727":1,"728":2,"729":1,"731":1,"736":1,"737":1,"738":1,"739":1,"740":1,"741":1,"742":2,"743":2,"744":1,"751":1,"752":2,"761":1,"762":1,"763":1,"767":1,"772":1}}],["give",{"2":{"11":1,"40":2,"42":1,"120":2,"137":1,"150":2,"189":1,"197":1,"210":1,"219":1,"220":1,"249":1,"698":2}}],["git$",{"2":{"183":2}}],["git",{"2":{"5":2,"16":17,"17":8,"22":2,"103":2,"166":1,"168":2,"177":2,"181":1,"183":9,"186":4,"197":1,"207":6,"216":11,"272":1}}],["github",{"0":{"183":1},"2":{"5":1,"103":2,"168":2,"175":1,"179":1,"181":1,"183":2,"186":2,"197":8,"207":4,"213":1,"216":1,"218":1,"270":2,"764":1,"777":1}}],["githubusercontent",{"2":{"2":2,"5":5}}],["gt",{"0":{"125":1,"126":2,"244":1,"296":1,"297":1,"301":2,"302":2,"303":1,"304":1,"305":1,"322":1,"323":2,"348":1,"349":1,"350":1,"351":1,"352":2,"353":2,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":2,"366":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":2,"389":2,"390":2,"391":2,"392":2,"393":2,"394":2,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"443":1,"445":1,"449":2,"450":1,"451":1,"452":1,"462":1,"463":1,"477":1,"479":1,"480":1,"481":1,"482":1,"487":1,"488":1,"489":1,"498":2,"499":1,"500":1,"506":1,"507":1,"508":1,"509":3,"510":2,"511":1,"512":1,"513":2,"514":2,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1,"566":1,"567":1,"568":1,"575":1,"576":1,"577":1,"578":1,"579":1,"588":1,"589":1,"590":1,"591":1,"592":2,"593":1,"594":1,"595":1,"596":1,"597":2,"598":2,"613":1,"614":1,"637":1,"638":2,"639":1,"640":1,"641":1,"642":1,"643":1,"644":1,"645":2,"646":1,"647":1,"648":1,"649":2},"2":{"9":2,"29":4,"36":1,"40":2,"41":1,"53":8,"67":1,"72":1,"76":1,"94":2,"98":4,"104":2,"114":2,"124":6,"125":2,"126":6,"130":2,"132":1,"141":1,"143":3,"144":2,"162":1,"167":2,"168":4,"186":2,"188":4,"191":2,"192":12,"193":138,"195":2,"198":6,"199":8,"200":8,"201":6,"202":6,"205":30,"218":3,"222":4,"238":1,"244":9,"265":3,"282":3,"289":3,"290":2,"291":1,"292":3,"298":2,"300":2,"301":1,"302":1,"308":1,"309":2,"311":3,"315":2,"317":3,"323":1,"325":6,"327":2,"329":1,"330":2,"331":2,"334":3,"335":4,"336":4,"338":7,"340":2,"341":4,"342":2,"343":2,"344":3,"347":2,"371":1,"372":1,"378":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"401":1,"402":2,"403":1,"404":1,"405":1,"418":1,"419":1,"420":2,"421":2,"422":1,"423":1,"424":1,"425":1,"426":2,"427":1,"428":2,"429":1,"430":2,"431":2,"433":1,"434":1,"435":1,"436":1,"437":2,"438":1,"439":1,"440":2,"441":1,"442":1,"444":3,"445":5,"446":2,"447":2,"448":1,"453":2,"456":3,"466":1,"467":2,"468":1,"469":2,"470":2,"472":2,"475":2,"477":1,"478":1,"484":2,"490":2,"491":2,"493":1,"494":1,"495":1,"496":1,"498":2,"501":1,"505":1,"509":2,"510":1,"513":1,"514":1,"522":2,"536":3,"540":1,"544":3,"548":1,"551":1,"552":2,"553":2,"554":1,"555":1,"556":2,"559":2,"560":1,"563":3,"565":1,"569":3,"570":1,"571":3,"572":1,"573":1,"574":2,"578":2,"579":2,"580":1,"581":1,"582":1,"592":1,"597":1,"598":1,"602":2,"603":1,"608":5,"609":1,"611":1,"612":3,"613":1,"614":1,"615":2,"617":2,"622":3,"623":1,"625":1,"626":3,"627":1,"629":1,"634":1,"636":1,"638":1,"645":1,"648":1,"649":2,"654":2,"655":1,"656":3,"666":2,"667":1,"668":4,"716":2,"718":1,"719":1,"720":1,"721":1,"723":1,"724":1,"726":1,"729":1,"730":1,"731":1,"733":1,"734":1,"736":1,"737":1,"738":1,"739":1,"740":1,"746":1,"747":1,"750":1,"754":1,"755":1,"757":5,"760":1,"763":1,"772":1}}],["g",{"2":{"9":1,"21":1,"31":1,"38":1,"39":1,"42":1,"56":1,"65":1,"81":1,"88":3,"108":1,"167":1,"168":1,"171":1,"174":1,"186":1,"193":1,"212":1,"218":1,"238":2,"247":5,"249":1,"250":1,"256":1,"259":1,"273":2,"716":1,"733":1,"769":1,"776":1}}],["gear",{"2":{"265":1}}],["geforce",{"2":{"253":2}}],["genericpredicatebox",{"0":{"445":1,"498":1,"638":1},"2":{"445":3,"498":1,"551":1,"638":1}}],["genericvaluepredicatebox",{"2":{"199":4,"200":2,"201":2}}],["generic",{"2":{"137":1,"668":1}}],["generator",{"2":{"269":1,"682":1}}],["generating",{"0":{"237":1,"238":1},"1":{"238":1,"239":2,"240":1,"241":1,"242":1,"243":1,"244":1},"2":{"134":3,"170":3,"237":1,"246":1,"256":1,"273":1,"666":1}}],["generation",{"0":{"114":1,"129":1,"144":1,"667":1},"1":{"130":1,"668":1},"2":{"105":2,"187":1,"238":3,"667":1}}],["generates",{"2":{"665":1}}],["generatekeypair",{"2":{"198":4,"200":2,"202":2}}],["generated",{"2":{"87":1,"97":1,"134":2,"170":2,"189":1,"198":2,"199":20,"200":1,"204":8,"205":22,"210":1,"219":1,"238":2,"667":1,"668":1}}],["generate",{"0":{"130":1,"134":1},"2":{"22":1,"53":2,"103":1,"114":1,"128":1,"130":4,"134":3,"141":1,"144":1,"170":5,"178":1,"189":2,"210":2,"217":1,"219":2,"229":1,"237":1,"238":3,"246":1,"252":1,"269":1,"286":1,"665":2,"666":1,"667":2,"682":1}}],["generally",{"2":{"249":1,"252":1}}],["generalised",{"2":{"97":1}}],["general",{"0":{"258":1},"2":{"18":1,"42":1,"160":1,"700":1}}],["genesis$",{"2":{"21":1,"130":1}}],["genesis",{"0":{"7":1,"128":1,"130":1,"136":1,"151":1,"272":1},"1":{"129":1,"130":1,"131":1},"2":{"6":1,"7":2,"8":1,"13":1,"16":8,"18":12,"20":1,"21":6,"24":1,"42":2,"53":2,"126":3,"128":18,"129":1,"130":13,"131":2,"132":2,"135":2,"136":2,"138":1,"141":3,"143":3,"151":12,"164":5,"168":1,"178":1,"190":3,"198":8,"199":4,"200":6,"201":6,"202":6,"203":6,"211":1,"220":1,"272":8,"671":1,"676":1,"682":1,"702":4,"703":4,"766":2,"771":10,"775":2}}],["getu32",{"2":{"205":2}}],["getvalue",{"2":{"205":2}}],["getassets",{"2":{"205":2}}],["getaccountamount",{"2":{"202":7,"203":2}}],["getid",{"2":{"205":4}}],["gettxtimeout",{"2":{"205":20}}],["getempty",{"2":{"198":2}}],["getlogger",{"2":{"198":4}}],["getproperties",{"2":{"197":2}}],["getstring",{"2":{"205":8}}],["getstatus",{"2":{"193":2}}],["getseconds",{"2":{"205":20}}],["gets",{"2":{"28":1,"89":1,"94":1,"98":2,"100":1,"157":1,"174":1,"212":2,"716":3,"742":1,"743":1,"748":1,"751":1}}],["get",{"0":{"178":1},"2":{"3":3,"5":2,"6":1,"13":1,"28":2,"39":1,"43":1,"54":1,"67":1,"88":3,"89":1,"90":1,"92":1,"96":1,"97":5,"103":2,"104":1,"105":1,"120":2,"137":1,"150":2,"153":1,"167":1,"169":1,"174":1,"176":1,"182":2,"183":1,"197":1,"199":1,"203":1,"205":30,"210":2,"216":1,"219":2,"222":1,"223":1,"234":1,"258":1,"270":1,"273":1,"280":1,"668":2,"702":2,"703":2,"766":1,"770":1,"771":1,"775":1}}],["circumvent",{"2":{"243":1}}],["circumstances",{"2":{"163":1,"256":1}}],["cyrillic",{"2":{"238":1}}],["csd",{"0":{"236":1},"2":{"236":1}}],["cfg",{"2":{"208":4}}],["centralized",{"2":{"229":1}}],["centric",{"2":{"141":1}}],["certain",{"2":{"31":1,"38":1,"39":1,"40":1,"41":1,"49":1,"62":2,"72":1,"82":1,"87":1,"97":1,"98":2,"148":1,"166":1,"238":1,"252":1,"677":1,"698":1}}],["c",{"2":{"103":3,"166":3,"168":2,"213":2,"238":1,"239":2,"241":1,"665":2,"667":2}}],["cross",{"2":{"667":2}}],["crucial",{"2":{"237":1}}],["critical",{"2":{"234":2,"249":1,"260":1}}],["criteria",{"2":{"138":1}}],["craft",{"2":{"253":1}}],["crafted",{"2":{"231":1}}],["cracking",{"2":{"252":1,"253":1}}],["crack",{"2":{"252":1,"253":5}}],["crashing",{"2":{"230":1}}],["crash",{"2":{"230":1}}],["crate",{"2":{"103":5,"104":2,"108":1,"136":1,"216":4,"665":2,"667":1}}],["crates",{"2":{"102":1,"107":1,"216":3}}],["credentials",{"0":{"117":1},"2":{"117":3,"263":1}}],["creator",{"2":{"193":1}}],["creation",{"2":{"143":2,"164":2,"169":1,"189":1,"210":1,"211":1,"219":1,"608":1,"667":1,"771":2,"775":2}}],["creating",{"2":{"71":1,"90":1,"136":1,"166":1,"186":1,"197":1,"209":1,"216":1,"246":1,"250":1,"265":2}}],["createapp",{"2":{"193":4}}],["creates",{"2":{"154":1,"204":1,"220":1}}],["createdomain",{"2":{"193":9}}],["created",{"2":{"31":1,"66":1,"97":1,"137":1,"167":1,"169":2,"170":1,"171":1,"177":1,"183":1,"186":1,"188":2,"199":5,"200":6,"201":12,"202":16,"203":12,"213":8,"216":1,"255":1,"265":1,"290":1,"309":1,"315":1,"332":1,"342":1,"561":1,"620":1}}],["create",{"0":{"66":1,"95":1,"96":1,"103":1},"2":{"17":1,"21":1,"40":2,"47":1,"68":1,"78":1,"88":1,"90":1,"94":1,"95":2,"96":1,"97":1,"98":1,"103":1,"123":1,"128":1,"135":1,"137":1,"143":3,"164":2,"166":1,"167":2,"168":1,"170":2,"183":3,"187":2,"188":1,"189":3,"190":2,"193":3,"197":1,"199":1,"202":1,"207":1,"210":1,"212":1,"216":2,"217":3,"218":12,"219":7,"220":5,"221":1,"222":1,"227":2,"237":1,"249":1,"254":1,"265":1,"268":1,"270":1,"665":1,"698":2,"702":2,"703":2,"704":4,"705":4,"706":4,"707":2,"708":4,"709":4,"710":2,"711":2,"712":4,"713":4,"714":4,"755":2}}],["cryptotypes",{"2":{"187":4}}],["crypto$",{"2":{"170":1,"238":1}}],["cryptography",{"0":{"254":1},"1":{"255":1,"256":1},"2":{"115":1,"132":1,"145":1,"151":1,"170":1,"186":1,"229":1,"246":2,"248":1,"254":2,"255":1,"256":1}}],["cryptographic",{"0":{"237":1,"238":1,"261":1,"262":1,"267":1},"1":{"238":1,"239":2,"240":1,"241":1,"242":1,"243":1,"244":1,"262":1,"263":2,"264":2,"265":2,"266":2,"267":1,"268":2,"269":2},"2":{"115":2,"132":2,"134":5,"145":2,"151":1,"170":6,"178":1,"186":1,"216":1,"237":1,"238":5,"246":4,"248":1,"250":1,"254":1,"261":2,"262":3,"263":1,"266":3,"267":1,"268":1,"682":1}}],["crypto",{"2":{"5":6,"22":2,"134":2,"170":3,"186":25,"187":8,"193":20,"197":2,"199":2,"204":2,"216":5,"238":2,"239":14}}],["cdylib",{"2":{"103":3}}],["cd",{"2":{"16":5,"168":2,"177":2,"183":2,"207":4}}],["cheap",{"2":{"716":1}}],["checkboxes",{"2":{"265":1}}],["checkbox",{"2":{"265":1}}],["checker",{"2":{"193":1}}],["checked",{"2":{"57":1,"62":2,"211":1,"271":1}}],["checkout",{"2":{"183":2,"272":1}}],["checks",{"2":{"168":1,"204":1,"260":1}}],["checking",{"2":{"62":1}}],["check",{"0":{"279":1,"280":1,"281":1,"282":1,"286":1},"2":{"13":1,"21":1,"36":1,"41":1,"42":1,"51":1,"62":1,"63":1,"70":1,"71":1,"82":1,"83":1,"147":1,"155":1,"161":1,"170":4,"178":1,"182":1,"186":2,"189":1,"197":1,"201":1,"202":1,"210":1,"213":1,"217":1,"219":3,"220":1,"244":1,"272":3,"283":2,"285":2,"286":2,"289":1,"665":1,"670":1,"777":1}}],["chromium",{"2":{"249":5}}],["chrome",{"2":{"249":2}}],["chunks",{"2":{"231":1}}],["chooses",{"2":{"234":1,"238":1}}],["choose",{"0":{"180":1},"2":{"179":1,"180":1,"207":1,"246":1,"249":1,"265":1}}],["choosing",{"0":{"137":1},"2":{"28":1,"101":1,"137":1}}],["choice",{"2":{"89":1,"249":1}}],["chose",{"2":{"79":1,"176":1,"212":1}}],["chosen",{"2":{"28":1,"103":1,"238":1}}],["challenge",{"2":{"263":2,"268":1}}],["challenges",{"2":{"250":1,"260":1}}],["chatting",{"2":{"162":1}}],["charts",{"2":{"286":1}}],["charge",{"2":{"118":1,"147":1}}],["character",{"2":{"219":2,"220":2,"252":6,"253":4}}],["characteristics",{"2":{"81":1,"190":1,"201":1,"211":1,"220":1,"674":1}}],["characters",{"2":{"74":1,"134":1,"171":2,"238":4,"249":2,"252":3,"273":1,"699":3}}],["chance",{"2":{"246":1}}],["changing",{"2":{"72":1,"260":1}}],["changed",{"2":{"28":1,"272":1,"332":1}}],["change",{"0":{"695":1},"2":{"21":1,"28":2,"33":1,"47":1,"57":1,"99":1,"108":1,"120":1,"135":2,"137":2,"148":3,"150":1,"155":1,"166":2,"168":1,"180":1,"233":1,"325":1,"669":1,"676":1,"697":1,"701":1}}],["changes",{"2":{"6":1,"8":1,"9":1,"28":2,"94":1,"99":1,"229":1,"265":1,"692":1,"755":1,"771":8,"775":6}}],["channels",{"2":{"270":2}}],["channel",{"2":{"18":1,"143":8,"164":8,"170":1,"250":1}}],["chapter",{"2":{"13":1,"25":1,"29":1,"58":1,"99":1,"160":1,"178":1,"700":1,"722":1}}],["chain",{"2":{"11":2,"36":1,"39":1,"40":1,"47":1,"63":2,"88":1,"101":1,"106":2,"111":1,"233":1,"698":1}}],["culture",{"2":{"260":1}}],["cultivate",{"2":{"260":1}}],["cut",{"2":{"230":1,"256":1}}],["cumbersome",{"2":{"97":1,"211":1,"241":1}}],["cup",{"2":{"92":2,"93":1,"97":2}}],["cursor",{"2":{"322":1,"323":1,"444":1}}],["curve",{"2":{"238":1}}],["curious",{"2":{"224":1}}],["curated",{"2":{"146":1}}],["currencies",{"2":{"227":1}}],["currency",{"2":{"39":1,"673":1}}],["currenttimemillis",{"2":{"199":2,"200":2,"201":2,"202":2}}],["currentlistener",{"2":{"193":12}}],["currently",{"2":{"42":1,"63":1,"82":1,"104":1,"112":1,"136":1,"171":2,"193":1,"199":1,"204":1,"216":1,"222":1,"256":1,"270":1,"696":1,"699":1,"760":1,"771":2}}],["current",{"2":{"6":1,"39":1,"65":1,"87":1,"88":1,"94":1,"97":1,"112":2,"166":1,"188":1,"193":1,"195":1,"211":1,"226":1,"272":1,"683":1,"696":2,"766":1,"767":1,"770":1,"771":4,"775":2}}],["curl",{"2":{"11":2,"176":3}}],["customize",{"2":{"170":1}}],["customizable",{"2":{"134":1,"214":1}}],["custom",{"0":{"8":1,"9":1,"693":1},"2":{"6":2,"7":1,"8":1,"9":1,"62":1,"103":1,"128":1,"155":1,"227":1,"669":1,"688":1,"690":1}}],["cp",{"2":{"6":2,"17":4,"167":4,"207":2,"216":2}}],["cluster",{"2":{"253":1}}],["clues",{"2":{"253":1}}],["cl",{"2":{"208":2,"209":2,"210":2,"211":2,"212":2}}],["clarity",{"2":{"264":1}}],["classes",{"2":{"198":1,"199":1}}],["class=",{"2":{"193":2}}],["classic",{"2":{"187":1,"253":1}}],["class",{"2":{"187":1,"198":4,"199":6,"200":2,"201":4,"202":2,"203":1,"205":16}}],["claim",{"2":{"141":1}}],["clearly",{"2":{"169":1}}],["clear",{"2":{"137":1,"249":2,"250":2,"260":1,"287":1}}],["clean",{"2":{"5":1,"183":1}}],["closing",{"2":{"166":1}}],["closed",{"2":{"148":1,"264":1,"265":1,"266":1}}],["closer",{"2":{"19":1,"50":1,"72":1,"105":1}}],["clock",{"2":{"88":1}}],["cloned",{"2":{"667":1}}],["clone",{"2":{"53":18,"92":24,"93":10,"94":4,"183":4,"186":3,"197":1,"207":3,"217":2,"220":2,"668":10,"702":2,"703":2}}],["clinic",{"2":{"137":1}}],["cli",{"0":{"166":1},"2":{"22":1,"39":1,"115":1,"132":1,"136":1,"146":1,"164":2,"166":14,"167":5,"168":13,"169":4,"170":8,"171":7,"172":2,"173":2,"174":4,"177":2,"208":1,"216":1,"222":1,"238":1,"256":1}}],["cli$",{"2":{"22":1,"168":1,"170":1}}],["click=",{"2":{"193":4}}],["click",{"2":{"5":1}}],["clients",{"2":{"249":2,"256":1,"693":1}}],["clientconfiguration",{"2":{"217":6}}],["client",{"0":{"113":1,"167":1,"175":1,"177":1,"186":1,"187":1,"197":1,"207":1,"216":1,"256":1},"1":{"114":1,"115":1,"116":1,"117":1,"118":1,"119":1,"120":1,"121":1,"122":1,"123":1,"176":1,"177":1},"2":{"5":10,"14":1,"18":1,"39":2,"53":16,"67":2,"97":1,"101":1,"113":2,"114":5,"115":2,"117":1,"118":1,"132":2,"136":5,"141":1,"146":2,"148":1,"164":3,"166":9,"167":9,"168":17,"169":4,"170":7,"171":6,"172":2,"173":2,"174":4,"175":2,"177":3,"178":1,"184":1,"185":1,"186":7,"187":29,"188":13,"192":14,"193":39,"195":4,"197":3,"199":16,"200":4,"201":6,"202":4,"203":2,"205":30,"207":3,"208":6,"212":1,"216":14,"217":20,"218":15,"220":6,"221":2,"222":3,"224":1,"249":1,"256":3,"259":2,"273":1,"283":1,"671":1,"686":1,"767":2,"768":1,"769":1}}],["c4af68c4f7959b154eb5380aa93c894e2e63fe4e",{"2":{"5":2,"185":1}}],["caveats",{"2":{"685":1}}],["calculations",{"2":{"253":1}}],["calculate",{"2":{"252":1}}],["callable",{"2":{"667":1}}],["calltrigger",{"2":{"90":1}}],["call",{"0":{"90":1},"2":{"39":1,"67":1,"85":1,"207":1,"273":1,"665":1}}],["called",{"2":{"3":1,"28":1,"40":1,"52":1,"59":1,"100":1,"138":1,"154":1,"166":1,"168":1,"170":1,"187":3,"200":1,"211":1,"230":1,"733":1,"759":1}}],["calling",{"2":{"3":2,"17":1,"188":1,"273":1,"665":1}}],["cache",{"2":{"249":1}}],["cacti",{"2":{"236":1}}],["cactus",{"2":{"236":1}}],["causing",{"2":{"755":1}}],["cause",{"2":{"97":1,"126":1,"137":1,"272":1,"665":1,"667":1}}],["caused",{"2":{"32":1,"97":1,"106":1,"687":1}}],["cautious",{"2":{"249":1,"261":1}}],["caution",{"2":{"249":1,"259":1}}],["casually",{"2":{"224":1}}],["cast",{"2":{"202":2}}],["cases",{"2":{"42":1,"62":2,"137":4,"138":1,"141":1,"247":1,"665":1,"722":1}}],["case",{"2":{"28":1,"46":1,"69":1,"90":1,"94":1,"104":1,"106":1,"116":1,"128":1,"137":1,"141":2,"148":1,"154":1,"166":1,"170":1,"176":1,"186":1,"193":6,"211":1,"232":1,"250":1,"253":1,"256":1,"268":2,"280":1,"695":1,"753":1}}],["came",{"2":{"180":1}}],["catering",{"2":{"267":1}}],["category",{"2":{"86":1,"137":1,"701":1}}],["catch",{"2":{"137":1,"273":1}}],["cabbage",{"2":{"128":4,"164":4,"201":2,"202":2}}],["captured",{"2":{"759":1}}],["capturing",{"2":{"125":1}}],["capable",{"2":{"241":1,"253":1,"677":1}}],["capabilities",{"2":{"167":1,"213":1,"671":1}}],["capacity",{"2":{"18":1,"143":10,"164":10,"174":2}}],["carry",{"2":{"667":1}}],["carpenter",{"2":{"128":2,"164":2,"200":2,"201":2,"202":2,"203":2}}],["careful",{"2":{"259":1}}],["carefully",{"2":{"45":1}}],["care",{"2":{"100":1,"101":1,"103":1,"186":1,"190":1,"231":1,"716":1}}],["cargo$",{"2":{"280":1,"282":1}}],["cargo",{"2":{"6":2,"22":4,"103":3,"104":1,"105":1,"108":2,"134":2,"170":2,"176":1,"177":2,"216":1,"231":1,"238":2,"239":8,"241":2,"279":5,"280":1,"282":7}}],["canregisterdomains",{"0":{"714":1},"2":{"701":1,"714":3}}],["canremovekeyvalueinassetdefinition",{"0":{"713":1},"2":{"55":1,"701":1,"713":3}}],["canremovekeyvalueinuseraccount",{"2":{"128":2,"164":2}}],["canremovekeyvalueinuserassets",{"0":{"709":1},"2":{"55":1,"701":1,"709":3}}],["canremovekeyvalueinusermetadata",{"0":{"711":1},"2":{"53":2,"55":1,"60":2,"701":1,"711":3}}],["canunregisterassetwithdefinition",{"0":{"705":1},"2":{"701":1,"705":3}}],["canburnassetwithdefinition",{"0":{"703":1},"2":{"701":1,"703":3}}],["canburnuserassets",{"0":{"704":1},"2":{"701":1,"704":3}}],["canmintuserassetdefinitions",{"0":{"702":1},"2":{"701":1,"702":3}}],["cantransferonlyfixednumberoftimesperperiod",{"0":{"707":1},"2":{"701":1,"707":3}}],["cantransferuserassets",{"0":{"706":1},"2":{"701":1,"706":3}}],["cansetparameters",{"2":{"128":2,"164":2}}],["cansetkeyvalueinassetdefinition",{"0":{"712":1},"2":{"55":1,"701":1,"712":3}}],["cansetkeyvalueinuseraccount",{"2":{"128":2,"164":2}}],["cansetkeyvalueinuserassets",{"0":{"708":1},"2":{"55":1,"701":1,"708":3}}],["cansetkeyvalueinusermetadata",{"0":{"710":1},"2":{"53":2,"55":1,"60":2,"701":1,"710":3}}],["cannot",{"2":{"18":1,"28":2,"97":1,"135":1,"137":1,"162":1,"180":1,"190":2,"220":1,"249":2,"259":1,"266":1,"272":1,"273":2,"665":1,"674":1}}],["can",{"2":{"5":1,"10":2,"13":2,"16":5,"18":3,"21":1,"24":4,"25":1,"27":1,"29":1,"32":1,"36":5,"37":1,"38":1,"40":1,"41":3,"42":7,"43":4,"44":1,"45":1,"47":1,"50":3,"52":1,"54":1,"56":2,"58":1,"59":1,"61":1,"62":1,"64":1,"65":3,"67":2,"68":1,"69":2,"71":1,"72":3,"78":2,"80":2,"82":2,"83":1,"84":2,"88":3,"89":1,"90":3,"91":1,"94":2,"95":1,"97":3,"98":2,"99":1,"101":3,"105":2,"107":1,"108":1,"109":1,"110":2,"114":1,"118":1,"120":1,"121":1,"124":1,"125":2,"126":1,"127":1,"128":2,"129":2,"130":1,"131":1,"134":2,"137":8,"138":2,"140":1,"141":3,"144":1,"146":2,"148":1,"151":3,"154":3,"155":1,"161":1,"164":1,"168":2,"170":2,"171":2,"172":1,"174":1,"176":2,"178":3,"180":2,"183":1,"186":3,"187":7,"188":2,"189":2,"190":1,"191":1,"193":2,"195":1,"197":2,"198":1,"199":1,"200":2,"204":1,"207":1,"208":2,"210":1,"211":2,"214":2,"216":1,"219":2,"220":6,"222":1,"226":1,"227":1,"230":4,"232":1,"233":1,"234":1,"237":1,"238":4,"241":1,"243":1,"245":1,"246":3,"248":1,"249":3,"250":1,"252":2,"253":3,"254":1,"255":2,"256":1,"258":1,"259":1,"263":1,"264":2,"266":2,"268":2,"269":2,"270":1,"272":2,"273":2,"278":1,"279":1,"281":1,"283":1,"665":3,"667":4,"669":1,"670":1,"671":4,"673":2,"674":1,"675":1,"682":1,"687":1,"689":1,"693":1,"697":1,"699":1,"702":1,"703":1,"704":1,"705":1,"706":1,"707":1,"708":1,"709":1,"710":1,"711":1,"712":1,"713":1,"714":1,"715":2,"716":3,"717":1,"741":1,"759":1,"764":1,"769":1,"772":2,"776":1}}],["coins",{"2":{"673":1}}],["cooperation",{"2":{"257":1}}],["cookies",{"2":{"249":2}}],["coding",{"2":{"223":1}}],["codegen",{"2":{"105":2}}],["codecs",{"2":{"186":1}}],["codec",{"2":{"94":1,"103":1,"231":1,"777":2}}],["code",{"0":{"0":1,"231":1},"1":{"1":1,"2":1,"3":1,"4":1,"5":1},"2":{"0":1,"2":2,"3":1,"4":3,"5":1,"20":1,"79":1,"92":1,"97":2,"99":1,"103":1,"104":1,"105":2,"108":1,"124":1,"137":2,"141":1,"143":1,"170":1,"177":1,"178":1,"188":1,"193":5,"197":1,"199":2,"201":3,"204":1,"209":2,"212":1,"214":2,"216":1,"218":3,"231":5,"249":1,"272":1,"665":1,"667":1,"693":1,"716":1}}],["co",{"2":{"197":2,"198":16,"199":24,"204":8,"205":40,"231":1}}],["cost",{"2":{"137":1,"218":1}}],["copies",{"2":{"258":1}}],["copied",{"2":{"135":1,"249":1}}],["copying",{"2":{"17":1,"154":1,"272":1}}],["copy",{"2":{"6":1,"8":1,"16":3,"17":2,"20":1,"137":2,"167":2,"168":1,"170":1,"189":1,"197":2,"210":1,"216":3,"219":1,"238":1,"253":1,"272":1,"764":1}}],["corda",{"2":{"236":1}}],["coroutines",{"2":{"197":4,"199":5}}],["corner",{"2":{"187":1}}],["core",{"0":{"691":1},"2":{"108":1,"178":1,"186":3,"187":2,"193":6,"197":4,"213":4,"216":1,"217":2,"246":1,"270":1,"669":1,"684":1,"693":1}}],["correctness",{"2":{"667":1}}],["correct",{"2":{"137":1,"157":1,"220":1,"263":1,"273":2}}],["correctly",{"2":{"18":1,"168":1,"199":1,"204":1}}],["corresponding",{"2":{"29":1,"94":1,"125":1,"127":1,"209":1,"255":1,"268":1,"667":1,"702":1,"703":1,"705":1,"712":1,"713":1,"727":1,"742":1,"743":1,"751":1,"752":1,"759":1,"762":1,"776":1}}],["correspond",{"2":{"20":1,"21":1}}],["corruption",{"2":{"6":1}}],["coverage",{"2":{"235":2}}],["covering",{"2":{"187":1}}],["covers",{"2":{"178":2}}],["cover",{"2":{"28":1,"46":1,"85":1,"110":1,"141":1,"147":1,"152":1,"186":1,"197":1,"216":1}}],["cousins",{"2":{"220":1}}],["course",{"2":{"81":1,"90":1,"217":1,"282":1,"665":1}}],["counter",{"2":{"209":1,"258":1,"771":4}}],["counterpart",{"2":{"188":1}}],["counterparts",{"2":{"21":1}}],["count",{"2":{"57":2,"84":1,"151":1,"707":4,"771":2}}],["couple",{"2":{"40":1,"210":1}}],["couldn",{"2":{"218":1}}],["could",{"2":{"2":1,"5":1,"16":1,"17":1,"21":1,"36":2,"62":2,"88":2,"89":1,"97":2,"109":1,"136":1,"137":1,"141":2,"153":1,"160":1,"167":1,"168":1,"171":1,"183":1,"186":1,"187":1,"190":3,"208":1,"211":1,"216":2,"217":1,"218":1,"230":1,"256":1,"268":1,"282":2,"665":1}}],["column",{"2":{"287":2}}],["colors",{"2":{"143":4,"164":4}}],["color",{"2":{"52":4}}],["colons",{"2":{"18":1}}],["collected",{"2":{"716":1}}],["collection",{"2":{"39":1,"71":1,"170":4,"247":1,"334":1,"335":1,"336":1,"716":2,"730":1}}],["collaboration",{"2":{"236":1}}],["collisions",{"2":{"18":1}}],["conflate",{"2":{"753":1}}],["conforms",{"2":{"667":1}}],["confinement",{"2":{"249":1}}],["confidentiality",{"2":{"245":1,"255":1}}],["confirm",{"2":{"169":1,"259":1,"260":1}}],["confirmation",{"2":{"169":1,"170":1,"195":1,"265":1,"266":1,"767":1}}],["configbuilder",{"2":{"187":1}}],["configs",{"2":{"13":2,"16":12,"17":2,"18":8,"21":1,"22":1,"115":1,"128":2,"130":1,"131":1,"132":3,"143":1,"145":1,"164":3,"166":1,"167":2,"207":2,"216":3,"217":4,"272":1}}],["configuring",{"0":{"168":1,"198":1,"208":1,"217":1,"265":1},"1":{"266":1},"2":{"252":1,"264":1}}],["configured",{"0":{"58":1},"2":{"55":1,"58":1,"135":1,"166":1,"187":1,"188":1,"272":1,"700":1,"701":2,"755":1}}],["configure",{"2":{"11":1,"122":1,"146":1,"151":1,"166":1,"168":2,"174":1,"178":1,"225":1,"263":1,"265":1}}],["configurationevent",{"0":{"332":1},"2":{"339":1}}],["configurations",{"2":{"131":1,"138":1,"147":1,"151":1,"152":1,"214":1,"216":1}}],["configuration",{"0":{"8":1,"113":1,"124":1,"131":1,"142":1,"143":1,"164":1,"187":1,"271":1},"1":{"114":1,"115":1,"116":1,"117":1,"118":1,"119":1,"120":1,"121":1,"122":1,"123":1,"125":1,"126":1,"127":1,"144":1,"145":1,"146":1,"147":1,"148":1,"149":1,"150":1,"151":1,"152":1,"153":1,"154":1,"155":1,"272":1,"273":1},"2":{"6":1,"8":1,"10":1,"11":1,"13":5,"16":3,"17":2,"18":1,"21":2,"22":2,"33":1,"41":1,"47":1,"111":1,"113":2,"114":1,"115":1,"117":1,"118":1,"119":1,"124":5,"125":5,"127":1,"128":2,"131":2,"132":3,"135":3,"137":1,"143":8,"144":1,"145":1,"146":1,"147":1,"148":1,"149":1,"150":1,"151":3,"155":1,"158":1,"161":2,"164":5,"166":2,"167":1,"168":3,"174":2,"178":3,"186":1,"187":1,"193":1,"197":1,"198":4,"199":1,"207":2,"208":2,"216":1,"217":10,"218":1,"238":3,"244":1,"271":1,"273":1,"285":1,"339":1,"676":1,"680":1,"717":1,"753":1}}],["config",{"2":{"8":2,"13":1,"16":8,"18":8,"22":1,"40":1,"114":4,"115":1,"125":1,"126":1,"127":2,"128":1,"131":1,"132":2,"135":1,"143":3,"144":4,"145":1,"146":1,"161":1,"164":6,"166":4,"167":2,"168":16,"170":2,"193":18,"198":1,"207":5,"208":4,"216":8,"217":21,"238":1,"698":1}}],["conduct",{"2":{"260":2}}],["conditions",{"2":{"106":1,"214":1,"684":1}}],["condition",{"2":{"41":1,"49":1,"170":4,"260":1,"289":1,"331":1,"456":1}}],["conditional",{"0":{"331":1},"2":{"36":1,"233":1,"457":1}}],["conditionals",{"0":{"36":1},"2":{"100":1,"101":1}}],["conjunction",{"2":{"253":1}}],["conversion",{"2":{"220":1,"367":1,"459":1,"550":1,"667":3,"772":2}}],["conversions",{"2":{"188":1}}],["convert",{"2":{"218":1}}],["converting",{"2":{"211":1}}],["converted",{"2":{"200":1,"667":2}}],["conventional",{"2":{"263":1}}],["conventions",{"0":{"699":1,"716":1},"2":{"42":1,"209":1,"699":1,"716":1}}],["convenience",{"2":{"16":1,"166":1,"170":1,"187":1}}],["conveniently",{"2":{"10":1,"134":1}}],["convenient",{"2":{"3":1,"101":1,"218":1}}],["concrete",{"2":{"212":1}}],["concurrently",{"2":{"229":1}}],["concurrent",{"2":{"205":8}}],["concept",{"0":{"236":1},"2":{"253":1,"665":1,"671":1}}],["concepts",{"2":{"178":1,"197":1,"216":1,"224":1}}],["concerns",{"2":{"260":1}}],["concern",{"2":{"189":1,"219":1}}],["concatenated",{"2":{"186":1}}],["conclusion",{"0":{"110":1}}],["consumer",{"2":{"198":4}}],["consulting",{"2":{"216":1}}],["consult",{"2":{"177":1,"233":1,"243":1,"244":1,"282":1}}],["console",{"2":{"188":2,"192":6,"195":2,"202":1}}],["consist",{"2":{"193":1}}],["consists",{"2":{"111":1,"186":2,"287":1}}],["considers",{"2":{"268":1}}],["considering",{"2":{"247":1}}],["consideration",{"2":{"190":1}}],["considerations",{"2":{"137":1}}],["considerably",{"2":{"42":1}}],["consider",{"2":{"67":1,"88":1,"136":1,"244":1,"247":1,"249":1,"250":2,"259":1,"260":4,"268":1}}],["considered",{"2":{"63":1,"249":1,"252":1,"268":1,"676":1}}],["consequences",{"2":{"245":1,"665":1}}],["consequence",{"2":{"28":1}}],["consensusblockrejection",{"2":{"328":1}}],["consensus",{"0":{"28":1},"2":{"28":5,"88":5,"97":1,"163":1,"230":3,"325":1,"679":1,"695":1,"777":1}}],["constrained",{"2":{"238":1}}],["constructs",{"2":{"209":1,"699":5}}],["construction",{"2":{"69":1,"99":1,"157":1,"160":1,"190":1,"218":1,"716":1}}],["constructed",{"2":{"67":1}}],["construct",{"2":{"66":1,"67":1,"217":1,"220":2,"716":1}}],["constantly",{"2":{"252":1}}],["constant",{"2":{"174":1,"226":1}}],["constants",{"2":{"5":1,"216":2}}],["const",{"2":{"5":5,"187":24,"188":10,"189":3,"190":6,"191":14,"192":19,"193":24,"195":6,"198":6,"666":2,"707":3}}],["connects",{"2":{"263":1,"268":1,"769":1}}],["connection",{"2":{"151":1,"218":1,"258":1}}],["connections",{"2":{"118":1,"119":1,"147":1}}],["connected",{"2":{"28":1,"214":1,"249":1,"258":1,"268":1,"771":8,"775":2}}],["connect",{"2":{"22":1,"135":1,"151":1,"263":1,"272":1,"671":1}}],["contact",{"2":{"271":1,"274":1,"277":1,"283":1,"284":1,"285":1}}],["contained",{"2":{"671":1}}],["containerised",{"2":{"213":1}}],["containers",{"2":{"13":1,"198":8,"213":1,"214":1,"272":5}}],["container",{"0":{"6":1,"333":1},"1":{"7":1,"8":1,"9":1},"2":{"6":7,"7":2,"8":2,"9":2,"128":2,"213":9,"214":1,"253":1,"272":1,"636":2,"671":1}}],["containing",{"2":{"177":1,"258":1,"265":1,"677":1,"678":1,"772":1}}],["contain",{"2":{"50":1,"115":1,"145":1,"168":1,"169":1,"199":1,"202":1,"238":1,"247":1,"719":1}}],["containsall",{"0":{"335":1},"2":{"376":2}}],["containsany",{"0":{"336":1},"2":{"170":8,"376":2}}],["contains",{"0":{"334":1},"2":{"32":1,"51":1,"53":2,"72":1,"103":2,"111":1,"128":1,"154":1,"186":2,"216":2,"219":2,"220":2,"273":1,"288":1,"376":2,"600":1,"665":1,"690":1,"764":1}}],["continuous",{"2":{"260":3,"287":1}}],["continuing",{"2":{"211":1,"282":1}}],["continues",{"2":{"195":1,"767":1}}],["continue",{"2":{"170":1}}],["content",{"2":{"143":2,"164":2,"775":1}}],["contents",{"2":{"16":1,"112":1,"696":1,"750":1}}],["contextvalue",{"0":{"337":1},"2":{"170":16,"376":2}}],["context",{"2":{"62":1,"263":1,"672":1}}],["contributors",{"2":{"270":1}}],["contributing",{"2":{"228":1}}],["contributes",{"2":{"248":1}}],["contribute",{"2":{"228":1,"260":1}}],["contrary",{"2":{"220":1}}],["contract",{"0":{"102":1,"104":1},"1":{"103":1,"104":1},"2":{"90":2,"93":1,"97":1,"104":2,"106":1,"110":1,"141":1,"233":1}}],["contracts",{"0":{"105":1,"233":1,"684":1},"1":{"106":1,"107":1,"108":1,"109":1,"110":1},"2":{"36":1,"79":1,"88":1,"100":1,"105":1,"227":1,"233":2,"669":1,"684":2,"689":1}}],["contrast",{"2":{"57":1,"65":1,"204":1,"220":1,"222":1,"230":1}}],["controls",{"2":{"246":1,"260":2}}],["control",{"2":{"20":1,"117":1,"213":1,"230":1,"260":3,"264":1}}],["comic",{"2":{"253":1}}],["come",{"2":{"231":1,"238":1,"252":1,"769":1}}],["comes",{"2":{"4":1,"28":1,"108":1,"141":1,"218":1,"249":1,"262":1,"665":1}}],["comfortable",{"2":{"216":1}}],["combs",{"2":{"72":1}}],["combining",{"2":{"62":1,"255":1}}],["combined",{"2":{"65":1,"246":1}}],["combine",{"2":{"36":1,"69":1}}],["combinations",{"2":{"252":5}}],["combination",{"2":{"6":1,"95":1,"686":1}}],["community",{"2":{"270":2}}],["communicating",{"2":{"259":2}}],["communication",{"2":{"149":1,"170":1,"246":1,"247":1,"250":1,"254":1,"259":1,"270":1,"687":1,"767":1}}],["communications",{"2":{"18":1,"187":1,"237":1,"254":1}}],["communicate",{"2":{"28":1,"187":2,"195":1,"263":1}}],["comments",{"2":{"235":1}}],["comment",{"2":{"167":2}}],["commonly",{"2":{"134":1,"260":1,"682":1}}],["common",{"2":{"28":1,"126":1,"259":1,"271":1,"280":2,"722":1,"753":1}}],["commitment",{"2":{"267":1}}],["commits",{"2":{"197":1}}],["committime",{"2":{"755":2}}],["committimelimit=4000",{"2":{"128":2,"164":2}}],["committing",{"2":{"72":1,"204":1,"222":1}}],["committed",{"2":{"28":1,"31":1,"32":2,"53":2,"87":1,"88":1,"89":1,"135":1,"174":3,"193":6,"212":3,"546":1,"547":1,"757":2,"769":4,"771":2,"775":2}}],["commit",{"0":{"89":1},"2":{"21":1,"85":1,"87":2,"89":3,"143":2,"164":2,"197":4,"325":1,"685":1,"755":2}}],["commands",{"2":{"16":1,"171":1}}],["command",{"2":{"6":1,"16":2,"18":3,"104":1,"134":1,"166":2,"167":2,"168":2,"170":2,"171":1,"217":1,"222":1,"238":2,"242":1,"272":2,"279":1,"282":2}}],["compromising",{"2":{"268":1}}],["compromised",{"2":{"250":1}}],["compromise",{"2":{"249":1}}],["comprehensive",{"2":{"260":2}}],["component",{"2":{"194":1}}],["components",{"0":{"678":1},"1":{"679":1,"680":1,"681":1,"682":1,"683":1,"684":1,"685":1,"686":1,"687":1},"2":{"193":12,"669":1}}],["compositions",{"2":{"690":1}}],["composite",{"0":{"49":1},"2":{"40":1,"49":1,"698":1}}],["compose",{"0":{"272":1},"2":{"13":1,"18":3,"19":1,"21":1,"174":1,"207":1,"213":4,"214":2,"216":1,"272":4}}],["compendium",{"2":{"187":1}}],["compilation",{"2":{"105":2,"107":1,"214":1}}],["compiling",{"2":{"105":2,"110":1,"177":1}}],["compiles",{"2":{"177":1}}],["compiler",{"2":{"104":1}}],["compilers",{"2":{"101":1}}],["compiled",{"2":{"39":1,"71":1,"100":1,"106":2,"186":3,"188":1,"242":1,"717":1}}],["compile",{"0":{"108":1},"2":{"14":1,"108":1,"177":1,"231":1,"232":1,"665":1}}],["companies",{"2":{"247":1}}],["companion",{"2":{"198":2,"205":18}}],["compatibility",{"0":{"287":1},"2":{"180":1,"231":1,"287":2}}],["compatible",{"2":{"177":1,"187":1,"197":2,"200":1,"207":2,"229":1}}],["comparison",{"2":{"177":1,"178":1,"229":1,"253":1}}],["compare",{"2":{"42":1,"252":1,"253":1}}],["compared",{"2":{"6":1,"83":1,"87":1,"97":1,"674":1}}],["compactcombination",{"2":{"239":1}}],["compact",{"2":{"100":1,"143":2,"164":2,"174":2,"238":1,"268":1,"775":2}}],["computed",{"2":{"193":4}}],["computers",{"2":{"248":1}}],["computer",{"2":{"39":2,"248":1}}],["computation",{"2":{"89":1}}],["compliant",{"2":{"241":1,"665":1}}],["complicated",{"2":{"39":1}}],["completablefuture",{"2":{"205":14}}],["completely",{"2":{"63":1,"127":1,"267":1}}],["complete",{"2":{"13":1,"97":1,"101":1,"159":1,"193":1,"197":1,"204":1,"207":1,"213":1,"216":2,"222":1,"229":1}}],["complexities",{"2":{"252":1}}],["complex",{"2":{"101":1,"233":1}}],["com",{"2":{"2":2,"5":5,"103":2,"168":2,"183":2,"186":2,"197":8,"207":2}}],["ps",{"2":{"272":2}}],["pyo3",{"2":{"207":1}}],["python3",{"2":{"283":2}}],["pythonlistener",{"2":{"212":1}}],["pythonfor",{"2":{"212":1}}],["pythonfilter",{"2":{"212":1}}],["pythonfrom",{"2":{"209":1,"210":1}}],["pythonamount",{"2":{"211":1}}],["pythonhash",{"2":{"209":1,"210":1}}],["pythonimport",{"2":{"208":1,"211":1}}],["python$",{"2":{"207":1}}],["python",{"0":{"206":1,"283":1},"1":{"207":1,"208":1,"209":1,"210":1,"211":1,"212":1},"2":{"27":1,"29":1,"42":1,"43":1,"136":1,"146":1,"166":4,"178":2,"206":1,"207":12,"208":3,"209":3,"211":1,"225":1,"278":1,"283":4}}],["phrase",{"0":{"269":1},"2":{"253":3,"269":1}}],["phone",{"2":{"253":1,"259":1}}],["phones",{"2":{"248":1}}],["phishing",{"2":{"249":1,"259":1}}],["philosophy",{"2":{"90":1}}],["physically",{"0":{"267":1},"1":{"268":1,"269":1},"2":{"267":1}}],["physical",{"2":{"247":1,"253":1,"258":2,"260":3,"263":1,"267":1}}],["phantomdata",{"2":{"170":12}}],["phased",{"2":{"166":1}}],["phase",{"2":{"137":1}}],["pivotal",{"2":{"260":1}}],["picking",{"2":{"253":1}}],["pieces",{"2":{"216":1}}],["pip3",{"2":{"283":2}}],["pip",{"2":{"207":4,"283":10}}],["pipelinerejectionreason",{"0":{"545":1},"2":{"546":1}}],["pipelinestatuskind",{"0":{"516":1,"547":1},"2":{"193":4,"516":1,"544":1}}],["pipelinestatus",{"0":{"546":1},"2":{"193":4,"543":1}}],["pipelineevent",{"0":{"543":1},"2":{"368":1}}],["pipelineeventfilter",{"0":{"544":1},"2":{"193":4,"204":1,"222":3,"378":1,"625":1}}],["pipelineentitykind",{"0":{"515":1,"542":1},"2":{"193":4,"515":1,"543":1,"544":1}}],["pipeline$",{"2":{"174":1}}],["pipeline",{"0":{"32":1},"2":{"31":1,"32":2,"89":1,"94":1,"168":1,"171":2,"174":9,"193":4,"204":9,"212":9,"218":1,"222":3,"368":1,"378":1,"625":1}}],["pitfalls",{"2":{"137":1}}],["p$",{"2":{"130":1}}],["plugins",{"2":{"197":2,"232":1,"249":4}}],["plus",{"2":{"88":1,"187":1,"222":1}}],["please",{"2":{"177":1,"186":2,"193":1,"214":1,"228":2,"233":1,"283":1,"285":1,"764":1}}],["platform",{"2":{"283":1}}],["platforms",{"2":{"197":1,"234":1,"259":1}}],["planned",{"2":{"278":1}}],["planning",{"2":{"186":1}}],["plans",{"2":{"235":1}}],["plan",{"0":{"250":1},"2":{"134":1,"136":1,"216":1,"250":5}}],["playing",{"2":{"665":1}}],["play",{"2":{"97":1,"186":1,"237":1}}],["plain",{"2":{"97":1,"256":1}}],["places",{"2":{"166":1,"273":1,"443":1}}],["place",{"2":{"5":1,"43":1,"125":1,"166":1,"186":1,"217":1,"258":1,"667":1,"695":1}}],["pulled",{"2":{"272":1}}],["pull",{"2":{"213":1}}],["pullpolicy",{"2":{"198":6}}],["purchase",{"2":{"673":1}}],["purpose",{"2":{"217":1,"248":1}}],["purposes",{"2":{"78":1,"166":1,"170":1,"186":1,"200":1,"222":1,"264":1,"750":1}}],["pure",{"0":{"205":1}}],["push",{"2":{"193":2,"702":2,"703":2}}],["pub",{"2":{"666":3,"755":1,"757":1}}],["pubkey",{"2":{"189":4}}],["published",{"2":{"186":2}}],["publish",{"2":{"16":1}}],["publickey",{"0":{"548":1,"595":1,"642":1},"2":{"189":4,"199":2,"200":2,"217":2,"219":2,"289":1,"431":2,"493":1,"537":1,"573":1,"574":2,"595":1,"634":2,"642":1}}],["public",{"0":{"115":1,"118":1,"138":1,"145":1,"147":1,"157":1,"254":1,"273":1},"1":{"119":1,"120":1,"139":1,"140":1,"141":1,"148":1,"149":1,"150":1,"255":1,"256":1},"2":{"9":3,"18":48,"20":3,"21":2,"41":1,"42":2,"53":2,"56":2,"113":2,"115":3,"125":1,"126":2,"127":1,"128":2,"132":1,"134":1,"135":3,"138":1,"139":1,"140":1,"141":5,"143":9,"145":3,"146":9,"151":5,"157":1,"164":6,"170":7,"174":2,"178":1,"187":3,"189":2,"193":2,"198":2,"200":3,"202":2,"205":14,"210":7,"217":4,"219":2,"237":1,"238":2,"239":12,"246":2,"248":1,"249":1,"254":5,"255":4,"256":6,"258":1,"263":3,"273":1,"286":1,"537":1,"573":1,"670":1,"674":1,"702":2,"703":2,"722":2}}],["putting",{"2":{"210":1}}],["puts",{"2":{"104":1}}],["put",{"2":{"18":1,"28":1,"42":1,"137":1,"166":1,"189":2,"205":2}}],["p2p",{"0":{"149":1},"2":{"18":8,"20":2,"21":1,"22":1,"127":1,"143":3,"147":1,"149":1,"164":2,"168":2,"198":10,"213":16}}],["pwd",{"2":{"16":12}}],["p",{"2":{"14":2,"17":2,"130":2,"167":2,"177":2,"183":2,"193":16,"238":1,"239":2}}],["pod",{"2":{"671":1}}],["policies",{"2":{"260":4}}],["pool",{"2":{"252":1}}],["popular",{"2":{"236":1}}],["poc",{"2":{"236":1}}],["poured",{"2":{"97":1}}],["pours",{"2":{"97":1}}],["potential",{"2":{"247":1,"248":2,"249":1,"250":4,"260":2,"268":1,"665":1}}],["potentially",{"2":{"88":1,"180":1,"249":1,"263":1}}],["pot",{"2":{"92":1}}],["powerful",{"2":{"253":1}}],["power",{"2":{"65":1,"230":1}}],["pointer",{"2":{"666":1,"667":1}}],["pointers",{"2":{"217":1,"665":3}}],["point",{"2":{"25":1,"81":1,"88":2,"92":1,"104":2,"110":1,"125":1,"168":1,"211":1,"233":1,"235":1,"238":1,"282":1,"443":1,"772":1}}],["possession",{"2":{"268":1}}],["possessing",{"2":{"255":1}}],["possibilities",{"2":{"253":1}}],["possibility",{"2":{"103":1,"137":1,"212":1}}],["possible",{"2":{"5":1,"16":1,"18":1,"62":3,"77":1,"82":1,"141":1,"166":3,"170":1,"201":1,"204":1,"222":1,"232":1,"239":8,"249":1,"252":7,"256":1,"259":5,"263":1,"286":1,"749":1}}],["pose",{"2":{"249":1,"259":2}}],["posture",{"2":{"248":1}}],["post",{"2":{"247":1,"772":1,"777":1}}],["posted",{"2":{"18":1}}],["posing",{"2":{"261":1}}],["posix",{"2":{"241":1}}],["positive",{"2":{"25":1,"268":1}}],["portable",{"2":{"39":1,"100":1,"101":1,"103":2,"227":1,"234":1,"248":2,"249":1}}],["ports",{"2":{"18":10,"148":1,"198":2,"264":1}}],["port",{"2":{"11":1,"22":1,"148":1,"198":22,"264":1,"268":1,"585":1,"586":1,"587":1}}],["pedantry",{"2":{"220":1}}],["pending",{"2":{"193":2}}],["people",{"2":{"88":2,"156":1,"166":1,"224":1}}],["peereventfilter",{"0":{"395":1,"535":1},"2":{"395":1,"536":1}}],["peerevent",{"0":{"392":1,"527":1,"534":1},"2":{"339":1,"392":1,"536":1}}],["peerfilter",{"0":{"396":1,"536":1},"2":{"338":1,"396":1}}],["peerurl",{"2":{"199":4}}],["peerid",{"0":{"537":1,"641":1},"2":{"94":1,"198":6,"325":1,"431":1,"454":2,"527":1,"533":1,"534":2,"641":1}}],["peerbuilder",{"2":{"53":2}}],["peer0",{"2":{"21":2}}],["peer2",{"2":{"21":1}}],["peer1",{"2":{"21":1}}],["peer$",{"2":{"16":1}}],["peers",{"0":{"135":1,"140":1,"146":1,"159":1,"163":1},"1":{"160":1,"161":1,"162":1},"2":{"11":8,"18":13,"20":1,"21":1,"22":3,"28":11,"33":1,"41":2,"42":4,"88":1,"94":1,"111":2,"112":1,"117":1,"127":1,"128":1,"134":1,"135":10,"140":1,"143":4,"146":9,"149":2,"151":5,"158":1,"160":2,"161":1,"163":1,"164":2,"168":2,"199":1,"213":1,"214":1,"230":1,"272":3,"286":3,"671":3,"687":2,"693":2,"696":1,"754":1,"755":4,"766":1,"769":4,"771":8,"775":6,"776":2}}],["peer",{"0":{"143":1,"156":1,"161":1,"533":1,"671":1,"687":1,"753":1},"1":{"144":1,"145":1,"146":1,"147":1,"148":1,"149":1,"150":1,"151":1,"152":1,"153":1,"154":1,"155":1,"157":1,"158":1,"159":1,"160":1,"161":1,"162":1,"163":1,"690":1,"754":1,"755":1},"2":{"10":1,"13":3,"14":3,"16":11,"17":5,"18":14,"19":1,"20":4,"21":4,"22":4,"39":1,"42":1,"53":2,"65":2,"88":1,"104":1,"106":1,"119":1,"124":1,"126":1,"127":2,"128":5,"130":1,"131":2,"132":3,"135":6,"138":1,"140":2,"143":8,"144":5,"145":2,"146":1,"148":1,"151":1,"157":1,"159":1,"160":4,"161":1,"162":5,"163":4,"164":7,"166":1,"168":2,"170":1,"186":2,"187":1,"189":1,"193":1,"195":1,"198":2,"213":8,"214":1,"218":1,"219":1,"256":2,"272":11,"339":1,"431":1,"455":2,"555":2,"669":2,"671":2,"677":2,"680":1,"686":1,"692":1,"697":1,"715":1,"753":3,"754":1,"770":1,"771":2,"775":2}}],["pertain",{"2":{"725":1}}],["pertaining",{"2":{"204":1,"222":1}}],["percentage",{"2":{"677":1}}],["perpetually",{"2":{"220":1}}],["perhaps",{"2":{"217":2,"220":1}}],["persistence",{"2":{"681":1}}],["persistently",{"2":{"217":1}}],["persistent",{"2":{"97":1,"155":1,"166":1,"168":1}}],["persists",{"2":{"283":1}}],["personnel",{"2":{"247":1,"260":3}}],["person",{"2":{"208":1,"255":1}}],["personal",{"2":{"106":1,"248":1,"253":1,"259":3}}],["performed",{"2":{"62":1,"245":1}}],["perform",{"2":{"62":1,"137":2,"186":1,"244":2,"260":1,"265":2,"283":1,"667":1}}],["performance",{"2":{"10":3,"42":1,"120":1,"150":1,"232":1,"716":1}}],["periodic",{"2":{"260":1}}],["periodically",{"2":{"193":1,"248":1,"249":1,"250":1,"252":1}}],["period",{"2":{"57":4,"88":1,"143":6,"151":1,"164":6,"565":1,"674":1,"707":5}}],["per",{"2":{"57":1,"130":6,"143":4,"164":4,"220":1,"273":1,"707":3}}],["permits",{"2":{"263":1}}],["permissionparameter",{"2":{"458":1}}],["permissionremoved",{"0":{"538":1},"2":{"290":1,"561":2}}],["permissionadded",{"2":{"290":1}}],["permissioned",{"2":{"227":1}}],["permissiontokendefinition",{"2":{"723":1}}],["permissiontokenschema",{"0":{"540":1},"2":{"541":2,"634":2}}],["permissiontokenschemaupdateevent",{"0":{"541":1},"2":{"339":1}}],["permissiontokenid",{"2":{"454":1}}],["permissiontoken",{"0":{"539":1,"594":1},"2":{"56":1,"128":2,"160":1,"164":2,"339":1,"431":1,"560":1,"594":1,"634":2,"702":2,"703":2,"704":2,"705":2,"706":2,"708":2,"709":2,"710":2,"711":2,"712":2,"713":2,"714":2,"724":1}}],["permission",{"0":{"57":1,"58":1,"59":1,"62":1,"701":1,"722":1},"1":{"58":1,"60":1,"61":1,"63":1,"702":1,"703":1,"704":1,"705":1,"706":1,"707":1,"708":1,"709":1,"710":1,"711":1,"712":1,"713":1,"714":1,"723":1,"724":1},"2":{"33":2,"40":1,"41":4,"42":1,"43":2,"44":1,"45":1,"53":4,"56":6,"57":8,"58":1,"59":1,"60":4,"62":2,"64":2,"89":1,"111":2,"128":1,"129":1,"138":2,"139":1,"141":5,"160":1,"170":4,"227":1,"232":1,"294":1,"538":1,"698":1,"700":3,"701":3,"702":8,"703":9,"704":9,"705":9,"706":9,"707":5,"708":9,"709":9,"710":3,"711":3,"712":9,"713":9,"714":9,"719":1,"720":1,"722":1,"723":1,"724":1,"772":2}}],["permissions",{"0":{"55":1,"56":1,"139":1,"160":1,"700":1},"1":{"57":1,"58":1,"59":1,"60":1,"61":1,"62":1,"63":1,"64":1,"701":1,"702":1,"703":1,"704":1,"705":1,"706":1,"707":1,"708":1,"709":1,"710":1,"711":1,"712":1,"713":1,"714":1},"2":{"29":1,"40":1,"45":3,"56":3,"59":1,"62":2,"63":1,"82":1,"89":1,"128":2,"138":1,"139":2,"141":1,"160":2,"161":1,"164":2,"560":1,"698":1,"700":1,"701":1,"722":3}}],["permanently",{"2":{"45":1,"106":1,"244":1}}],["permanent",{"2":{"16":1}}],["permalink",{"2":{"5":1}}],["practice",{"2":{"247":1}}],["practices",{"2":{"189":1,"210":1,"219":1,"246":2,"247":1,"248":1,"261":2}}],["practical",{"2":{"137":1}}],["prioritising",{"2":{"687":1}}],["prices",{"2":{"670":1}}],["primarily",{"2":{"262":1}}],["primary",{"2":{"250":1,"263":1}}],["primitives",{"2":{"216":1}}],["primer",{"0":{"166":1}}],["pride",{"2":{"177":1}}],["privacy",{"2":{"249":4,"255":1}}],["privatekey",{"2":{"217":2,"273":2}}],["private",{"0":{"115":1,"138":1,"145":1,"158":1,"273":1},"1":{"139":1,"140":1,"141":1,"159":1,"160":1,"161":1,"162":1,"163":1},"2":{"18":11,"20":2,"42":3,"56":2,"113":2,"115":2,"125":1,"126":2,"127":1,"134":2,"135":5,"136":1,"138":1,"139":1,"140":1,"141":8,"143":8,"145":2,"151":4,"158":1,"161":1,"164":6,"170":6,"174":2,"178":1,"187":3,"189":2,"193":2,"199":14,"208":1,"210":2,"217":4,"219":2,"238":3,"239":13,"254":3,"255":4,"256":5,"258":1,"259":1,"261":3,"263":6,"265":1,"268":1,"269":1,"273":2,"717":1,"722":2}}],["privilege",{"2":{"697":2}}],["privileged",{"2":{"161":1,"732":1}}],["privileges",{"2":{"128":2,"135":1}}],["principle",{"2":{"665":1}}],["principles",{"0":{"257":1,"258":1,"259":1,"260":1},"1":{"258":1,"259":1,"260":1},"2":{"65":1,"178":1,"246":2,"247":1,"256":1,"257":1,"262":1}}],["print",{"2":{"212":2}}],["printout",{"2":{"169":1,"222":1}}],["prints",{"2":{"168":6}}],["println",{"2":{"4":3,"199":4,"200":4,"201":6,"202":10,"203":4,"222":4}}],["proactively",{"2":{"260":1}}],["proactive",{"2":{"248":1,"250":1}}],["protected",{"2":{"258":1}}],["protection",{"2":{"254":1,"258":1}}],["protecting",{"2":{"249":1,"251":1,"262":1,"264":1}}],["protect",{"2":{"245":1,"246":1,"248":1,"258":1,"260":1,"261":1}}],["protocol",{"2":{"247":1,"262":2,"263":4,"766":1,"767":2,"768":2,"770":1,"771":1,"772":1,"775":1,"777":1}}],["protocols",{"2":{"238":1,"247":1,"260":1}}],["proto",{"2":{"176":2}}],["proxified",{"2":{"193":2}}],["proxy",{"2":{"117":1,"755":2}}],["production",{"2":{"180":3,"212":1,"218":1,"256":1}}],["produces",{"2":{"104":1,"105":2}}],["produced",{"2":{"38":1,"94":1,"97":1}}],["produce",{"2":{"28":2,"101":1,"241":1}}],["problem",{"2":{"137":1,"154":1,"220":1,"278":1,"280":1,"282":1}}],["problems",{"2":{"100":2,"280":1}}],["profiles",{"2":{"249":2}}],["profile",{"2":{"105":2,"249":3}}],["projected",{"2":{"253":1}}],["project",{"0":{"103":1},"2":{"103":2,"134":1,"170":1,"193":2,"199":1,"200":1,"201":2,"236":1,"238":1}}],["projects",{"2":{"102":1,"229":2}}],["proprietary",{"2":{"248":1,"249":1}}],["properties",{"2":{"238":1}}],["property",{"2":{"235":1,"677":1}}],["properly",{"2":{"97":1,"168":1,"230":1,"263":1,"677":1}}],["proposals",{"2":{"11":2}}],["promptly",{"2":{"250":3,"260":2,"264":1}}],["prompt",{"2":{"65":1,"256":1}}],["prometheus",{"2":{"10":2,"120":3,"150":3,"231":1,"771":1}}],["proved",{"2":{"268":1}}],["prove",{"2":{"230":1,"247":1}}],["proven",{"2":{"63":1}}],["providing",{"2":{"126":1,"127":1,"167":1,"197":1,"212":1,"216":1,"253":1}}],["provided",{"2":{"32":1,"49":2,"87":1,"98":1,"101":2,"110":1,"122":1,"125":2,"126":1,"127":2,"136":1,"146":1,"187":1,"198":1,"207":1,"208":1,"216":1,"217":1,"224":1,"241":1,"266":1,"689":1,"691":1,"693":1,"716":1,"720":1,"728":1,"748":1}}],["provides",{"2":{"29":1,"136":1,"168":2,"186":4,"187":1,"195":1,"238":1,"254":1,"263":1,"264":1,"287":1,"665":1,"700":1,"767":1}}],["provide",{"2":{"10":1,"69":1,"85":1,"97":1,"116":1,"117":2,"125":1,"138":1,"143":1,"151":1,"166":1,"178":1,"187":2,"188":1,"189":1,"197":1,"204":1,"210":1,"216":1,"219":1,"231":1,"256":1,"260":1,"262":1,"287":1,"664":1,"665":1,"688":1,"692":1,"716":1}}],["progress",{"2":{"32":1,"97":1,"226":1,"272":1}}],["programs",{"2":{"166":4,"266":1,"684":1}}],["programmer",{"2":{"127":1}}],["programmers",{"2":{"100":1,"220":1}}],["programming",{"2":{"101":1,"103":1,"108":1,"199":1}}],["program",{"0":{"264":1},"1":{"265":1,"266":1},"2":{"10":1,"36":1,"103":1,"166":7,"197":1,"222":1,"252":2,"256":1,"263":1,"667":1}}],["prospective",{"2":{"28":1,"224":1}}],["proof",{"0":{"236":1},"2":{"28":1}}],["proceed",{"2":{"769":1}}],["processed",{"2":{"97":1,"204":1}}],["processes",{"2":{"42":1,"136":1,"141":1,"268":1,"671":1}}],["processing",{"2":{"65":1,"87":1,"97":1,"253":1}}],["processor",{"2":{"39":2}}],["process",{"2":{"16":1,"27":1,"28":4,"42":3,"43":1,"82":1,"87":1,"97":1,"101":1,"127":1,"137":2,"141":3,"159":1,"161":1,"162":2,"163":1,"176":3,"204":1,"210":1,"212":1,"213":1,"216":1,"222":1,"253":1,"255":1,"260":1,"263":1,"266":2,"667":1,"671":2,"695":1,"716":1}}],["procedures",{"2":{"250":1,"260":1}}],["procedure",{"2":{"6":1,"161":1}}],["prone",{"2":{"13":1,"19":1}}],["prerequisite",{"2":{"773":1,"774":1}}],["prerequisites",{"0":{"14":1,"181":1},"1":{"182":1},"2":{"179":1,"213":1}}],["preemptive",{"2":{"245":1}}],["preamble",{"0":{"224":1}}],["predicate",{"2":{"199":4,"321":1,"635":1}}],["predominantly",{"2":{"107":1}}],["preserving",{"2":{"279":1}}],["preserve",{"2":{"6":1}}],["presence",{"2":{"199":2,"260":1,"263":1}}],["presently",{"2":{"201":1}}],["present",{"2":{"198":1,"229":1,"287":1,"715":1,"717":1}}],["prepared",{"2":{"218":2,"250":1}}],["prepare",{"2":{"187":1,"218":2}}],["prev",{"2":{"603":1}}],["preventing",{"2":{"247":1}}],["prevent",{"2":{"137":1,"248":1,"262":1}}],["prevents",{"2":{"127":1}}],["previously",{"2":{"7":1,"97":1,"128":1,"178":1,"200":1,"252":1}}],["previous",{"0":{"7":1},"2":{"6":1,"97":1,"187":2,"189":1,"197":1,"216":1,"219":1,"220":2,"253":1,"325":1}}],["precommit",{"2":{"375":1}}],["precaution",{"2":{"250":1}}],["precedence",{"2":{"125":1}}],["precious",{"2":{"90":1}}],["precisely",{"2":{"154":1}}],["precise",{"2":{"79":1,"716":1}}],["precision",{"2":{"25":1,"50":1,"190":2,"775":2}}],["prefers",{"2":{"270":1}}],["preference",{"2":{"249":1}}],["preferences",{"2":{"134":1}}],["prefer",{"2":{"167":1,"183":1}}],["preferably",{"2":{"119":1}}],["prefix",{"2":{"119":1}}],["prebuilt",{"2":{"108":1}}],["premium",{"2":{"106":1}}],["prelude",{"2":{"104":2,"217":4,"218":2,"219":2,"222":2}}],["preliminaries",{"2":{"104":1}}],["pre",{"0":{"58":1,"89":1},"2":{"55":1,"58":1,"85":1,"87":2,"89":3,"168":1,"185":1,"188":1,"216":9,"700":1,"701":2}}],["painted",{"2":{"674":1}}],["painting",{"2":{"674":1}}],["paired",{"2":{"279":1}}],["pairs",{"0":{"134":1},"2":{"50":1,"52":3,"127":1,"134":1,"135":1,"137":1,"682":1}}],["pair",{"0":{"530":1},"2":{"22":1,"40":1,"41":1,"49":2,"51":1,"53":6,"61":2,"71":2,"115":1,"127":2,"134":2,"135":1,"143":2,"145":1,"164":2,"170":5,"187":5,"189":1,"200":3,"210":1,"219":1,"238":4,"254":1,"256":2,"263":2,"273":1,"286":1,"457":2,"461":1,"698":1,"702":2,"703":2,"710":1,"711":1}}],["panel",{"2":{"265":1}}],["panics",{"2":{"105":2,"231":1}}],["panic",{"2":{"105":2,"106":2,"108":1,"143":2,"164":2,"759":1}}],["pales",{"2":{"253":1}}],["patches",{"2":{"249":1}}],["patching",{"2":{"234":1}}],["patch",{"2":{"234":1,"248":1}}],["paths",{"2":{"16":1,"283":1}}],["path=",{"2":{"16":12,"244":4}}],["path",{"0":{"154":1},"2":{"2":1,"16":4,"126":1,"143":6,"152":1,"154":2,"155":1,"164":6,"166":1,"168":2,"174":2,"216":8,"244":10,"283":1,"633":1,"776":1}}],["page",{"2":{"197":1,"238":1,"249":1,"288":1,"764":3}}],["pagination",{"0":{"67":1},"2":{"67":9,"69":2}}],["paginated",{"2":{"65":1,"715":1}}],["padstart",{"2":{"193":2}}],["padding",{"2":{"103":1,"193":2}}],["paying",{"2":{"249":1}}],["pay",{"2":{"190":1}}],["payload",{"2":{"18":10,"20":2,"113":2,"128":6,"134":1,"164":8,"170":6,"174":2,"187":2,"189":2,"193":2,"239":2,"539":1,"548":1,"573":1,"580":1,"581":1,"582":1}}],["packaged",{"2":{"231":1}}],["package",{"2":{"52":1,"103":2,"154":1,"176":1,"186":7,"187":1,"197":1,"198":1,"205":1,"216":2,"282":1,"283":1}}],["packages",{"2":{"5":4,"186":6,"199":1,"207":1,"248":1}}],["pasting",{"2":{"238":1}}],["paste",{"2":{"16":1,"20":2}}],["passphrase",{"2":{"263":1}}],["passed",{"2":{"187":1,"190":1,"220":1,"287":1,"777":1}}],["passes",{"2":{"63":1}}],["passwords",{"2":{"246":1,"248":2,"250":1,"251":1,"252":4,"253":3,"256":1,"259":2,"264":2,"268":1}}],["password",{"0":{"251":1,"252":1,"253":1,"264":1},"1":{"252":1,"253":1,"265":1,"266":1},"2":{"113":2,"117":4,"164":2,"170":2,"238":1,"246":3,"247":1,"250":1,"251":1,"252":22,"253":6,"258":1,"259":1,"263":3,"264":4,"266":1,"269":1}}],["passing",{"2":{"98":1,"187":2}}],["pass",{"2":{"22":1,"88":1,"187":3,"195":1,"287":1,"636":1}}],["parlance",{"2":{"753":1}}],["parsing",{"2":{"217":1}}],["parse",{"2":{"52":4,"74":1,"211":3,"217":4,"218":2,"219":6,"702":4,"703":4,"775":1}}],["parity",{"2":{"94":1,"103":1,"222":1,"231":2,"775":2,"777":1}}],["paramount",{"2":{"245":1,"251":1}}],["parametric",{"2":{"94":1}}],["parameterised",{"2":{"716":1}}],["parameterid",{"0":{"532":1},"2":{"332":3,"431":1,"454":2,"531":1}}],["parametervaluetype",{"2":{"626":1}}],["parameter",{"0":{"358":1,"531":1},"2":{"40":1,"57":3,"128":22,"134":1,"164":22,"187":1,"238":3,"431":1,"455":2,"496":2,"572":2,"698":1,"755":3,"772":4}}],["parameters",{"2":{"6":1,"41":1,"47":1,"57":6,"111":1,"134":1,"137":1,"170":2,"238":3,"239":2,"716":3,"720":1,"721":1,"724":1,"727":1,"728":1,"729":1,"730":1,"731":1,"735":1,"736":1,"737":1,"738":1,"739":1,"740":1,"741":1,"742":1,"743":1,"744":1,"748":1,"751":1,"752":1,"755":1,"757":1,"758":1,"761":1,"762":1,"763":1,"772":1}}],["params",{"2":{"57":6}}],["parallels",{"2":{"39":1}}],["paradigm",{"2":{"37":1,"212":2}}],["partialord",{"2":{"772":1}}],["partially",{"2":{"187":1}}],["parties",{"2":{"247":1,"259":1,"264":1,"693":1}}],["participants",{"2":{"236":1}}],["participates",{"2":{"137":1}}],["participate",{"2":{"28":1}}],["particular",{"2":{"97":1,"99":1,"125":1,"186":1,"229":1,"230":1,"232":1,"253":1,"273":1}}],["particularly",{"2":{"13":1,"67":1,"99":1,"729":1}}],["party",{"2":{"160":1,"245":1,"252":1,"259":1,"268":1,"688":1}}],["part",{"2":{"39":1,"42":1,"56":1,"98":1,"146":1,"160":2,"166":1,"177":1,"187":1,"193":1,"197":2,"216":1,"247":1,"256":1,"270":2}}],["parts",{"2":{"28":1}}],["pnpm",{"2":{"3":4,"186":5}}],["otp",{"2":{"259":1}}],["otherwise",{"2":{"63":1,"331":1,"456":1,"775":1}}],["others",{"2":{"39":1,"103":1,"126":1,"143":1,"187":1,"227":1,"249":1,"270":1}}],["other",{"0":{"240":1,"282":1},"1":{"241":1,"242":1,"243":1,"244":1},"2":{"0":1,"20":2,"21":1,"22":1,"24":1,"39":1,"41":1,"65":1,"81":1,"87":1,"94":2,"97":1,"102":1,"103":1,"108":2,"111":1,"116":1,"125":1,"126":1,"127":1,"128":1,"131":1,"134":1,"137":3,"141":1,"146":2,"151":1,"153":1,"170":2,"171":1,"186":2,"211":1,"214":1,"216":1,"220":1,"222":1,"229":1,"230":1,"234":1,"236":2,"243":1,"245":1,"250":1,"255":1,"263":1,"265":1,"272":1,"282":1,"667":2,"671":2,"673":1,"674":1}}],["old",{"2":{"229":1,"541":1,"670":1}}],["older",{"2":{"16":1}}],["oh",{"2":{"220":1}}],["omitted",{"2":{"126":1}}],["omit",{"2":{"125":1}}],["occur",{"2":{"123":1,"278":1}}],["occasions",{"2":{"97":1}}],["o",{"2":{"109":2,"188":2,"249":1,"690":1}}],["obvious",{"2":{"273":1,"667":1}}],["obviously",{"2":{"218":1,"273":1}}],["obstacle",{"2":{"264":1}}],["observation",{"2":{"97":1}}],["obtain",{"2":{"188":2,"264":1,"776":1}}],["obtained",{"2":{"65":1,"125":1,"253":1}}],["objects",{"2":{"29":2,"40":1,"41":3,"42":1,"50":1,"65":1,"170":1,"668":1}}],["object",{"2":{"25":1,"40":2,"41":1,"50":1,"51":1,"54":1,"98":1,"128":6,"164":6,"187":4,"188":2,"189":2,"190":4,"191":2,"193":2,"198":2,"330":1,"446":1,"484":1,"554":1,"556":1,"559":1,"571":1,"612":1,"627":1,"629":1,"665":1,"672":1,"698":1,"773":1,"774":1}}],["owner",{"2":{"313":1}}],["ownerchanged",{"2":{"309":1}}],["ownership",{"2":{"137":1,"216":1,"667":2}}],["owned",{"2":{"52":2,"53":2,"217":2,"308":1}}],["own",{"2":{"56":1,"132":1,"135":2,"136":1,"137":1,"138":1,"139":1,"146":1,"187":1,"197":1,"211":1,"216":2,"222":1,"233":1,"247":1,"722":1}}],["ok",{"2":{"53":2,"188":2,"192":6,"211":1,"222":2,"265":1,"766":1,"770":1,"771":1}}],["outcome",{"2":{"616":1,"617":1}}],["outdated",{"0":{"272":1},"2":{"248":1,"272":1,"283":1}}],["outlined",{"2":{"253":1}}],["outline",{"2":{"138":1,"229":1,"250":1}}],["outgoing",{"2":{"118":1,"119":1,"147":1}}],["outside",{"2":{"56":1,"135":1,"139":1,"722":1}}],["out",{"2":{"39":1,"81":1,"91":1,"101":1,"104":1,"127":1,"166":1,"180":1,"186":1,"197":1,"210":1,"212":1,"213":1,"222":1,"226":1,"253":2,"665":1,"769":1}}],["outputframe",{"2":{"198":4}}],["outputs",{"0":{"174":1,"193":1,"204":1,"212":1,"222":1},"1":{"194":1},"2":{"178":1,"225":1,"283":1}}],["output",{"2":{"4":1,"5":1,"109":2,"130":1,"134":1,"143":2,"154":1,"164":2,"168":3,"170":2,"171":1,"174":3,"198":4,"199":3,"200":1,"201":1,"202":2,"203":1,"209":1,"238":2,"239":14,"272":1,"716":2}}],["ours",{"2":{"39":1}}],["our",{"2":{"16":1,"18":1,"21":1,"42":1,"94":1,"97":2,"101":1,"103":1,"104":1,"106":1,"107":1,"134":1,"141":1,"170":1,"177":1,"178":1,"180":1,"187":1,"188":1,"190":1,"193":1,"206":1,"214":1,"216":1,"218":1,"220":1,"226":1,"228":1,"231":4,"233":1,"253":1,"267":1,"268":2,"270":1,"279":1,"287":2}}],["overflow",{"2":{"476":1}}],["overview",{"2":{"263":1,"287":1}}],["oversee",{"2":{"260":1}}],["overridden",{"2":{"256":1}}],["overkill",{"2":{"252":1}}],["overloading",{"2":{"125":1}}],["over",{"2":{"10":1,"28":1,"166":1,"180":1,"203":1,"238":1,"247":1,"252":1,"254":1,"258":1,"665":1,"667":1}}],["overallsize",{"2":{"483":1}}],["overall",{"2":{"6":1,"248":1,"259":1,"268":1}}],["os",{"2":{"6":2,"16":2,"103":1,"109":2,"283":1}}],["ofminutes",{"2":{"198":2}}],["often",{"2":{"109":1,"199":1,"249":1,"756":1}}],["office",{"2":{"258":1}}],["official",{"2":{"166":1,"176":1,"241":1,"264":1}}],["offline",{"2":{"248":1,"267":1,"769":3}}],["offset",{"2":{"88":5}}],["off",{"2":{"21":1,"106":1,"140":1,"158":1,"213":1,"230":1,"256":1}}],["offering",{"2":{"238":1,"262":1}}],["offers",{"2":{"49":1,"166":1,"238":1,"263":1,"264":1,"271":1,"274":1,"277":1,"284":1}}],["offer",{"2":{"13":2,"180":2,"231":1,"674":1}}],["of",{"0":{"18":1,"73":1,"85":1,"236":1,"273":1},"1":{"74":1,"75":1,"76":1,"77":1,"78":1,"79":1,"80":1,"86":1,"87":1,"88":1,"89":1,"90":1},"2":{"5":2,"6":6,"10":2,"11":26,"13":2,"14":1,"16":3,"17":2,"18":2,"19":2,"20":1,"21":7,"22":3,"24":3,"27":2,"28":9,"29":1,"31":1,"32":1,"33":2,"36":2,"37":1,"38":3,"39":8,"40":4,"41":5,"42":5,"43":5,"45":1,"49":2,"50":1,"51":1,"52":3,"54":1,"56":5,"57":5,"58":1,"59":1,"62":5,"63":2,"65":11,"66":1,"67":1,"69":1,"70":1,"71":2,"72":1,"75":1,"77":1,"79":4,"80":1,"81":4,"82":2,"83":1,"84":1,"85":4,"86":4,"87":3,"88":2,"89":5,"90":3,"91":1,"92":4,"94":7,"95":1,"97":10,"98":7,"100":4,"101":4,"103":5,"104":3,"105":1,"106":4,"107":2,"108":1,"109":1,"110":2,"111":4,"112":2,"115":1,"117":2,"118":1,"120":2,"121":2,"124":1,"125":4,"126":2,"127":2,"128":11,"132":1,"134":4,"135":4,"136":2,"137":14,"138":3,"139":2,"140":1,"141":5,"143":3,"145":1,"146":8,"147":1,"148":2,"149":1,"150":3,"151":2,"154":1,"155":1,"156":1,"157":1,"158":1,"160":3,"161":2,"163":1,"164":8,"166":7,"168":4,"169":1,"170":8,"172":1,"174":2,"177":1,"178":4,"180":4,"183":1,"186":9,"187":7,"189":3,"190":4,"191":1,"192":6,"193":4,"194":1,"195":1,"197":7,"199":10,"200":4,"201":10,"202":8,"203":4,"204":2,"207":3,"208":2,"209":4,"210":6,"211":6,"212":4,"213":2,"214":1,"216":13,"217":2,"218":5,"219":3,"220":10,"222":5,"223":2,"224":3,"225":2,"226":3,"229":5,"230":8,"231":2,"233":2,"234":2,"235":1,"237":3,"238":9,"239":1,"242":1,"243":1,"244":1,"245":2,"246":6,"247":5,"249":17,"250":3,"251":2,"252":21,"253":10,"254":2,"255":4,"256":7,"258":1,"259":6,"260":6,"262":3,"263":4,"264":6,"267":3,"268":7,"269":1,"270":3,"271":1,"272":7,"273":7,"278":1,"279":3,"280":1,"282":4,"283":1,"286":3,"287":4,"664":1,"665":6,"666":1,"667":4,"668":2,"669":1,"671":2,"672":2,"673":3,"674":2,"675":1,"676":1,"677":3,"684":1,"686":2,"689":1,"690":1,"695":2,"696":2,"697":1,"699":3,"707":3,"715":2,"716":7,"719":1,"720":2,"721":1,"722":1,"724":1,"725":1,"729":2,"732":1,"733":1,"734":1,"737":1,"738":1,"741":1,"742":1,"743":1,"744":1,"749":1,"750":3,"753":1,"754":2,"755":3,"756":1,"757":4,"759":1,"760":1,"762":1,"764":2,"766":3,"769":2,"770":1,"771":9,"772":2,"775":13,"776":2}}],["opaque",{"2":{"666":1,"667":1}}],["opsec",{"0":{"248":1},"2":{"247":4,"261":2}}],["opposite",{"2":{"125":1,"254":1,"667":1}}],["opposed",{"2":{"88":1,"718":1,"719":1}}],["opt",{"2":{"105":2,"109":4,"110":1}}],["optimally",{"2":{"146":1}}],["optimized",{"2":{"615":1}}],["optimizedexecutable",{"0":{"297":1,"505":1,"614":1},"2":{"297":1,"614":1,"615":1}}],["optimizations",{"2":{"137":2}}],["optimising",{"0":{"105":1},"1":{"106":1,"107":1,"108":1,"109":1,"110":1}}],["optimisation",{"2":{"101":1,"105":2,"109":1,"110":1}}],["optimisations",{"2":{"100":1}}],["optimistically",{"2":{"88":1}}],["optimised",{"2":{"83":1,"100":1,"104":1,"238":1}}],["optimises",{"2":{"14":1}}],["optimise",{"0":{"109":1},"2":{"10":1,"97":1,"105":2,"109":1}}],["optionpipelinestatuskind",{"2":{"193":4}}],["optionpipelineentitykind",{"2":{"193":4}}],["optionhash",{"2":{"193":4}}],["optionipfspath",{"2":{"188":4,"193":4}}],["option",{"0":{"125":1,"126":2,"506":1,"507":1,"508":1,"509":1,"510":1,"511":1,"512":1,"513":1,"514":1,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1},"2":{"16":1,"108":1,"124":6,"125":2,"126":8,"143":3,"154":1,"168":1,"266":1,"267":2,"281":1,"308":1,"325":2,"331":1,"341":1,"444":2,"494":1,"495":1,"506":1,"507":1,"508":1,"509":1,"510":1,"511":1,"512":1,"513":1,"514":1,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1,"544":3,"565":1,"603":1,"608":2,"611":1,"617":2,"623":1}}],["options",{"0":{"214":1},"2":{"6":1,"13":3,"21":1,"113":1,"124":3,"125":1,"127":1,"143":3,"147":1,"148":1,"151":1,"153":1,"168":5,"211":1,"214":1,"217":1}}],["optional",{"2":{"2":1,"6":2,"16":1,"38":1,"69":1,"94":1,"126":1,"717":1,"722":1,"772":4,"775":1}}],["opera",{"2":{"249":1}}],["operative",{"2":{"231":1}}],["operating",{"2":{"176":1,"264":1}}],["operational",{"0":{"247":1},"1":{"248":1,"249":1,"250":1},"2":{"178":1,"246":1,"247":1,"250":1,"260":1,"261":1}}],["operations",{"0":{"240":1},"1":{"241":1,"242":1,"243":1,"244":1},"2":{"36":1,"62":2,"63":2,"65":1,"100":1,"199":1,"202":1,"238":1,"246":1,"247":1,"249":1}}],["operation",{"2":{"36":1,"39":1,"62":3,"63":3,"199":1,"211":1,"218":1,"256":1,"701":1}}],["operates",{"2":{"39":1,"40":1,"143":1,"216":1}}],["operate",{"2":{"36":1,"101":1,"256":1}}],["opened",{"2":{"265":1}}],["opening",{"2":{"166":1}}],["openssl$",{"2":{"182":1}}],["openssl",{"0":{"182":1},"2":{"6":1,"181":1,"182":5,"234":1}}],["open",{"2":{"5":1,"22":1,"168":1,"170":1,"199":4,"201":1,"208":2,"217":2,"218":1,"248":1,"249":5,"266":1,"670":1}}],["online",{"2":{"222":1,"254":2,"259":1,"769":1}}],["only",{"2":{"4":1,"14":1,"17":1,"21":1,"24":1,"28":1,"36":1,"39":1,"40":1,"41":1,"42":1,"43":1,"45":1,"56":1,"57":1,"62":4,"63":1,"65":2,"68":1,"69":1,"78":1,"82":1,"83":1,"87":1,"89":1,"90":1,"94":1,"97":1,"116":1,"125":2,"126":3,"127":1,"128":2,"137":2,"141":1,"146":1,"147":1,"148":1,"154":1,"166":2,"168":2,"183":1,"186":2,"187":2,"189":3,"190":3,"199":2,"200":2,"204":1,"206":1,"210":1,"216":1,"219":3,"220":4,"230":1,"234":1,"238":1,"243":1,"248":1,"249":2,"252":2,"255":2,"259":2,"260":2,"261":2,"262":1,"263":1,"264":1,"268":1,"273":1,"282":1,"665":2,"671":1,"693":1,"707":1,"719":1,"750":1}}],["ongoing",{"2":{"214":1,"260":1}}],["onbeforeunmount",{"2":{"193":4}}],["onto",{"2":{"96":1,"263":1}}],["once",{"2":{"43":1,"62":2,"65":1,"84":1,"90":1,"106":1,"137":1,"141":1,"166":1,"180":1,"209":1,"216":1,"220":4,"264":1,"266":1,"486":1,"676":1,"715":1}}],["on",{"0":{"13":1,"18":1,"166":1,"256":1,"272":1},"1":{"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1},"2":{"5":1,"6":2,"13":2,"14":1,"16":3,"18":3,"20":1,"21":1,"22":2,"36":3,"39":3,"40":1,"41":3,"42":3,"44":1,"49":1,"56":1,"62":1,"65":4,"67":1,"69":1,"78":1,"79":1,"85":1,"87":1,"88":1,"94":2,"97":1,"99":1,"101":2,"103":2,"104":1,"106":5,"110":1,"115":1,"135":1,"136":2,"137":5,"139":1,"141":3,"145":1,"146":1,"163":1,"166":1,"168":1,"170":3,"171":1,"174":1,"177":1,"182":2,"183":2,"186":2,"187":10,"190":1,"193":2,"195":2,"197":3,"199":1,"204":1,"208":1,"211":1,"212":1,"213":2,"216":3,"217":1,"218":2,"222":1,"228":2,"230":2,"232":2,"233":1,"234":1,"238":1,"241":1,"242":2,"243":1,"244":1,"246":1,"247":1,"248":1,"249":7,"252":2,"253":1,"256":4,"258":1,"259":2,"260":1,"261":1,"262":2,"263":3,"264":3,"272":2,"273":2,"278":1,"283":2,"665":1,"667":2,"668":1,"671":1,"672":2,"674":2,"685":1,"689":1,"694":1,"698":1,"700":1,"716":1,"717":1,"733":1,"734":1,"753":1,"759":1,"764":2,"769":1,"771":2,"775":1,"777":1}}],["ones",{"2":{"67":1,"90":1,"126":1,"127":1,"170":1}}],["one",{"2":{"5":2,"6":2,"18":1,"20":1,"27":1,"33":1,"42":1,"43":2,"49":1,"50":2,"62":3,"67":1,"87":1,"88":1,"90":1,"97":1,"105":2,"126":1,"128":2,"134":1,"135":3,"137":1,"141":1,"160":2,"163":1,"166":3,"170":2,"171":1,"176":1,"177":1,"178":3,"180":1,"187":1,"206":1,"211":1,"212":1,"216":2,"220":2,"224":1,"225":1,"238":2,"242":1,"243":1,"244":1,"246":1,"248":1,"249":3,"250":1,"256":2,"268":2,"272":2,"282":2,"665":1,"671":1,"716":3,"733":1,"776":1}}],["ord",{"2":{"772":1}}],["ordered",{"2":{"772":1}}],["order",{"2":{"18":2,"49":2,"78":1,"96":1,"138":1,"146":1,"159":1,"171":1,"199":1,"209":1,"212":1,"216":1,"225":1,"667":1,"769":1}}],["originfilter",{"0":{"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1},"2":{"292":1,"311":1,"317":1,"344":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"536":1,"563":1,"622":1}}],["origin",{"2":{"292":1,"311":1,"317":1,"344":1,"536":1,"563":1,"622":1}}],["original",{"2":{"18":1,"21":1,"42":1,"220":1,"230":1,"232":1}}],["organisation",{"2":{"749":1}}],["organisations",{"0":{"260":1},"2":{"257":1}}],["organization",{"2":{"260":1}}],["organizational",{"2":{"260":1}}],["organizations",{"2":{"137":1,"245":1,"246":1,"247":1,"250":1,"252":1,"259":1}}],["org",{"2":{"197":6,"198":14,"205":4}}],["orgs",{"2":{"168":2}}],["or",{"0":{"522":1,"682":1},"2":{"2":1,"6":1,"8":2,"13":2,"16":3,"20":1,"21":2,"24":1,"25":1,"27":1,"28":1,"31":1,"32":3,"33":1,"36":1,"38":2,"39":1,"40":2,"42":1,"43":1,"45":1,"50":2,"51":4,"55":2,"56":3,"57":1,"62":5,"63":2,"65":1,"71":1,"72":2,"76":1,"80":1,"84":1,"94":3,"97":2,"100":1,"108":1,"109":1,"111":2,"119":1,"124":1,"125":2,"126":2,"127":3,"128":2,"129":1,"135":3,"136":1,"137":3,"139":1,"141":2,"143":1,"148":1,"151":1,"153":1,"154":1,"155":1,"160":2,"166":2,"168":4,"174":1,"176":1,"178":1,"180":2,"183":1,"186":3,"187":3,"190":3,"198":1,"201":2,"208":1,"211":2,"213":1,"217":2,"218":3,"220":2,"223":1,"224":1,"226":1,"227":1,"230":2,"232":1,"242":1,"244":1,"245":1,"246":1,"247":1,"248":2,"249":2,"250":8,"252":3,"253":4,"258":3,"259":4,"260":3,"263":2,"264":1,"265":1,"266":1,"268":1,"270":1,"272":1,"273":2,"281":1,"286":1,"376":2,"445":1,"667":1,"669":1,"671":1,"689":1,"693":1,"698":2,"699":1,"716":4,"722":1,"734":1,"769":3,"775":1}}],["e70121022a9d6e0d54022c0e2752e43add91ada28259e1f2ce0c6d4e9183fb2882de6749",{"2":{"239":2}}],["eq",{"2":{"219":2}}],["equal",{"0":{"347":1},"2":{"28":1,"376":2,"671":1,"673":1}}],["equals",{"2":{"18":1}}],["ee",{"2":{"193":2,"195":2}}],["estimation",{"2":{"325":1}}],["establishes",{"2":{"255":1}}],["established",{"2":{"249":1,"260":1}}],["establishing",{"2":{"245":1,"246":1,"250":1,"260":1}}],["establish",{"2":{"170":1,"248":1,"250":1,"258":1,"260":1,"280":1}}],["esm",{"2":{"186":1}}],["especially",{"2":{"168":1,"249":1,"258":1,"259":1,"278":1,"756":1}}],["essentially",{"2":{"85":1,"137":1,"247":1}}],["essential",{"2":{"4":1,"245":1,"249":1,"250":1}}],["ecc",{"2":{"238":2}}],["ecosystem",{"2":{"185":1,"262":1}}],["economical",{"2":{"89":1}}],["echo",{"2":{"167":2,"186":2,"244":4}}],["eloquently",{"2":{"253":1}}],["elf",{"2":{"241":1}}],["else",{"2":{"166":1,"273":1}}],["element",{"2":{"334":1,"716":1}}],["elements",{"2":{"170":4,"335":1,"336":1}}],["election",{"2":{"695":1}}],["electron",{"2":{"249":1}}],["electromagnetic",{"2":{"248":1}}],["elected",{"2":{"112":1,"696":1}}],["elevated",{"2":{"128":1}}],["err",{"2":{"217":4,"218":5,"220":2,"222":6}}],["errors",{"2":{"188":1,"220":1,"273":1}}],["error",{"2":{"6":1,"13":1,"19":1,"125":1,"126":1,"127":2,"153":4,"166":1,"187":1,"188":4,"190":1,"211":1,"213":4,"218":3,"272":3,"273":2,"282":1,"283":2,"286":1,"473":1,"611":1,"772":5}}],["erasing",{"2":{"209":1}}],["ergonomic",{"2":{"166":1,"231":1}}],["ergonomics",{"2":{"97":1}}],["eyed",{"2":{"97":1}}],["efficiency",{"2":{"238":2}}],["efficient",{"2":{"97":1,"101":1,"227":1,"238":1,"263":1}}],["effortlessly",{"2":{"263":1}}],["efforts",{"2":{"109":1,"287":1}}],["effort",{"2":{"88":1,"97":1,"166":1,"667":1}}],["effective",{"2":{"250":1}}],["effectively",{"2":{"21":1,"25":1,"89":1,"250":1,"256":1,"262":1,"775":1}}],["effect",{"2":{"8":1,"9":1}}],["ethereum",{"2":{"86":1}}],["etc",{"2":{"18":1,"88":1,"166":1,"216":1,"246":1,"249":1,"252":1,"254":1,"287":1,"681":1,"682":1,"685":1}}],["epoch",{"2":{"57":1,"112":1,"696":1}}],["emerging",{"2":{"260":1}}],["emergency",{"2":{"250":1}}],["embrace",{"2":{"260":1}}],["embedded",{"2":{"232":1}}],["emails",{"2":{"259":3}}],["email",{"2":{"249":2,"254":1,"259":2}}],["emojis",{"2":{"238":1}}],["emperor",{"0":{"679":1},"2":{"669":1}}],["employee",{"2":{"260":1}}],["employees",{"2":{"247":1,"250":1,"260":8}}],["employ",{"2":{"188":1,"260":1}}],["employs",{"2":{"188":1,"254":1}}],["emphasised",{"2":{"188":1,"190":1}}],["emptypath",{"2":{"483":1}}],["empty",{"2":{"128":1,"219":2,"220":2}}],["emptied",{"2":{"28":1}}],["emulated",{"2":{"97":1}}],["emulate",{"2":{"89":1}}],["emits",{"2":{"759":1}}],["emit",{"2":{"72":1}}],["emitted",{"2":{"31":1,"32":1,"33":1,"34":1,"35":1,"51":2,"82":1,"97":1}}],["evaluationerror",{"0":{"367":1},"2":{"458":1}}],["evaluation",{"2":{"260":1}}],["evaluate",{"2":{"260":1,"459":1,"550":1,"772":2}}],["evaluatestoassetid",{"2":{"191":6}}],["evaluatestovalue",{"2":{"190":4,"191":4}}],["evaluatestoidbox",{"2":{"190":4,"191":6}}],["evaluatestoregistrablebox",{"2":{"188":4,"189":4,"190":4,"193":4}}],["evaluatesto",{"0":{"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"592":1},"2":{"36":2,"170":12,"298":2,"300":2,"330":2,"331":1,"334":2,"335":2,"336":2,"340":2,"347":2,"372":1,"401":1,"402":2,"403":1,"404":1,"405":1,"418":1,"419":1,"420":2,"421":2,"422":1,"423":1,"424":1,"425":1,"426":2,"427":1,"428":1,"429":1,"430":2,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":2,"441":1,"446":2,"447":2,"456":3,"470":2,"472":2,"475":2,"484":2,"490":2,"491":2,"496":1,"501":1,"522":2,"552":2,"554":1,"556":2,"559":2,"571":3,"572":1,"592":1,"602":2,"612":3,"627":1,"629":1,"656":2}}],["evolving",{"2":{"252":1,"260":1}}],["evolves",{"2":{"250":1}}],["evil™",{"2":{"28":2}}],["evil",{"2":{"28":1}}],["everyday",{"2":{"249":2}}],["everyone",{"2":{"158":1}}],["every",{"2":{"18":1,"24":2,"82":1,"106":1,"135":1,"137":1,"143":2,"153":1,"164":2,"170":1,"216":1,"218":1,"256":2,"263":1,"265":1,"282":1,"665":2,"691":1,"733":1,"759":1}}],["everything",{"2":{"18":2,"42":3,"94":1,"101":1,"103":1,"153":1,"166":1,"186":1,"211":1,"251":1,"258":1,"665":1}}],["eventmessage",{"0":{"369":1},"2":{"768":1}}],["eventdata",{"2":{"193":4}}],["eventlistener",{"2":{"193":7}}],["eventfilters",{"2":{"97":3}}],["eventfilter",{"2":{"72":1,"75":2,"79":1,"94":2,"99":1,"174":2,"204":8,"212":6}}],["event",{"0":{"91":1,"94":1,"368":1,"637":1},"1":{"92":1,"93":1,"94":1,"95":1,"96":1,"97":1},"2":{"32":2,"51":1,"65":1,"72":1,"79":2,"82":1,"85":2,"87":3,"89":1,"91":1,"94":3,"97":6,"168":2,"174":6,"193":6,"204":2,"212":12,"222":9,"233":2,"250":1,"266":1,"292":1,"311":1,"317":1,"327":2,"344":1,"369":1,"536":1,"563":1,"622":1,"637":1,"685":1,"689":1,"759":3,"769":1}}],["eventsubscriptionrequest",{"0":{"370":1},"2":{"768":1}}],["events",{"0":{"31":1,"32":1,"33":1,"34":1,"35":1,"768":1,"769":1},"1":{"32":1,"33":1,"34":1,"35":1,"769":1},"2":{"31":6,"32":1,"33":2,"34":1,"35":1,"37":1,"38":1,"51":1,"65":1,"72":3,"79":1,"82":2,"83":2,"86":3,"87":1,"94":1,"97":3,"98":2,"168":3,"171":2,"174":7,"187":1,"193":11,"204":7,"212":2,"222":4,"268":1,"768":1,"769":1}}],["eventually",{"2":{"28":1}}],["even",{"2":{"5":1,"106":1,"128":1,"134":1,"135":1,"190":1,"214":1,"230":1,"232":1,"248":2,"249":1,"250":1,"252":2,"256":1,"259":2,"268":1,"273":1,"665":1}}],["educated",{"2":{"268":1}}],["educate",{"2":{"260":1}}],["edge",{"2":{"206":2,"249":1}}],["eddsa",{"2":{"197":2,"238":1}}],["ed0120b678073cfae6e247a58b442661c7da0e13bac5031cbc6343ef566b8718d47d04",{"2":{"239":8}}],["ed01206b0f56f58761060056355dba0e0fc489cfb2f974481ed64873082e6032796235",{"2":{"239":2}}],["ed0120e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3",{"2":{"187":2}}],["ed0120a4c4dadd9f18b0f63d6a420151fe0748d785475dec63034a15fcf999ceda1e65",{"2":{"170":2}}],["ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5",{"2":{"170":6}}],["ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",{"2":{"20":2,"113":2,"128":7,"164":8,"174":2,"193":2,"217":2}}],["ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255",{"2":{"18":8}}],["ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f",{"2":{"18":10,"146":2}}],["ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020",{"2":{"18":10,"146":2}}],["ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1",{"2":{"18":10,"146":2}}],["ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b",{"2":{"18":10,"146":2}}],["ed25519public",{"2":{"170":1}}],["ed25519",{"2":{"18":10,"20":2,"113":2,"141":1,"164":2,"170":7,"174":2,"187":2,"193":2,"217":2,"238":6,"239":10,"299":1}}],["editable",{"2":{"137":1}}],["edition",{"2":{"103":2,"177":1}}],["editing",{"2":{"22":1}}],["edit",{"2":{"5":1,"137":1,"168":2}}],["ea01610402a54abcc40819f15e3553cc8d42d628eead7e1b10724bd2afe523a7c0446eb1cb3f14d4500bd68c997784136fd056ba04215dfd2d3fdc7883b43ae94ac52b7d01525f5a80b41c01701502b46dbb9f0384cc7be037dc2cbc928014e52a4c5c3b",{"2":{"239":2}}],["earlier",{"2":{"88":1,"101":1,"170":1}}],["early",{"2":{"88":1}}],["easily",{"2":{"88":1,"137":1,"222":1,"250":1,"253":1,"268":1,"673":1}}],["easier",{"2":{"14":1,"97":1,"101":1,"167":1,"168":1,"188":1,"199":1,"282":1}}],["easiest",{"2":{"13":1,"176":1,"208":1,"242":1}}],["easy",{"2":{"16":1,"74":1,"97":1,"100":1,"218":1,"231":1,"234":2,"254":1}}],["each",{"2":{"3":1,"18":1,"21":2,"28":1,"36":1,"39":1,"40":2,"41":2,"62":1,"63":1,"65":1,"84":1,"85":1,"88":1,"97":1,"121":1,"135":1,"137":4,"146":1,"168":2,"176":1,"186":1,"187":1,"193":1,"216":1,"226":1,"233":1,"238":1,"268":1,"285":1,"287":2,"683":1,"686":1,"716":2,"754":1,"773":1,"774":1}}],["e300886e76c777776efad1e2f5cb245bfb8ed02e",{"2":{"5":3}}],["enforced",{"2":{"665":1}}],["enforce",{"2":{"260":1}}],["ensuring",{"2":{"253":1}}],["ensuredomainexistence",{"2":{"188":4}}],["ensure",{"2":{"178":1,"188":2,"235":1,"250":1,"251":1,"257":1,"258":1,"260":3,"287":1,"667":1}}],["ensures",{"2":{"127":1,"249":1,"250":2,"255":1,"267":1}}],["enabling",{"2":{"249":1,"254":1,"263":1,"266":1}}],["enabled",{"2":{"143":2,"164":2,"249":1}}],["enable",{"2":{"3":1,"249":1,"265":1,"268":1,"667":1}}],["enhancement",{"2":{"260":1}}],["enhanced",{"2":{"249":2,"264":1}}],["enhance",{"2":{"249":1,"250":1,"260":3,"264":1}}],["enhancing",{"2":{"248":1,"252":1}}],["encode",{"2":{"775":1}}],["encoded",{"2":{"28":1,"39":1,"120":1,"134":2,"150":1,"273":1,"767":1,"768":1,"772":1,"775":2,"777":1}}],["encounter",{"2":{"272":1,"283":1,"285":1}}],["encountered",{"2":{"252":1}}],["encourages",{"2":{"260":1}}],["encourage",{"2":{"260":1}}],["encrypted",{"2":{"254":1,"255":2,"258":1}}],["encrypt",{"2":{"248":2}}],["encrypting",{"2":{"247":1,"248":1}}],["encryption",{"0":{"255":1},"2":{"238":2,"246":1,"255":1}}],["enjoy",{"2":{"232":1}}],["english",{"2":{"171":1,"252":1,"253":1}}],["engineering",{"2":{"261":1}}],["engine",{"2":{"155":1}}],["enough",{"2":{"166":1,"248":1,"252":1,"282":1}}],["enormous",{"2":{"137":1}}],["enum",{"2":{"77":1,"188":6,"192":20,"193":4,"195":2,"216":1,"218":1,"290":1,"291":1,"299":1,"309":1,"310":1,"315":1,"316":1,"319":1,"320":1,"328":1,"332":1,"333":1,"338":1,"339":1,"342":1,"343":1,"367":1,"368":1,"371":1,"375":1,"376":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"431":1,"445":1,"453":1,"454":1,"455":1,"457":1,"458":1,"459":1,"461":1,"464":1,"473":1,"476":1,"483":1,"485":1,"486":1,"502":1,"503":1,"504":1,"505":1,"534":1,"535":1,"542":1,"545":1,"546":1,"547":1,"549":1,"550":1,"555":1,"557":1,"561":1,"562":1,"569":1,"574":1,"584":1,"600":1,"610":1,"615":1,"618":1,"619":1,"620":1,"621":1,"625":1,"626":1,"628":1,"630":1,"632":1,"633":1,"634":1,"636":1,"648":1,"649":1,"650":1,"651":1,"652":1,"755":2,"757":2}}],["enumeration",{"2":{"77":1}}],["enums",{"2":{"50":1}}],["endswith",{"2":{"600":1}}],["endian",{"2":{"186":1}}],["ending",{"2":{"137":1}}],["end",{"2":{"79":1,"166":1,"204":1,"667":1}}],["endpoints",{"0":{"765":1},"1":{"766":1,"767":1,"768":1,"769":1,"770":1,"771":1,"772":1,"773":1,"774":1,"775":1,"776":1,"777":1},"2":{"187":4}}],["endpoint",{"0":{"12":1},"2":{"11":2,"18":1,"120":1,"125":1,"150":1,"161":1,"195":2,"754":1,"766":1,"767":2,"768":1,"770":1,"771":1,"772":1,"775":1,"777":1}}],["endregion",{"2":{"4":2}}],["environments",{"2":{"238":1}}],["environment",{"0":{"9":1,"16":1,"20":1,"107":1},"2":{"6":1,"9":1,"13":2,"16":3,"18":9,"78":1,"107":1,"125":4,"126":2,"127":1,"135":1,"146":1,"151":1,"166":2,"186":2,"187":1,"195":2,"198":1,"256":1,"286":1,"664":1}}],["entails",{"2":{"256":1,"282":1,"695":1}}],["entail",{"2":{"252":1}}],["entries",{"2":{"266":1}}],["entropy=log",{"2":{"252":1}}],["entropy=log2​",{"2":{"252":1}}],["entropy=log2",{"2":{"252":1}}],["entropy",{"2":{"246":1,"252":14,"253":3}}],["entrytoobig",{"2":{"483":1}}],["entry",{"2":{"69":1,"104":2,"143":8,"164":8,"260":1,"265":5,"474":1}}],["entities",{"2":{"33":1,"69":1,"111":1,"669":1}}],["entity=pipeline",{"2":{"212":2}}],["entitytype",{"2":{"204":2,"212":2}}],["entityfilters",{"2":{"38":1}}],["entityfilter",{"2":{"38":2,"94":4}}],["entity",{"2":{"32":2,"33":1,"38":1,"40":1,"42":2,"72":2,"111":1,"137":2,"174":8,"193":2,"543":1,"544":1,"665":1,"698":1}}],["entirely",{"2":{"65":1,"108":1,"166":1,"209":2,"232":1}}],["entire",{"2":{"4":1,"220":1}}],["enternally",{"2":{"268":1}}],["entering",{"2":{"263":1}}],["enter",{"2":{"6":1,"65":1,"153":1,"183":1,"260":1}}],["e",{"2":{"3":1,"9":1,"21":1,"31":1,"38":1,"39":1,"41":1,"42":2,"56":1,"65":1,"81":1,"84":1,"88":3,"108":1,"125":1,"141":1,"167":1,"168":1,"171":1,"174":1,"187":1,"193":1,"195":1,"212":1,"218":1,"230":1,"238":2,"247":5,"249":1,"250":1,"252":2,"256":1,"259":1,"273":2,"282":1,"716":1,"733":1,"767":1,"769":1,"776":1}}],["either",{"2":{"2":1,"16":1,"25":1,"32":1,"38":1,"39":1,"45":1,"63":1,"72":1,"76":1,"84":1,"94":2,"97":1,"116":1,"125":2,"127":1,"128":1,"135":1,"148":1,"153":1,"166":1,"213":1,"217":1,"218":1,"224":1,"227":1,"249":1,"252":1,"272":1,"273":1,"689":1,"716":2,"769":2,"775":1}}],["exhaustive",{"2":{"253":1}}],["exemplar",{"0":{"682":1},"2":{"669":1}}],["exemplary",{"2":{"231":1}}],["exercise",{"2":{"249":1,"259":1}}],["executing",{"2":{"72":1,"204":1,"222":1}}],["executiontime",{"0":{"375":1},"2":{"604":1}}],["executionexception",{"2":{"205":6}}],["execution",{"0":{"35":1},"2":{"31":1,"35":1,"36":1,"62":2,"72":1,"78":2,"84":1,"89":1,"97":4,"141":1,"236":1,"664":1,"750":1}}],["executes",{"2":{"100":1}}],["execute",{"2":{"36":1,"40":1,"41":1,"48":1,"49":4,"88":1,"97":2,"98":1,"104":5,"208":1,"698":1}}],["executetriggerbox",{"0":{"372":1},"2":{"457":1}}],["executetriggereventfilter",{"0":{"374":1},"2":{"378":1,"625":1}}],["executetriggerevent",{"0":{"373":1},"2":{"368":1}}],["executetrigger",{"0":{"48":1},"2":{"35":1,"40":1,"41":1,"368":1,"378":1,"457":1,"461":1,"625":1,"698":1}}],["executed",{"2":{"32":1,"35":1,"62":2,"71":1,"81":1,"87":2,"88":5,"90":2,"97":2,"98":2,"253":1}}],["executable",{"0":{"76":1,"296":1,"371":1,"613":1},"2":{"16":2,"72":1,"75":4,"76":1,"93":2,"95":2,"97":1,"98":1,"101":1,"167":1,"187":4,"188":4,"193":4,"241":1,"296":2,"297":1,"555":1,"608":1,"613":1,"615":1}}],["exec",{"2":{"6":2,"187":4}}],["exchange",{"2":{"238":1}}],["exclusively",{"2":{"166":1}}],["excluding",{"2":{"110":1}}],["excludes",{"2":{"161":1}}],["exclude",{"2":{"108":1}}],["excellent",{"2":{"166":1}}],["excess",{"2":{"137":1}}],["except",{"2":{"137":1,"153":1,"210":1,"667":1,"775":2}}],["exceptional",{"2":{"665":1}}],["exception",{"2":{"21":1,"205":8,"208":1,"665":1,"671":1}}],["exorbitant",{"2":{"67":1}}],["exotic",{"2":{"13":1,"90":1}}],["existence",{"2":{"220":1,"757":1}}],["exists",{"2":{"78":1,"137":1,"168":1,"189":1,"190":1,"210":1,"219":1}}],["exist",{"2":{"62":1,"116":1,"154":1,"166":1,"220":1}}],["existing",{"2":{"28":1,"70":1,"104":2,"189":1,"200":1,"210":1,"216":1,"219":1,"238":1,"665":1}}],["exited",{"2":{"272":1}}],["exit",{"2":{"6":1}}],["examine",{"2":{"253":1}}],["examples",{"0":{"239":1},"2":{"43":1,"57":1,"146":1,"252":1,"260":1}}],["example",{"0":{"5":1,"91":1,"102":1,"666":1},"1":{"92":1,"93":1,"94":1,"95":1,"96":1,"97":1,"103":1,"104":1},"2":{"2":2,"5":2,"6":1,"9":1,"11":1,"18":1,"29":1,"36":1,"39":1,"41":3,"44":1,"52":1,"53":2,"65":1,"66":2,"71":2,"97":1,"113":1,"119":1,"121":1,"122":1,"126":1,"128":3,"134":1,"136":1,"137":1,"143":2,"146":1,"166":1,"186":8,"187":3,"191":1,"195":1,"197":1,"199":1,"207":3,"208":1,"209":1,"216":4,"218":1,"238":1,"249":1,"259":1,"264":1,"273":1,"278":1,"666":2,"667":1,"673":1,"720":1,"729":1,"775":1}}],["exact",{"2":{"28":1,"62":1,"239":6,"772":1}}],["exactly",{"2":{"18":1,"24":1,"77":2,"84":2,"94":1,"97":1,"272":1,"557":1}}],["extremely",{"2":{"154":1,"177":1,"232":1}}],["extraneous",{"2":{"135":1}}],["extra",{"2":{"16":1,"137":1}}],["extended",{"2":{"620":1}}],["extends",{"2":{"205":2}}],["extension",{"2":{"253":1}}],["extensions",{"2":{"249":5,"688":1}}],["extensively",{"2":{"235":1}}],["extensive",{"2":{"199":1,"216":1,"232":1,"247":1,"253":1}}],["extent",{"2":{"14":1}}],["external",{"2":{"241":1,"259":1,"260":1}}],["extern",{"2":{"104":2,"666":2}}],["expanding",{"2":{"252":1,"253":1}}],["expanded",{"2":{"252":2}}],["expand",{"2":{"168":1,"170":2,"199":2,"200":1,"201":1,"202":1,"203":1,"230":1,"716":1}}],["expired",{"2":{"610":1,"760":1}}],["expire",{"2":{"56":1}}],["experts",{"2":{"260":1}}],["experiencing",{"2":{"217":1,"271":1,"274":1,"277":1,"284":1,"285":1}}],["experienced",{"2":{"224":1}}],["experience",{"2":{"6":1,"13":1,"19":1,"97":1,"176":1,"223":1}}],["experiment",{"2":{"216":2}}],["experimenting",{"2":{"170":1}}],["expensive",{"2":{"232":1}}],["expects",{"2":{"187":1,"772":1,"775":1,"777":1}}],["expected",{"0":{"266":1},"2":{"168":1,"170":2,"199":2,"200":1,"201":1,"202":1,"203":1,"263":1,"487":1,"488":1,"489":1}}],["expecting",{"2":{"166":1}}],["expect",{"2":{"53":2,"106":1,"166":2,"208":1,"217":4,"219":2,"220":2}}],["expression",{"0":{"376":1},"2":{"36":1,"62":1,"101":1,"104":1,"170":12,"188":6,"189":6,"190":16,"191":10,"193":6,"209":3,"210":2,"211":4,"348":2,"349":2,"350":2,"351":2,"352":2,"353":2,"354":2,"355":2,"356":2,"357":2,"358":2,"359":2,"360":2,"361":2,"362":2,"363":2,"364":2,"365":2,"366":2,"458":1,"501":1,"656":1}}],["expressions",{"0":{"36":1},"2":{"36":2,"62":1}}],["exposing",{"2":{"243":1}}],["expose",{"2":{"187":1,"286":1}}],["exposes",{"2":{"103":1}}],["exposed",{"2":{"11":1}}],["exponent",{"2":{"143":2,"164":2}}],["export",{"2":{"2":1,"5":2,"16":12,"193":6,"244":4,"666":2}}],["explore",{"2":{"664":1}}],["explorers",{"2":{"756":1}}],["explorer",{"2":{"88":1}}],["exploit",{"2":{"259":1}}],["explicit",{"2":{"211":2,"218":2}}],["explicitly",{"2":{"2":1,"56":1,"139":1,"188":1,"244":1,"722":1}}],["explains",{"2":{"178":1,"257":1}}],["explain",{"2":{"124":1,"197":1,"216":1,"288":1}}],["explained",{"2":{"101":1,"131":1}}],["explanatory",{"2":{"116":1}}],["explanation",{"2":{"6":1,"286":1}}],["hmac",{"2":{"268":1}}],["html",{"2":{"259":1}}],["http",{"2":{"2":1,"11":2,"18":1,"113":4,"117":1,"119":3,"120":2,"164":4,"170":4,"174":2,"187":16,"193":4,"199":4,"680":1,"766":1,"767":1,"768":1,"770":1,"771":1,"772":1,"775":1,"777":1}}],["https",{"2":{"2":2,"5":3,"103":2,"119":1,"168":2,"176":2,"183":2,"186":2,"197":2,"207":2}}],["hr",{"2":{"193":4}}],["h3",{"2":{"193":12}}],["hub",{"2":{"777":1}}],["hundred",{"2":{"171":1}}],["human",{"2":{"150":1}}],["h",{"2":{"168":2,"238":1,"253":4}}],["hijiri",{"0":{"687":1},"1":{"690":1},"2":{"669":1}}],["hiring",{"2":{"260":1}}],["hinges",{"2":{"252":1}}],["highest",{"2":{"267":1}}],["higher",{"2":{"252":1,"253":1,"279":1}}],["high",{"2":{"238":2,"249":1,"252":2,"253":1}}],["highlight",{"2":{"229":1}}],["highly",{"2":{"67":1,"82":1,"101":1,"109":1,"188":1,"234":1,"249":2,"254":1}}],["hierarchy",{"2":{"227":1}}],["hit",{"2":{"213":1,"248":1}}],["histogram",{"2":{"771":2}}],["history",{"2":{"69":1,"197":1,"249":1}}],["his",{"2":{"211":1}}],["him",{"2":{"190":1}}],["hidden",{"2":{"127":1,"217":1,"249":1}}],["half",{"2":{"189":1,"210":1,"219":1,"253":1}}],["harm",{"2":{"687":1}}],["hare",{"2":{"91":1,"92":9}}],["hardware",{"0":{"268":1},"2":{"232":1,"248":2,"250":1,"268":5}}],["hard",{"2":{"39":1,"42":1,"174":1,"190":1,"247":1,"253":1}}],["harder",{"2":{"16":1,"104":1,"163":1,"248":1,"249":1,"252":1}}],["hack",{"2":{"89":1}}],["happy",{"2":{"88":1}}],["happened",{"2":{"212":1}}],["happening",{"2":{"186":1}}],["happen",{"2":{"31":1,"209":1,"281":1}}],["happens",{"2":{"28":1,"125":1,"137":1,"272":1}}],["hatter",{"2":{"91":1,"92":10,"93":7,"97":1,"104":3,"113":2,"117":2,"164":2,"170":13,"171":4,"172":3,"173":2,"256":4}}],["hats",{"2":{"52":2}}],["hat",{"2":{"52":20}}],["handshake",{"2":{"162":1,"767":1,"768":1}}],["handling",{"2":{"118":1,"119":1,"147":1,"680":1}}],["handles",{"2":{"187":1,"263":1,"681":1}}],["handled",{"2":{"100":1}}],["handle",{"2":{"34":1,"137":1,"256":1,"666":2}}],["hand",{"2":{"41":1,"87":1,"127":1,"137":1,"234":1,"273":1}}],["hada",{"2":{"186":2}}],["had",{"2":{"28":1,"92":1,"100":1,"190":1,"200":1,"278":1}}],["having",{"2":{"16":2,"21":1,"56":2,"65":1,"120":1,"150":1,"154":1,"177":1,"216":2,"243":1,"249":1,"256":1}}],["haven",{"2":{"183":1,"190":1,"212":1}}],["have",{"2":{"13":2,"14":1,"16":1,"18":4,"21":1,"22":2,"25":2,"28":1,"39":1,"40":1,"41":1,"43":1,"44":1,"56":1,"57":1,"62":1,"69":2,"78":1,"84":1,"88":3,"92":2,"97":3,"100":1,"101":1,"102":1,"103":1,"104":2,"122":1,"124":1,"125":2,"128":1,"135":1,"137":4,"139":1,"141":3,"143":1,"151":1,"156":1,"160":1,"161":1,"167":1,"168":1,"171":1,"175":1,"178":1,"182":1,"183":1,"187":4,"188":1,"189":2,"190":1,"195":1,"197":1,"198":2,"200":2,"202":1,"210":2,"211":4,"212":1,"216":4,"218":1,"219":2,"220":1,"224":1,"229":1,"231":1,"232":1,"242":1,"248":1,"249":2,"252":1,"256":1,"258":1,"260":1,"262":1,"263":1,"268":1,"270":2,"272":2,"279":3,"280":1,"281":1,"282":3,"286":1,"665":1,"674":1,"687":1,"693":1,"699":2,"716":1,"722":2,"729":1,"731":1,"733":1,"738":1,"740":1,"749":1,"760":1,"777":1}}],["haskey",{"2":{"333":1}}],["hasn",{"2":{"248":1}}],["hashvalue",{"0":{"453":1},"2":{"634":1}}],["hashof",{"0":{"352":1,"353":1,"449":1,"450":1,"451":1,"452":1,"509":1,"510":1},"2":{"325":2,"428":1,"431":2,"437":1,"453":2,"477":1,"509":1,"510":1,"609":1,"654":1}}],["hashtopolis",{"2":{"253":1}}],["hash=none",{"2":{"212":2}}],["hashmap",{"2":{"205":4}}],["hashes",{"2":{"123":1}}],["hash",{"0":{"448":1,"508":1,"683":1},"2":{"28":1,"32":1,"174":8,"193":16,"197":2,"204":11,"209":2,"210":1,"212":6,"217":1,"273":2,"325":2,"428":1,"437":1,"449":1,"450":1,"451":1,"452":1,"508":1,"543":2,"544":2,"609":1,"634":1,"654":1,"669":1,"748":2,"758":2}}],["has",{"2":{"2":1,"6":1,"18":1,"24":1,"28":1,"29":4,"36":1,"42":1,"43":1,"46":1,"62":1,"63":1,"73":1,"79":1,"88":1,"92":1,"94":3,"97":1,"104":1,"125":1,"128":2,"135":1,"141":2,"146":1,"166":2,"168":1,"169":1,"170":1,"186":2,"187":1,"190":2,"192":8,"200":2,"201":2,"207":2,"208":1,"211":3,"218":2,"219":2,"220":6,"231":1,"233":1,"235":1,"249":4,"252":1,"268":2,"278":1,"716":2,"720":1,"757":1}}],["he",{"2":{"190":1,"220":1,"253":1}}],["helm",{"2":{"286":1}}],["hello",{"2":{"167":2}}],["help$",{"2":{"282":1}}],["helpful",{"2":{"238":1,"241":1}}],["helper",{"2":{"101":1}}],["help",{"2":{"10":1,"11":10,"107":1,"166":1,"168":8,"178":1,"187":2,"214":1,"223":1,"238":1,"248":1,"250":1,"273":1,"282":1,"285":2,"771":16}}],["helps",{"2":{"5":1,"212":1,"250":1,"258":1}}],["hexadecimal",{"2":{"273":2}}],["hex",{"2":{"134":1,"217":2,"273":2}}],["heavy",{"2":{"208":1}}],["heap",{"2":{"200":1,"218":1}}],["healthy",{"2":{"770":2}}],["health",{"0":{"770":1},"2":{"137":1,"186":1,"770":1}}],["headers",{"2":{"747":1}}],["header",{"2":{"117":1,"195":2,"327":1,"748":1,"775":1}}],["heart",{"2":{"75":1}}],["hearts",{"2":{"24":1,"86":1}}],["hence",{"2":{"94":1,"199":2,"256":1}}],["her",{"2":{"86":1,"93":1,"94":1}}],["here",{"2":{"6":1,"13":1,"28":1,"29":1,"38":1,"40":1,"43":1,"52":1,"57":1,"66":1,"71":1,"116":1,"128":1,"137":2,"143":1,"146":1,"147":1,"164":1,"177":1,"187":7,"188":1,"189":2,"194":1,"195":1,"197":1,"212":1,"218":1,"263":1,"271":1,"272":1,"274":1,"277":1,"284":1,"285":1,"286":1,"666":1,"669":1,"672":1,"685":1,"689":1,"694":1}}],["heightened",{"2":{"260":1}}],["height",{"2":{"11":6,"87":1,"193":1,"195":11,"325":1,"683":1,"767":1,"771":8}}],["hold",{"2":{"744":1}}],["holds",{"2":{"263":1}}],["hole",{"2":{"222":1,"282":1}}],["houses",{"2":{"674":1}}],["housing",{"2":{"253":1}}],["hours",{"2":{"88":1}}],["home",{"2":{"166":1,"183":1,"244":4,"258":1,"282":1,"283":2}}],["host",{"2":{"6":2,"193":8,"584":1,"585":1,"671":2}}],["hot",{"0":{"6":1},"1":{"7":1,"8":1,"9":1},"2":{"6":2}}],["however",{"2":{"5":1,"14":1,"19":1,"67":1,"78":1,"88":1,"89":1,"97":1,"100":2,"101":1,"128":1,"134":1,"137":2,"141":1,"150":1,"169":1,"170":1,"187":3,"189":1,"208":1,"211":1,"219":1,"222":1,"224":1,"241":1,"247":1,"252":2,"253":2,"268":2,"273":1,"282":1,"769":1}}],["how",{"0":{"1":1,"6":1,"11":1,"39":1,"81":1,"97":1},"1":{"2":1,"3":1,"4":1,"7":1,"8":1,"9":1,"82":1,"83":1,"84":1},"2":{"18":1,"39":1,"40":1,"42":1,"44":1,"53":1,"72":1,"81":2,"85":1,"97":1,"105":1,"108":1,"120":1,"137":2,"141":1,"143":2,"150":1,"151":1,"160":1,"168":1,"178":3,"187":1,"188":1,"189":1,"190":1,"193":1,"195":1,"197":2,"198":1,"208":1,"210":1,"211":1,"216":1,"219":1,"220":1,"224":1,"225":1,"237":1,"246":1,"249":2,"252":2,"253":1,"262":1,"263":1,"264":1,"272":1,"273":1,"282":1,"287":1,"664":2,"771":1}}],["hyperledgerいろは2にようこそ",{"2":{"213":8}}],["hyperledger",{"2":{"2":2,"5":5,"18":8,"103":2,"177":1,"183":2,"186":2,"197":6,"207":2,"213":8,"223":2,"225":1,"228":3,"229":1,"236":2,"270":1,"272":2,"764":1}}],["wünderbar",{"2":{"256":1}}],["wrestling",{"2":{"252":1}}],["wrong",{"2":{"153":1,"208":1,"218":1,"258":1,"273":2,"285":1,"286":1}}],["wrapping",{"2":{"209":2,"210":1}}],["wrapped",{"2":{"124":1,"125":2,"126":3,"143":1,"189":1,"209":4,"219":1}}],["wrappers",{"2":{"199":1,"665":1}}],["wrapper",{"2":{"74":1,"188":1,"203":1,"249":2,"716":1}}],["wrap",{"2":{"94":1,"187":2,"188":1,"217":4,"218":4,"220":2}}],["wraps",{"2":{"94":1,"188":1}}],["writing",{"2":{"79":1,"88":1,"101":1,"136":1,"141":1,"208":1,"229":1,"253":1,"665":1}}],["write",{"0":{"104":1},"2":{"20":1,"101":1,"103":1,"104":2,"130":2,"199":1,"229":1,"253":1}}],["written",{"2":{"3":1,"39":2,"207":1,"211":1,"217":1}}],["ws",{"2":{"187":12,"193":6,"195":1}}],["wsl",{"2":{"16":1}}],["wsvidentlengthlimits=1",{"2":{"128":2,"164":2}}],["wsvdomainmetadatalimits=1048576",{"2":{"128":2,"164":2}}],["wsvaccountmetadatalimits=1048576",{"2":{"128":2,"164":2}}],["wsvassetdefinitionmetadatalimits=1048576",{"2":{"128":2,"164":2}}],["wsvassetmetadatalimits=1048576",{"2":{"128":2,"164":2}}],["wsv",{"0":{"112":1,"696":1},"2":{"11":2,"143":2,"164":2,"669":1}}],["woes",{"2":{"107":1}}],["wouldn",{"2":{"82":1,"222":1,"224":1}}],["would",{"2":{"17":1,"28":3,"41":1,"66":1,"78":1,"88":4,"97":1,"104":2,"106":2,"137":2,"141":1,"154":1,"174":1,"187":1,"189":1,"209":1,"210":1,"211":1,"216":1,"219":1,"222":1,"229":2,"249":1,"252":1,"253":1,"261":1,"265":1,"270":1,"273":2,"282":1,"286":1,"665":1,"667":1,"668":7}}],["worst",{"2":{"253":1}}],["word",{"2":{"126":1,"166":1,"253":1}}],["words",{"2":{"94":1,"108":1,"116":1,"128":1,"230":1,"255":1,"269":3}}],["worthwhile",{"2":{"106":1}}],["worth",{"2":{"97":1}}],["worry",{"2":{"16":1,"88":1,"102":1,"103":1,"116":1,"189":1,"200":1,"212":1,"219":1}}],["worlds",{"2":{"234":1}}],["world",{"0":{"22":1,"111":1,"112":1,"696":1},"1":{"112":1},"2":{"16":1,"22":1,"28":6,"34":1,"39":2,"40":1,"41":1,"65":1,"98":1,"103":1,"111":3,"112":1,"224":1,"233":1,"669":1,"692":1,"694":1,"750":1,"759":1}}],["workflow",{"2":{"186":1,"263":1}}],["worked",{"2":{"168":1,"178":1,"273":1}}],["working",{"0":{"53":1,"101":1},"2":{"16":1,"18":1,"50":1,"97":1,"107":1,"166":1,"168":2,"176":1,"198":1,"207":1,"230":2,"272":1,"285":1}}],["work",{"0":{"81":1,"107":1},"1":{"82":1,"83":1,"84":1},"2":{"11":1,"16":1,"18":1,"65":2,"72":1,"82":1,"94":1,"97":1,"98":1,"125":1,"137":1,"146":1,"179":1,"180":1,"183":2,"186":1,"187":3,"188":1,"199":1,"214":1,"226":1,"230":1,"232":1,"242":1,"256":1,"257":1,"280":1,"283":1,"701":1}}],["workspaces",{"2":{"216":2}}],["works",{"0":{"1":1,"39":1,"97":1},"1":{"2":1,"3":1,"4":1},"2":{"63":1,"81":2,"94":3,"108":1,"137":2,"146":1,"193":1,"206":1,"224":1,"243":1}}],["wondeland",{"2":{"188":1}}],["wonderland",{"2":{"52":4,"53":4,"66":2,"78":1,"92":13,"93":2,"94":1,"113":2,"116":4,"128":27,"136":1,"164":20,"171":4,"174":4,"187":2,"189":1,"190":2,"191":2,"192":8,"193":2,"199":6,"200":6,"201":12,"202":12,"203":2,"210":1,"211":4,"217":2,"219":1,"220":1,"256":3,"702":6,"703":6,"710":2,"711":2,"733":1,"771":2}}],["won",{"2":{"11":1,"28":2,"69":1,"128":1,"153":1,"166":1,"208":1,"212":1,"701":1}}],["wallets",{"2":{"269":1}}],["walked",{"2":{"29":1}}],["walk",{"2":{"27":1,"42":1,"43":1,"224":1}}],["watch",{"2":{"210":1}}],["warehouse",{"0":{"681":1},"2":{"155":1,"669":1}}],["warn",{"2":{"153":3,"273":1,"473":1}}],["warning",{"2":{"98":1,"136":1,"153":1,"206":1,"212":1,"250":1,"253":1,"256":1,"273":1,"667":1,"741":1,"750":1}}],["waitstrategy",{"2":{"198":2}}],["waiting",{"2":{"157":1}}],["wait",{"2":{"16":1,"53":2,"88":1,"122":1,"151":3,"272":1,"755":2}}],["way",{"2":{"16":1,"18":1,"28":2,"39":1,"40":1,"41":1,"42":1,"49":1,"91":1,"92":1,"94":1,"141":1,"146":2,"166":2,"170":2,"176":1,"189":2,"193":1,"195":2,"210":1,"219":1,"248":1,"249":1,"258":1,"263":1,"264":1,"272":1,"665":2,"667":1,"701":1}}],["ways",{"2":{"3":1,"16":1,"56":1,"125":1,"141":1,"170":1,"219":2,"242":2,"270":1}}],["wants",{"2":{"135":1,"163":1}}],["wanted",{"2":{"94":1,"220":1}}],["want",{"2":{"16":3,"18":3,"22":1,"41":1,"65":1,"78":1,"89":2,"90":2,"91":1,"93":1,"94":2,"97":6,"101":2,"120":1,"135":1,"137":2,"146":1,"148":1,"150":1,"154":2,"156":1,"160":1,"163":2,"166":2,"183":1,"189":1,"193":1,"197":1,"210":1,"212":1,"217":2,"218":1,"219":1,"224":1,"228":1,"232":1,"233":3,"256":1,"263":1,"280":1,"734":1}}],["wasn",{"2":{"88":1}}],["wasmexecutionfail",{"0":{"653":1},"2":{"610":1}}],["wasmexecution",{"2":{"610":1}}],["wasminternalrepr",{"0":{"654":1},"2":{"505":2}}],["wasmiroha",{"2":{"168":1}}],["wasmsmartcontract",{"0":{"452":1,"655":1},"2":{"371":1,"631":1,"654":1}}],["wasmmaxmemory=524288000",{"2":{"128":2,"164":2}}],["wasmfuellimit=23000000",{"2":{"128":2,"164":2}}],["wasm$",{"2":{"109":1}}],["wasm32",{"2":{"108":2,"110":1}}],["wasm",{"0":{"100":1,"101":1,"109":1},"1":{"101":1,"102":1,"103":1,"104":1,"105":1,"106":1,"107":1,"108":1,"109":1,"110":1},"2":{"39":2,"50":1,"63":1,"71":1,"72":1,"76":1,"89":1,"97":5,"100":1,"101":3,"102":1,"103":7,"104":6,"105":4,"106":4,"109":8,"110":2,"113":2,"121":3,"128":2,"143":4,"164":8,"168":3,"170":2,"186":3,"216":1,"232":1,"233":1,"371":1,"464":1,"607":1,"631":1,"665":2}}],["was",{"2":{"6":2,"28":1,"32":1,"51":1,"52":1,"79":1,"88":5,"97":1,"161":1,"168":1,"190":1,"193":1,"203":4,"204":3,"220":2,"222":1,"230":1,"236":1,"674":1,"729":1,"748":1,"764":1,"773":1,"774":1}}],["wikipedia",{"2":{"238":3,"683":1}}],["wiki",{"2":{"228":1,"233":1,"238":1}}],["wip",{"2":{"206":1}}],["wiping",{"0":{"7":1}}],["window",{"2":{"168":1,"174":1,"187":6,"193":4,"249":1,"264":1,"265":1,"266":2}}],["windows",{"2":{"13":1,"16":3}}],["wish",{"2":{"29":1,"229":1}}],["wizards",{"2":{"28":1}}],["widely",{"2":{"197":1,"216":1}}],["wide",{"2":{"17":1,"40":1,"47":1,"82":2,"134":1,"238":1,"698":1}}],["will",{"2":{"6":2,"7":1,"8":1,"9":1,"11":2,"17":1,"21":1,"22":1,"28":1,"60":1,"63":1,"78":1,"81":1,"88":1,"94":1,"97":3,"98":2,"100":1,"106":1,"109":1,"117":1,"120":2,"123":1,"125":1,"126":1,"127":1,"128":3,"135":2,"150":2,"154":1,"156":1,"162":1,"166":2,"169":2,"170":8,"171":3,"174":2,"176":1,"178":1,"180":2,"183":2,"186":1,"187":7,"190":2,"193":1,"197":1,"199":3,"202":1,"207":1,"209":1,"211":2,"212":1,"218":1,"236":1,"238":2,"241":1,"247":1,"252":1,"256":2,"272":3,"273":3,"666":1,"693":2,"716":1,"720":1,"733":1,"769":1,"775":1}}],["withiroha",{"2":{"205":14}}],["within",{"2":{"31":1,"41":2,"71":1,"100":1,"101":1,"134":1,"137":1,"169":1,"170":2,"171":3,"174":1,"228":1,"238":1,"246":1,"262":1,"268":1}}],["withtimeout",{"2":{"199":4,"200":2,"201":4,"202":2,"203":2}}],["without",{"2":{"13":2,"16":2,"28":1,"42":1,"116":1,"128":1,"141":1,"154":1,"168":2,"176":1,"197":1,"218":1,"230":1,"232":1,"247":1,"254":1,"263":1,"266":1,"282":1,"694":1}}],["with",{"0":{"53":1,"101":1,"213":1,"238":1,"240":1},"1":{"214":1,"239":1,"241":1,"242":1,"243":1,"244":1},"2":{"5":1,"6":3,"16":2,"18":3,"20":1,"21":4,"24":1,"25":1,"36":1,"38":1,"39":1,"40":1,"46":1,"47":1,"50":3,"52":1,"53":2,"57":1,"62":1,"63":1,"65":2,"67":2,"69":3,"71":1,"72":1,"74":1,"79":1,"84":1,"85":2,"86":1,"88":1,"89":1,"91":2,"94":3,"96":1,"97":1,"98":1,"100":2,"103":1,"105":2,"108":1,"112":1,"117":1,"127":1,"128":1,"134":3,"137":5,"141":1,"146":5,"153":1,"154":1,"160":1,"162":1,"166":6,"168":4,"169":1,"170":7,"171":2,"174":2,"176":2,"177":1,"178":2,"179":1,"180":3,"185":1,"186":3,"187":16,"188":2,"189":3,"190":2,"193":6,"194":1,"195":3,"197":1,"198":2,"199":1,"200":1,"201":1,"202":1,"204":1,"206":1,"207":2,"210":1,"211":2,"212":1,"213":2,"214":3,"216":5,"218":6,"219":7,"220":4,"222":2,"223":2,"224":2,"225":1,"226":1,"227":2,"229":1,"230":1,"231":2,"233":1,"236":1,"237":1,"238":3,"246":1,"247":1,"248":3,"249":10,"252":6,"253":6,"255":2,"256":2,"257":1,"259":2,"260":4,"261":1,"263":2,"264":2,"266":1,"268":2,"270":3,"271":1,"272":5,"273":1,"274":1,"277":1,"279":1,"284":1,"285":1,"286":1,"287":2,"665":2,"666":1,"667":1,"671":1,"677":2,"680":1,"685":1,"686":1,"687":1,"689":1,"691":1,"696":1,"702":2,"703":2,"704":1,"705":2,"706":1,"707":1,"708":1,"709":1,"710":1,"711":1,"712":1,"713":1,"714":1,"716":5,"753":1,"757":2,"761":1,"762":1,"769":2,"770":1,"772":1,"775":3}}],["whl",{"2":{"207":1,"283":1}}],["whl$",{"2":{"207":1}}],["whilst",{"2":{"231":1}}],["while",{"2":{"36":1,"41":1,"74":1,"83":1,"88":1,"89":1,"92":1,"100":2,"117":1,"120":1,"126":1,"136":1,"137":1,"143":1,"150":1,"154":1,"166":1,"186":1,"206":1,"211":1,"218":1,"226":1,"238":1,"253":1,"256":1,"259":2,"266":1,"272":2,"278":1,"285":1,"665":1,"667":1,"716":1,"769":1}}],["whitespace",{"2":{"219":2,"220":2}}],["whitespaces",{"2":{"74":1}}],["whiterabbitasset",{"2":{"202":8}}],["whiterabbitkeypair",{"2":{"202":4}}],["whiterabbit",{"2":{"202":16,"203":14}}],["white",{"2":{"170":5,"172":3,"189":6,"190":2,"210":7,"211":1,"219":10,"220":3}}],["whitepaper",{"2":{"28":1,"228":1}}],["which",{"2":{"16":2,"17":1,"18":3,"19":1,"20":1,"21":1,"25":4,"28":2,"36":1,"38":1,"39":1,"41":3,"62":1,"69":1,"72":4,"74":1,"79":1,"88":3,"92":1,"94":2,"95":1,"97":1,"100":1,"103":1,"106":1,"124":1,"135":2,"143":1,"148":1,"151":1,"153":1,"161":1,"166":1,"170":3,"183":2,"185":1,"187":4,"188":1,"189":1,"193":2,"204":1,"209":4,"211":3,"214":2,"216":3,"217":1,"218":2,"219":1,"220":2,"221":1,"222":1,"230":1,"235":1,"236":1,"238":1,"241":1,"243":1,"247":1,"248":1,"249":1,"254":1,"256":1,"262":1,"264":2,"280":4,"282":1,"665":4,"671":1,"686":2,"719":1,"729":1,"772":1}}],["why",{"0":{"665":1},"2":{"16":1,"19":1,"28":1,"78":1,"79":1,"88":2,"97":2,"141":1,"163":1,"220":1,"262":1,"286":1}}],["what",{"0":{"184":1},"2":{"13":1,"17":1,"21":2,"24":1,"36":1,"39":2,"40":3,"79":2,"81":1,"94":2,"104":2,"105":1,"137":2,"138":1,"163":1,"166":4,"168":1,"176":1,"186":1,"190":2,"201":1,"204":1,"211":2,"212":2,"216":2,"218":2,"220":2,"230":1,"249":1,"252":1,"263":2,"278":1,"288":1,"665":1,"715":1,"734":1}}],["whole",{"2":{"97":2}}],["who",{"2":{"5":1,"88":1,"229":1,"253":1,"255":1,"267":1}}],["wheels",{"2":{"207":2}}],["wheel",{"2":{"101":1,"283":2}}],["whether",{"2":{"42":1,"62":1,"63":1,"65":1,"270":1,"773":1,"774":1}}],["whenever",{"2":{"94":1,"137":1,"249":1,"258":1,"259":1,"263":2,"266":1,"268":1}}],["when",{"2":{"4":1,"16":2,"18":1,"28":1,"31":1,"32":1,"33":1,"34":1,"35":1,"40":1,"41":2,"42":1,"51":1,"52":1,"57":1,"60":1,"65":1,"68":1,"69":1,"72":1,"81":1,"83":1,"88":5,"92":1,"93":1,"94":1,"97":1,"99":1,"105":2,"106":1,"111":1,"127":1,"135":1,"137":4,"141":1,"143":1,"146":1,"151":1,"153":1,"160":1,"166":1,"167":1,"174":1,"180":1,"186":1,"187":1,"189":1,"200":1,"209":1,"213":1,"220":2,"230":1,"233":1,"234":1,"245":1,"248":1,"249":1,"250":2,"252":1,"256":1,"259":1,"262":1,"264":1,"265":6,"266":1,"268":1,"272":1,"282":2,"283":1,"286":1,"684":1,"699":1,"710":2,"711":2,"717":1,"769":1}}],["whereis",{"2":{"244":2,"283":1}}],["whereas",{"2":{"126":1}}],["where",{"0":{"656":1},"2":{"0":1,"18":1,"20":1,"21":1,"28":1,"57":1,"67":1,"82":1,"84":1,"94":1,"127":1,"137":1,"146":1,"155":1,"160":1,"178":1,"186":1,"187":2,"245":1,"258":1,"261":1,"273":1,"280":1,"376":2,"665":1,"716":1,"772":2}}],["wear",{"2":{"673":1}}],["week",{"2":{"226":1}}],["welcome",{"2":{"213":8,"223":1}}],["well",{"2":{"16":1,"21":1,"43":1,"82":1,"92":1,"101":1,"104":1,"112":1,"125":1,"128":1,"129":2,"156":1,"167":1,"186":1,"187":3,"216":1,"228":1,"231":2,"232":1,"238":1,"243":1,"249":2,"250":1,"260":1,"667":1,"680":1,"691":1,"696":1}}],["website",{"2":{"264":1}}],["websites",{"2":{"249":1,"252":2}}],["websocket",{"2":{"187":3,"767":1,"768":1}}],["webassembly",{"2":{"227":1}}],["web$",{"2":{"186":1}}],["webpack",{"2":{"186":1}}],["web",{"0":{"193":1},"1":{"194":1},"2":{"22":1,"100":1,"113":2,"117":3,"164":2,"168":2,"170":2,"186":7,"187":4,"193":5,"233":1,"249":7}}],["went",{"2":{"21":1,"153":1}}],["we",{"2":{"4":1,"5":5,"10":1,"13":4,"14":4,"16":6,"17":1,"18":4,"20":2,"21":2,"25":2,"28":1,"29":2,"36":1,"39":1,"40":3,"42":4,"46":1,"65":1,"67":1,"78":1,"79":1,"81":3,"85":1,"88":5,"91":3,"92":2,"93":2,"94":8,"95":1,"96":1,"97":10,"99":3,"100":2,"101":3,"103":1,"104":3,"105":1,"107":1,"109":1,"110":2,"124":1,"128":1,"131":1,"137":2,"138":2,"141":2,"147":1,"160":1,"166":4,"167":3,"170":7,"171":2,"176":1,"177":1,"178":1,"180":5,"183":1,"186":3,"187":13,"188":6,"189":6,"190":7,"193":4,"195":1,"197":4,"199":3,"200":3,"202":2,"204":3,"206":1,"207":1,"209":2,"210":2,"211":6,"212":8,"216":9,"218":7,"219":4,"220":7,"222":5,"224":2,"225":1,"226":2,"229":2,"230":1,"231":4,"232":1,"236":1,"252":2,"253":1,"256":1,"264":1,"273":1,"282":1,"286":1,"287":1,"664":2,"665":3,"667":1,"715":1,"716":2,"722":1,"734":1,"753":2}}],["were",{"2":{"3":1,"42":1,"62":1,"88":1,"97":1,"151":1,"166":1,"220":1,"238":1,"670":2}}],["bcrypt",{"2":{"253":2}}],["bcprov",{"2":{"197":2}}],["bsd",{"2":{"241":1}}],["blssmall",{"2":{"299":1}}],["blsnormal",{"2":{"299":1}}],["bls",{"2":{"238":6,"239":4}}],["blob",{"2":{"39":1,"71":1,"72":1,"110":1,"121":1,"654":1}}],["blocktime",{"2":{"755":2}}],["blocktime=2000",{"2":{"128":2,"164":2}}],["blockrejectionreason",{"0":{"328":1},"2":{"545":1}}],["blockpayload",{"0":{"327":1,"575":1,"578":1,"597":1},"2":{"578":1,"580":2,"597":1}}],["blockmessage",{"0":{"326":1},"2":{"767":1}}],["blockheader",{"0":{"325":1},"2":{"327":1,"634":2,"747":1,"748":1}}],["blocker",{"2":{"249":3}}],["blocking",{"2":{"53":8,"199":1,"222":1}}],["blocksubscriptionrequest",{"0":{"329":1},"2":{"767":1}}],["blocks",{"0":{"767":1},"2":{"6":1,"7":3,"11":8,"21":3,"39":3,"89":1,"100":1,"112":1,"128":1,"143":4,"155":1,"164":4,"168":2,"178":1,"187":1,"193":5,"195":2,"213":4,"681":1,"682":1,"696":1,"746":1,"747":1,"767":2,"769":1,"775":6}}],["blockchains",{"0":{"138":1},"1":{"139":1,"140":1,"141":1},"2":{"14":1,"42":1,"141":1,"223":1,"671":1,"672":1,"722":1}}],["blockchain",{"0":{"7":1,"157":1,"158":1,"670":1},"1":{"159":1,"160":1,"161":1,"162":1,"163":1},"2":{"6":1,"11":2,"27":1,"29":2,"31":1,"36":3,"39":7,"40":2,"42":7,"43":1,"46":1,"50":1,"56":4,"65":1,"71":1,"88":1,"96":1,"101":1,"105":1,"110":1,"112":1,"116":1,"128":1,"136":1,"137":1,"139":2,"140":2,"141":9,"143":1,"146":2,"157":1,"158":1,"168":2,"170":1,"174":1,"178":1,"190":1,"195":1,"202":1,"208":1,"216":1,"220":1,"224":1,"227":2,"230":2,"232":1,"233":3,"236":1,"237":1,"238":2,"245":1,"246":1,"248":1,"251":1,"670":3,"672":1,"684":1,"696":1,"698":2,"717":1,"718":1,"719":1,"722":2,"726":1,"733":1,"746":1,"747":1,"749":1,"756":1,"757":1,"767":1}}],["block",{"0":{"128":1,"130":1,"195":1,"745":1},"1":{"129":1,"130":1,"131":1,"746":1,"747":1,"748":1},"2":{"6":2,"7":2,"11":8,"24":1,"28":3,"31":1,"32":2,"42":1,"72":1,"87":3,"88":6,"89":1,"97":2,"106":1,"125":1,"126":1,"128":10,"129":1,"130":5,"131":1,"132":1,"135":1,"141":2,"143":10,"151":5,"155":1,"164":12,"168":3,"174":3,"180":1,"190":1,"195":14,"197":2,"204":1,"222":1,"249":1,"272":2,"325":1,"431":1,"453":1,"542":1,"545":1,"609":1,"634":1,"671":1,"676":1,"681":1,"683":1,"685":1,"697":1,"747":1,"748":1,"755":2,"766":1,"767":4,"769":1,"771":10,"775":2}}],["black",{"2":{"28":2}}],["body",{"2":{"772":2,"775":2,"777":1}}],["boundaries",{"2":{"667":1}}],["boundary",{"2":{"665":1,"667":2}}],["bouncycastle",{"2":{"197":2}}],["boneh",{"2":{"238":2}}],["bones",{"2":{"188":1}}],["borrowing",{"2":{"216":1}}],["bob",{"2":{"128":3,"164":2,"199":2,"200":2,"201":2,"202":2,"704":8,"705":8,"706":8,"708":8,"709":8,"712":8,"713":8}}],["box",{"2":{"96":2,"200":1,"218":1,"757":4}}],["boxes",{"2":{"94":1,"200":1}}],["boilerplates",{"2":{"188":1}}],["boilerplate",{"2":{"93":1,"199":1,"218":1,"665":1}}],["boot",{"2":{"248":1}}],["bootstrapping",{"2":{"161":1,"671":1}}],["bootstrap",{"2":{"21":1}}],["books",{"2":{"670":2}}],["book",{"2":{"216":1}}],["boolean",{"2":{"50":1,"198":6}}],["bool",{"0":{"366":1,"657":1},"2":{"36":1,"300":2,"331":1,"456":1,"501":1,"522":2,"634":2,"657":1}}],["bottom",{"2":{"94":1}}],["bottlenecks",{"2":{"10":1}}],["both",{"2":{"19":1,"42":1,"49":1,"67":1,"82":1,"122":1,"125":2,"128":1,"134":1,"168":1,"187":1,"189":1,"206":1,"210":1,"219":1,"224":1,"227":1,"229":1,"234":1,"252":1,"256":1,"259":1,"270":1,"273":2,"279":1,"722":1}}],["bft",{"0":{"19":1,"677":1},"1":{"20":1,"21":1},"2":{"135":1,"216":1,"669":1}}],["bruteforce",{"2":{"252":1}}],["brute",{"2":{"252":1,"253":1}}],["brackets",{"2":{"282":1}}],["brave",{"2":{"249":1}}],["branches",{"2":{"206":1}}],["branch",{"2":{"103":2,"183":6,"186":3,"197":1,"206":1,"207":2}}],["brands",{"2":{"268":1}}],["brand",{"2":{"72":1,"78":1}}],["browsing",{"2":{"249":3}}],["browsers",{"0":{"249":1},"2":{"246":1,"249":4}}],["browser",{"2":{"187":2,"249":13}}],["broken",{"2":{"180":1}}],["breaches",{"2":{"250":2,"260":1}}],["breach",{"2":{"245":1,"246":1,"250":1,"268":1}}],["breakfor",{"2":{"212":1}}],["breaking",{"2":{"107":1}}],["break",{"2":{"97":1,"212":1,"234":1,"282":1}}],["breaks",{"2":{"16":1}}],["brew",{"2":{"182":3}}],["brightest",{"2":{"189":1,"210":1,"219":1}}],["brief",{"0":{"166":1},"2":{"238":1}}],["bring",{"2":{"167":1,"213":1}}],["brings",{"2":{"19":1}}],["bringing",{"2":{"17":1}}],["balancing",{"2":{"287":1}}],["balanced",{"2":{"97":1,"238":1}}],["balance",{"2":{"65":1,"202":8,"203":8,"234":1}}],["backend",{"2":{"287":1}}],["back",{"2":{"258":1,"263":2,"769":1,"775":1}}],["backups",{"2":{"258":2}}],["bad",{"2":{"222":1,"249":1}}],["bane",{"2":{"220":1}}],["banknotes",{"2":{"673":1}}],["bank",{"2":{"158":1,"236":1,"259":2}}],["barring",{"2":{"212":1}}],["bare",{"0":{"13":1,"18":1},"1":{"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1},"2":{"13":2,"14":1,"74":1,"188":1}}],["batchedresponse",{"0":{"322":1,"323":1},"2":{"648":1,"649":1}}],["batched",{"2":{"218":1}}],["batch",{"2":{"143":4,"161":1,"164":4,"322":1,"323":1}}],["base",{"0":{"231":1},"2":{"218":1,"231":1,"443":1}}],["based",{"2":{"13":1,"49":1,"62":1,"89":1,"97":1,"136":1,"200":1,"232":1,"235":1,"249":3,"259":1,"260":1,"263":1,"264":1,"670":1,"674":1,"684":1}}],["basics",{"2":{"91":1,"197":1,"216":1}}],["basic",{"0":{"117":1},"2":{"72":1,"85":1,"103":1,"113":2,"117":4,"164":2,"170":2,"178":1,"197":1,"216":1,"224":1,"257":1,"272":1,"749":1,"753":1}}],["bashpublic",{"2":{"170":1}}],["bashrc$",{"2":{"244":1}}],["bashrc",{"2":{"16":1,"244":4}}],["bash",{"0":{"165":1},"1":{"166":1,"167":1,"168":1,"169":1,"170":1,"171":1,"172":1,"173":1,"174":1},"2":{"6":1,"11":1,"20":1,"27":1,"29":1,"42":1,"43":2,"44":1,"178":2,"213":1,"225":1,"239":7,"244":1,"771":1}}],["bash$",{"2":{"6":4,"7":1,"9":1,"11":1,"16":4,"17":3,"18":1,"20":1,"21":2,"22":3,"103":1,"108":1,"109":1,"114":1,"130":3,"134":1,"144":1,"167":4,"168":4,"169":2,"170":5,"171":3,"172":1,"173":1,"174":1,"176":1,"177":2,"182":2,"183":3,"186":6,"207":4,"213":1,"216":1,"238":1,"241":1,"242":1,"243":1,"244":4,"279":3,"280":2,"281":1,"282":6}}],["biometric",{"2":{"260":1}}],["birthdays",{"2":{"137":1,"253":1}}],["bigger",{"2":{"154":1}}],["big",{"2":{"102":1,"105":2,"154":1,"166":1,"220":1}}],["bigquantity",{"2":{"25":1,"211":1,"319":1,"320":1}}],["bill",{"2":{"24":2}}],["bin$",{"2":{"242":1,"243":1,"244":1}}],["bin",{"0":{"242":1,"243":1,"244":1},"1":{"244":1},"2":{"14":2,"17":2,"22":2,"134":2,"170":2,"238":2,"239":8,"241":2,"242":2,"243":2,"244":6,"280":1,"282":4,"283":1}}],["binding",{"0":{"667":1},"1":{"668":1},"2":{"187":2,"213":8,"666":2,"667":1}}],["bindings",{"2":{"103":2}}],["bind",{"2":{"8":1,"187":3,"193":2}}],["binaryopincompatiblenumericvaluetypes",{"2":{"476":1}}],["binaryopincompatiblenumericvaluetypeserror",{"0":{"324":1},"2":{"476":1}}],["binary",{"2":{"6":1,"14":3,"17":3,"39":1,"76":1,"100":1,"101":1,"102":1,"103":1,"105":7,"109":2,"167":2,"187":1,"241":1,"242":1,"243":3,"248":2,"665":1,"683":1,"686":1,"753":2}}],["bits",{"2":{"252":3,"273":1}}],["bitcoin",{"2":{"238":2}}],["bit",{"2":{"5":1,"25":3,"84":1,"101":1,"189":1,"211":2,"219":1,"665":1,"775":1}}],["bucket",{"2":{"771":24}}],["bugging",{"2":{"273":1}}],["bug",{"2":{"249":1,"270":1}}],["bunny",{"2":{"190":1,"210":4}}],["bundler$",{"2":{"186":1}}],["bundlers",{"2":{"186":1}}],["bundler",{"2":{"186":2}}],["bunyan",{"2":{"154":5}}],["bulk",{"2":{"50":1}}],["burnbox",{"0":{"330":1},"2":{"221":2,"457":1}}],["burnasset",{"2":{"203":2}}],["burnassets",{"2":{"203":5}}],["burning",{"0":{"173":1,"203":1,"221":1},"2":{"43":2,"173":1,"203":5,"221":1,"704":2}}],["burn",{"0":{"43":1},"2":{"40":2,"41":5,"43":1,"44":1,"56":1,"86":1,"94":1,"98":1,"173":2,"203":4,"221":8,"457":1,"461":1,"698":2,"701":2,"703":9,"704":5}}],["burned",{"2":{"27":1}}],["but",{"2":{"11":1,"13":1,"14":1,"16":1,"18":2,"28":1,"39":1,"42":3,"65":1,"79":1,"87":1,"88":3,"94":1,"100":1,"103":1,"105":2,"106":1,"125":1,"128":1,"137":2,"160":1,"163":1,"166":4,"168":1,"176":1,"187":2,"188":1,"189":2,"210":2,"211":1,"216":1,"217":1,"219":2,"220":2,"222":2,"229":1,"230":1,"234":1,"238":1,"243":1,"249":1,"254":1,"255":1,"256":1,"259":1,"264":1,"273":1,"278":1,"280":1,"281":2,"664":1,"665":1,"716":2,"722":1,"729":1,"732":1,"733":1,"753":1,"769":1,"777":1}}],["button",{"2":{"5":1,"193":8,"265":1}}],["builds",{"2":{"248":1}}],["buildsigned",{"2":{"199":4,"200":4,"201":6,"202":4,"203":2,"205":28}}],["build$",{"2":{"207":1}}],["builder",{"2":{"188":1,"205":18,"220":1}}],["builders",{"2":{"42":1}}],["building",{"0":{"241":1},"2":{"14":1,"104":1,"218":2,"241":1}}],["build`",{"2":{"6":1}}],["build",{"0":{"175":1,"177":1},"1":{"176":1,"177":1},"2":{"6":3,"14":9,"22":3,"62":1,"101":1,"108":6,"167":1,"170":2,"174":2,"175":2,"177":4,"178":1,"184":1,"189":1,"193":1,"197":2,"207":4,"213":1,"217":1,"218":2,"219":1,"222":1,"241":3,"248":1,"714":2}}],["built",{"0":{"242":1},"2":{"0":1,"14":1,"24":1,"63":2,"134":1,"141":2,"170":1,"190":1,"193":1,"201":1,"204":1,"211":1,"220":1,"222":1,"241":1,"249":6,"693":1}}],["byshortened",{"2":{"621":1}}],["bysome",{"2":{"38":1,"94":13,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1}}],["byextended",{"2":{"621":1}}],["bydomain",{"2":{"338":1}}],["bydeleted",{"2":{"291":1,"310":1,"316":1,"343":1,"562":1,"621":1}}],["bytrigger",{"2":{"338":1}}],["bytotalquantitychanged",{"2":{"310":1}}],["bytearray",{"2":{"204":2}}],["byte",{"2":{"143":8,"164":8,"193":4,"205":2,"273":1,"474":1}}],["bytestohex",{"2":{"193":4}}],["bytes",{"2":{"100":1,"113":2,"121":3,"143":2,"164":4,"170":2,"189":2,"193":4,"273":3,"607":1}}],["byownerchanged",{"2":{"310":1}}],["bymintabilitychanged",{"2":{"310":1}}],["bymetadataremoved",{"2":{"291":1,"310":1,"316":1,"343":1}}],["bymetadatainserted",{"2":{"291":1,"310":1,"316":1,"343":1}}],["byrole",{"2":{"338":1}}],["byrolegranted",{"2":{"291":1}}],["byrolerevoked",{"2":{"291":1}}],["byremoved",{"2":{"94":3,"316":1,"535":1}}],["bypeer",{"2":{"338":1}}],["bypermissionremoved",{"2":{"291":1,"562":1}}],["bypermissionadded",{"2":{"291":1}}],["bypass",{"2":{"263":1}}],["byadded",{"2":{"316":1,"535":1}}],["byassetdefinition",{"2":{"338":1,"343":1}}],["byasset",{"2":{"291":1,"338":1}}],["byauthenticationremoved",{"2":{"291":1}}],["byauthenticationadded",{"2":{"291":1}}],["byaccount",{"2":{"94":3,"338":1,"343":1}}],["bycreated",{"2":{"291":1,"310":1,"316":1,"343":1,"562":1,"621":1}}],["byzantine",{"0":{"677":1},"2":{"146":1,"163":1,"230":2,"669":1,"697":1}}],["by",{"0":{"90":1,"91":1},"1":{"92":1,"93":1,"94":1,"95":1,"96":1,"97":1},"2":{"3":3,"11":1,"16":1,"17":1,"22":1,"24":2,"28":1,"38":2,"50":1,"53":2,"57":1,"62":1,"63":1,"65":1,"69":1,"85":3,"87":1,"88":1,"93":1,"94":2,"97":3,"100":1,"110":1,"117":2,"125":1,"126":1,"127":1,"128":1,"134":2,"137":3,"141":5,"150":1,"166":2,"167":1,"187":1,"188":1,"197":2,"204":1,"211":3,"212":1,"216":4,"218":1,"220":1,"222":1,"226":1,"230":1,"233":1,"238":2,"243":1,"244":4,"245":1,"248":1,"249":1,"250":4,"252":3,"253":2,"255":2,"258":1,"259":1,"262":1,"263":2,"264":1,"265":1,"266":1,"268":2,"272":1,"273":2,"308":1,"624":1,"665":1,"687":1,"693":2,"699":1,"716":1,"717":1,"728":2,"733":1,"734":1,"741":1,"742":1,"743":1,"749":1,"752":1,"754":2,"755":1,"758":1,"759":1,"766":1,"772":3}}],["bevendorff",{"2":{"268":1}}],["beauty",{"2":{"263":1}}],["beautiful",{"2":{"146":1}}],["beneficial",{"2":{"268":1}}],["benefit",{"2":{"216":2}}],["bench",{"2":{"231":1}}],["beyond",{"2":{"212":1}}],["beginning",{"2":{"282":1}}],["begin",{"2":{"166":1}}],["begins",{"2":{"28":1,"65":1}}],["become",{"2":{"209":1}}],["becomes",{"2":{"137":2}}],["because",{"2":{"28":1,"46":1,"88":2,"94":1,"97":1,"109":1,"163":1,"167":1,"168":1,"183":1,"186":1,"190":1,"193":1,"195":1,"219":2,"220":2,"282":1,"286":1,"665":1,"673":1}}],["beside",{"2":{"153":1}}],["besides",{"2":{"125":1}}],["best",{"2":{"88":1,"161":1,"189":2,"207":1,"210":2,"219":2,"226":1,"231":1,"234":1,"246":2,"248":1,"261":1,"268":2,"667":1}}],["belonging",{"2":{"256":1}}],["belongs",{"2":{"137":1,"220":1,"686":1,"733":1}}],["belong",{"2":{"116":1,"128":1,"137":1,"220":1,"730":1,"737":1}}],["below",{"2":{"29":1,"97":1,"137":1,"138":1,"191":1,"229":1,"264":1,"667":1,"668":1,"716":2}}],["being",{"2":{"41":1,"88":1,"107":1,"135":1,"166":1,"230":3,"231":1,"234":1,"235":1,"249":3,"256":1,"677":1}}],["between",{"0":{"137":1},"2":{"29":3,"39":1,"40":1,"44":1,"56":1,"90":1,"124":1,"137":1,"149":1,"170":1,"178":1,"225":1,"229":1,"234":1,"238":1,"249":1,"272":2,"698":1}}],["better",{"2":{"0":1,"13":1,"186":1,"187":1}}],["behalf",{"2":{"216":1,"256":3}}],["behaviour",{"2":{"89":2,"97":1,"99":1,"665":1,"667":1}}],["behavior",{"2":{"3":1}}],["behave",{"2":{"87":1,"166":1}}],["behind",{"2":{"28":1,"217":1,"257":1}}],["been",{"2":{"24":1,"78":1,"97":1,"141":1,"169":1,"170":1,"190":1,"200":1,"201":2,"211":1,"220":1,"248":1,"262":1,"272":2}}],["before",{"2":{"6":1,"7":1,"8":1,"9":1,"59":1,"65":1,"87":1,"88":1,"89":1,"138":1,"151":3,"170":2,"178":1,"186":1,"187":1,"210":1,"248":1,"249":2,"273":1,"769":1}}],["be",{"2":{"2":1,"3":1,"6":2,"7":1,"11":1,"16":1,"17":2,"18":3,"20":2,"21":3,"24":3,"25":1,"27":1,"28":3,"32":1,"37":1,"40":1,"41":4,"42":6,"43":4,"44":1,"45":2,"50":2,"52":1,"56":3,"59":2,"62":4,"63":1,"64":1,"65":5,"66":1,"71":1,"72":2,"78":3,"80":1,"82":3,"84":2,"88":4,"89":1,"90":2,"97":5,"98":2,"100":1,"101":1,"102":2,"104":4,"106":2,"110":3,"116":2,"117":1,"122":1,"125":3,"126":4,"127":3,"128":3,"130":1,"132":1,"134":1,"135":2,"137":2,"138":4,"139":1,"141":6,"146":3,"149":1,"150":1,"153":1,"154":1,"159":2,"160":1,"161":1,"166":2,"168":2,"169":1,"170":1,"171":2,"176":1,"180":1,"186":3,"187":3,"188":1,"190":5,"200":1,"201":1,"204":1,"207":1,"208":2,"209":2,"211":4,"213":1,"214":1,"216":1,"217":1,"220":5,"222":1,"229":2,"230":2,"232":2,"233":1,"237":1,"238":3,"241":3,"242":1,"246":1,"247":3,"248":1,"249":7,"250":2,"252":4,"253":2,"255":1,"256":3,"259":3,"261":1,"264":2,"266":2,"268":4,"272":3,"273":1,"280":1,"282":1,"665":2,"667":1,"668":7,"671":2,"673":2,"674":1,"675":1,"687":1,"689":1,"693":2,"697":1,"701":1,"715":1,"716":4,"722":1,"749":1,"750":1,"759":1,"766":2,"769":2,"772":2,"775":1}}],["aid",{"2":{"249":1}}],["air",{"2":{"248":1}}],["aiming",{"2":{"264":1}}],["aim",{"2":{"88":1,"247":1,"252":2,"273":1}}],["awareness",{"2":{"260":2}}],["away",{"2":{"248":1}}],["await",{"2":{"187":4,"188":6,"192":6,"193":8,"195":2,"199":2,"200":2,"201":4,"202":2,"203":2}}],["audits",{"2":{"260":2}}],["audit",{"2":{"249":1}}],["audited",{"2":{"248":1,"249":2}}],["auditing",{"2":{"231":1}}],["authenticator",{"2":{"259":1}}],["authenticated",{"2":{"243":1}}],["authenticationremoved",{"2":{"290":1}}],["authenticationadded",{"2":{"290":1}}],["authentication",{"0":{"117":1},"2":{"117":2,"243":1,"259":3,"263":1,"268":1}}],["authenticity",{"2":{"237":1,"255":1,"256":1}}],["auth",{"2":{"113":2,"117":2,"164":2,"170":2}}],["authorised",{"2":{"260":1}}],["authorization",{"2":{"117":1,"266":1}}],["authority",{"2":{"78":1,"261":1,"296":1,"297":1,"373":1,"374":1,"551":1,"608":1}}],["author",{"2":{"88":1}}],["automatic",{"2":{"140":1,"146":1,"158":1}}],["automatically",{"2":{"3":1,"7":1,"128":1}}],["automated",{"2":{"109":1,"260":1}}],["a4c4dadd9f18b0f63d6a420151fe0748d785475dec63034a15fcf999ceda1e65",{"2":{"170":2}}],["a753146e75b910ae5e2994dc8adea9e7d87e5d53024cfa310ce992f17106f92c",{"2":{"170":2}}],["a70dab95c7482eb9f159111b65947e482108cfe67df877bd8d3b9441a781c7c98e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f",{"2":{"18":2}}],["ahem",{"2":{"101":2}}],["ago",{"2":{"278":1}}],["agent",{"0":{"263":1},"2":{"263":11,"264":1,"265":5,"266":3}}],["age",{"2":{"253":1}}],["agebased",{"2":{"198":2}}],["agnostic",{"2":{"195":1}}],["aggregated",{"2":{"733":1,"735":1}}],["aggregate",{"2":{"186":1}}],["agreed",{"2":{"693":1}}],["agreement",{"2":{"137":1}}],["agree",{"2":{"88":2,"135":1,"665":1}}],["again",{"2":{"18":1,"84":1,"190":1,"220":2}}],["against",{"2":{"6":1,"57":1,"97":1,"101":1,"209":1,"241":2,"664":1}}],["among",{"2":{"97":1,"229":1,"283":1}}],["amounttotransfer",{"2":{"191":4}}],["amounts",{"2":{"67":1}}],["amount",{"2":{"39":1,"43":1,"82":1,"89":1,"94":1,"97":3,"166":1,"211":7,"220":2,"252":2,"307":1,"314":1,"676":1,"755":2,"771":34}}],["amp",{"2":{"53":4,"187":11,"217":2,"218":2,"263":1,"666":4,"668":4}}],["amazing",{"2":{"36":1}}],["affected",{"2":{"250":1}}],["affects",{"2":{"217":1}}],["affectionately",{"2":{"39":1}}],["aforementioned",{"2":{"200":1,"222":1,"233":1}}],["afterwards",{"2":{"190":1}}],["after",{"2":{"3":1,"6":1,"11":1,"22":1,"25":1,"43":1,"61":1,"84":1,"88":1,"95":1,"135":2,"151":1,"161":1,"162":1,"171":2,"172":1,"175":1,"189":2,"191":1,"193":1,"195":1,"202":1,"203":4,"204":1,"207":1,"209":1,"210":2,"213":1,"219":4,"220":2,"222":1,"234":1,"238":1,"249":1,"282":1,"670":1,"767":2,"768":1}}],["abstract",{"2":{"665":1}}],["ability",{"2":{"168":2,"260":1}}],["abi",{"2":{"103":2,"667":1}}],["abundance",{"2":{"79":1}}],["able",{"2":{"28":1,"42":1,"56":1,"78":1,"139":1,"141":1,"166":1,"171":2,"187":2,"222":1,"242":1,"280":1,"677":1,"722":1}}],["abort",{"2":{"105":4,"108":2}}],["about",{"2":{"13":2,"16":1,"24":1,"25":1,"29":1,"40":1,"42":1,"57":1,"65":1,"70":1,"80":1,"81":1,"88":1,"98":1,"99":1,"103":1,"116":1,"124":1,"127":1,"132":1,"134":1,"137":3,"141":2,"151":1,"161":1,"163":2,"170":3,"178":1,"189":1,"190":1,"193":1,"200":1,"201":1,"204":1,"211":2,"216":1,"219":1,"220":1,"222":1,"224":1,"225":1,"231":1,"233":1,"246":1,"247":1,"253":1,"259":2,"260":1,"268":2,"270":1,"700":1,"716":2,"717":1,"722":1,"735":1}}],["above",{"2":{"6":1,"16":1,"97":1,"110":1,"125":1,"128":2,"134":1,"168":1,"170":2,"187":1,"197":1,"216":1,"244":1,"268":1,"666":1}}],["arise",{"2":{"286":2}}],["architectural",{"2":{"262":1}}],["architectures",{"2":{"234":1}}],["architecture",{"2":{"79":1,"99":1,"103":1,"759":1}}],["artist",{"2":{"674":1}}],["article",{"2":{"249":1}}],["artifacts",{"2":{"166":1,"177":1}}],["args",{"2":{"199":2}}],["argument",{"2":{"166":1,"168":1,"170":2}}],["arguments",{"2":{"166":1,"217":1}}],["arrange",{"2":{"282":1}}],["arraylist",{"2":{"205":4}}],["arrays",{"2":{"50":2}}],["array",{"0":{"301":1,"302":1,"303":1,"304":1,"305":1},"2":{"22":1,"42":1,"135":3,"199":2,"286":1,"301":1,"302":1,"303":1,"304":1,"305":1,"448":1,"466":1,"467":1,"468":1,"469":1}}],["arrived",{"2":{"92":1}}],["around",{"2":{"74":1,"94":1,"97":1,"177":1,"269":1,"665":2,"667":1,"716":1}}],["areas",{"2":{"260":2}}],["are",{"0":{"282":1},"2":{"0":2,"2":1,"3":3,"5":1,"13":1,"16":2,"18":3,"19":1,"22":1,"28":3,"31":2,"32":2,"33":2,"34":1,"35":1,"39":3,"40":2,"41":2,"42":2,"43":2,"45":1,"46":1,"50":2,"51":1,"52":1,"56":3,"57":3,"62":4,"63":1,"65":2,"71":1,"83":1,"85":1,"86":2,"87":4,"89":1,"94":1,"97":3,"99":1,"100":3,"104":1,"105":3,"111":1,"117":1,"124":1,"126":2,"127":2,"128":2,"134":3,"137":4,"139":1,"143":4,"151":1,"153":3,"155":1,"160":1,"166":4,"167":1,"168":2,"170":1,"171":2,"177":1,"178":1,"180":1,"185":1,"186":5,"187":2,"189":1,"190":1,"193":1,"195":1,"198":2,"199":2,"200":2,"201":1,"204":1,"207":1,"209":2,"210":2,"211":2,"212":1,"216":3,"217":2,"219":1,"220":3,"222":1,"225":1,"226":1,"229":5,"230":1,"231":1,"232":1,"234":1,"235":2,"238":4,"241":1,"242":1,"244":1,"245":1,"248":2,"249":5,"250":2,"253":1,"256":3,"258":1,"259":1,"260":4,"261":1,"262":1,"264":1,"266":3,"268":1,"270":2,"271":1,"272":3,"273":2,"274":1,"277":1,"280":1,"282":1,"284":1,"285":1,"287":1,"665":4,"667":4,"668":1,"670":2,"671":1,"673":3,"674":1,"684":2,"690":1,"698":1,"699":2,"701":1,"716":2,"721":1,"722":2,"739":1,"749":1,"755":1,"767":2,"768":1,"769":2,"772":1,"775":2}}],["acknowledging",{"2":{"267":1}}],["academy",{"2":{"263":3}}],["achieving",{"2":{"247":1}}],["achieve",{"2":{"90":1,"186":1,"218":1,"697":1}}],["across",{"2":{"135":2,"234":1,"248":1,"287":2,"667":1,"733":1,"735":1}}],["acts",{"2":{"264":1}}],["actors",{"2":{"677":2}}],["actor",{"2":{"143":8,"163":1,"164":8,"213":8}}],["activities",{"2":{"249":1,"254":1,"260":3,"692":1}}],["actively",{"2":{"230":2,"716":1}}],["active",{"2":{"170":2,"186":1,"235":1,"272":2,"282":1,"760":1}}],["activates",{"2":{"79":1}}],["acting",{"2":{"137":1}}],["actionable",{"2":{"260":1}}],["action",{"0":{"75":1,"76":1,"77":1,"78":1,"79":1,"80":1,"296":1,"297":1},"1":{"76":1,"77":1,"78":1,"79":1,"80":1},"2":{"62":1,"73":4,"75":3,"76":1,"95":7,"250":1,"613":2,"614":2}}],["actions",{"2":{"56":1,"260":1}}],["actual",{"2":{"14":1,"65":1,"88":1,"104":1,"255":1,"487":1,"488":1,"489":1,"583":1}}],["actually",{"2":{"14":1,"19":1,"212":1,"218":1}}],["accuracy",{"2":{"670":1}}],["accidentally",{"2":{"249":1}}],["accompanied",{"2":{"233":1,"754":1}}],["accommodated",{"2":{"209":1}}],["accordance",{"2":{"180":1}}],["accordingly",{"2":{"775":1}}],["according",{"2":{"87":1,"186":1,"775":1}}],["accountdoesnotexist",{"2":{"610":1}}],["accountrolechanged",{"0":{"295":1},"2":{"290":2}}],["accountpermissionchanged",{"0":{"294":1},"2":{"290":2}}],["accounteventfilter",{"0":{"291":1,"379":1},"2":{"292":1,"379":1}}],["accountevent",{"0":{"290":1,"388":1,"523":1},"2":{"292":1,"339":1,"342":1,"388":1}}],["account=",{"2":{"171":2,"173":2}}],["accountfilter",{"0":{"292":1,"380":1},"2":{"94":3,"338":1,"343":1,"380":1}}],["accountid`",{"2":{"219":2}}],["accountid",{"0":{"293":1,"348":1,"479":1,"588":1},"2":{"52":2,"53":4,"66":2,"75":2,"92":6,"94":2,"104":4,"128":2,"164":2,"187":8,"189":10,"190":4,"191":6,"193":4,"199":10,"200":2,"201":4,"202":6,"203":2,"205":14,"217":2,"219":7,"289":1,"290":5,"294":1,"295":1,"296":1,"297":1,"308":1,"313":1,"318":1,"341":1,"373":1,"374":1,"401":1,"402":1,"423":1,"431":1,"433":1,"435":1,"438":1,"454":2,"470":1,"479":1,"493":1,"523":1,"551":1,"588":1,"608":1,"702":2,"703":2,"704":8,"705":6,"706":8,"708":8,"709":8,"710":2,"711":2,"712":8,"713":8,"714":4,"721":1,"724":1,"727":2,"728":1,"730":1,"731":1,"737":1,"757":1,"773":1,"774":1}}],["accounts`",{"2":{"192":2}}],["accounts",{"0":{"23":1,"92":1,"141":1,"192":1},"2":{"29":3,"33":1,"40":2,"41":6,"42":3,"44":1,"50":1,"51":1,"55":1,"56":3,"62":1,"66":1,"69":1,"72":1,"80":1,"94":1,"111":1,"116":1,"128":5,"129":2,"130":3,"136":1,"139":2,"141":5,"168":2,"169":2,"170":3,"178":1,"192":8,"200":4,"201":2,"202":2,"203":2,"216":1,"227":1,"259":1,"341":1,"692":1,"698":2,"699":1,"722":2,"725":1,"726":1,"729":1,"730":2,"731":1,"733":1,"744":1,"749":1,"771":10}}],["account",{"0":{"78":1,"116":1,"136":1,"170":1,"189":1,"200":1,"210":1,"219":1,"289":1,"588":1,"725":1,"773":1},"1":{"726":1,"727":1,"728":1,"729":1,"730":1,"731":1},"2":{"18":10,"29":5,"31":1,"36":4,"38":1,"39":2,"40":1,"41":7,"42":9,"43":1,"44":1,"45":1,"52":2,"53":5,"56":1,"57":3,"59":1,"60":2,"65":1,"69":1,"72":2,"75":2,"78":5,"82":1,"86":2,"93":2,"94":5,"95":2,"104":6,"113":2,"115":2,"116":3,"128":13,"131":1,"135":3,"136":2,"137":2,"139":1,"141":8,"143":9,"145":2,"151":7,"164":12,"168":4,"169":1,"170":24,"171":5,"174":2,"178":1,"187":4,"188":1,"189":7,"190":2,"191":5,"192":20,"193":4,"199":6,"200":14,"201":8,"202":10,"203":6,"205":88,"208":1,"210":9,"216":1,"217":4,"219":21,"220":4,"225":1,"229":2,"273":1,"290":1,"294":1,"295":1,"318":1,"339":1,"341":1,"342":1,"423":1,"431":1,"438":1,"455":2,"470":1,"555":1,"588":1,"699":2,"701":2,"710":5,"711":5,"714":2,"721":1,"722":1,"724":1,"726":1,"727":2,"728":2,"729":2,"730":1,"731":1,"733":1,"737":1,"757":1,"773":2,"774":2}}],["acceptable",{"2":{"187":3}}],["acceptall",{"2":{"38":1,"94":3,"97":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"503":1}}],["accepted",{"2":{"138":1,"252":1,"260":1,"771":2,"775":6,"777":1}}],["accepts",{"2":{"39":1,"238":1}}],["accept",{"2":{"38":1,"94":1,"135":1,"680":1,"775":1}}],["accessing",{"2":{"258":1,"259":1}}],["accessible",{"2":{"11":1,"65":1,"106":1,"187":1,"229":1,"243":1,"250":1,"260":1}}],["accessed",{"2":{"216":1,"263":1,"266":1}}],["access",{"2":{"11":1,"21":1,"53":5,"60":3,"65":1,"117":1,"128":2,"137":2,"164":2,"189":1,"210":1,"216":2,"219":1,"246":1,"247":1,"248":2,"249":3,"250":1,"251":1,"258":1,"260":6,"262":1,"263":5,"267":1,"268":1,"693":1}}],["ads",{"2":{"249":1}}],["ad",{"2":{"249":3}}],["adopting",{"2":{"247":1,"250":1,"268":1}}],["adopted",{"2":{"247":1}}],["adopt",{"2":{"246":1,"259":1,"261":1}}],["ado",{"2":{"197":1}}],["adapter",{"2":{"187":4,"193":2}}],["adapters",{"2":{"187":2}}],["adhere",{"2":{"121":1,"260":1}}],["adminiroha2client",{"2":{"199":6}}],["administrator",{"2":{"138":1,"160":2,"161":1}}],["administrators",{"2":{"65":1}}],["adminkeypair",{"2":{"199":6}}],["admin",{"2":{"197":2,"199":18,"200":8,"201":14,"202":8,"203":6,"720":2}}],["admittedly",{"2":{"97":1}}],["advice",{"2":{"247":1,"252":1,"256":1}}],["advised",{"2":{"108":1,"252":1}}],["advise",{"2":{"14":1,"109":1,"218":1,"252":1}}],["advance",{"2":{"250":1}}],["advanced",{"0":{"105":1},"1":{"106":1,"107":1,"108":1,"109":1,"110":1},"2":{"28":1,"36":1,"46":1,"222":1,"265":1}}],["advantage",{"2":{"101":1}}],["advantages",{"2":{"97":1}}],["adjust",{"2":{"18":1}}],["adjusted",{"2":{"18":1,"186":1}}],["addclient",{"2":{"186":2}}],["added",{"2":{"88":1,"135":1,"137":1,"138":1,"176":1,"195":1,"200":2,"201":1,"216":1,"226":1,"237":1,"249":1,"265":1,"315":1,"534":1,"767":1}}],["additional",{"2":{"249":2,"259":1,"264":1,"672":1}}],["additionally",{"2":{"25":1,"252":1}}],["addition",{"2":{"201":1,"244":1,"252":1}}],["adding",{"0":{"264":1},"1":{"265":1,"266":1},"2":{"16":1,"42":1,"150":1,"244":1,"252":1,"265":2}}],["addr=iroha3",{"2":{"213":6}}],["addr=iroha0",{"2":{"213":6}}],["addr=iroha2",{"2":{"213":6}}],["addr=iroha1",{"2":{"213":6}}],["addr=",{"2":{"20":2}}],["addr",{"0":{"149":1},"2":{"18":8,"21":1,"22":1,"127":1,"143":3,"147":1,"149":1,"164":2}}],["addresses",{"0":{"118":1,"147":1},"1":{"119":1,"120":1,"148":1,"149":1,"150":1},"2":{"18":1,"20":1,"21":1,"118":1,"143":1,"146":1,"253":1,"259":1}}],["address",{"2":{"11":1,"18":33,"20":1,"21":1,"22":1,"65":1,"97":1,"119":1,"120":1,"141":1,"146":8,"149":2,"150":1,"249":1,"259":2,"260":2,"537":1,"754":1}}],["add",{"0":{"298":1},"2":{"5":1,"16":2,"22":1,"39":1,"53":4,"60":4,"83":1,"88":1,"104":1,"113":2,"119":1,"123":1,"129":1,"135":1,"141":1,"159":1,"164":2,"168":1,"170":2,"171":1,"174":2,"186":4,"190":1,"199":3,"200":1,"201":3,"202":3,"203":1,"211":2,"212":2,"216":1,"218":1,"220":1,"232":1,"244":2,"265":3,"376":2}}],["alerts",{"2":{"260":1}}],["alphabet",{"2":{"252":1,"253":1}}],["alphanumeric",{"2":{"171":1}}],["alas",{"2":{"220":1}}],["alongside",{"2":{"237":1,"246":1}}],["along",{"2":{"170":1}}],["alternative",{"2":{"187":2,"237":1,"268":1}}],["alternatively",{"2":{"16":1,"168":1,"176":1,"269":1}}],["although",{"2":{"65":1,"174":1,"272":1}}],["aliases",{"2":{"282":2}}],["alias",{"2":{"166":2,"168":1,"198":4,"218":1,"282":4,"326":1,"329":1,"369":1,"370":1,"442":1,"448":1,"449":1,"450":1,"451":1,"452":1,"465":1,"466":1,"467":1,"468":1,"469":1,"492":1,"498":1,"499":1,"500":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1,"575":1,"576":1,"577":1,"599":1,"601":1,"604":1,"655":1,"657":1}}],["alices",{"2":{"220":1}}],["alice",{"2":{"53":14,"61":3,"66":7,"71":3,"78":1,"92":3,"93":3,"94":3,"97":3,"113":2,"116":4,"128":20,"136":1,"164":16,"168":1,"171":2,"174":4,"187":2,"188":1,"189":1,"190":2,"191":3,"192":4,"193":2,"200":2,"201":6,"202":6,"205":60,"210":1,"211":2,"217":2,"219":1,"220":6,"221":2,"702":10,"703":10,"704":14,"705":8,"706":14,"708":10,"709":10,"712":10,"713":10,"714":12}}],["alignment",{"2":{"103":1}}],["align",{"2":{"21":1,"260":2}}],["already",{"2":{"29":1,"40":1,"78":1,"81":1,"88":1,"107":1,"116":2,"131":1,"146":1,"154":1,"167":1,"182":1,"183":1,"190":1,"220":1,"225":1,"229":1,"256":2,"716":1}}],["algorithms",{"2":{"134":1,"170":2,"260":1}}],["algorithm",{"0":{"299":1},"2":{"28":1,"217":2,"230":2,"238":8,"239":1,"548":1}}],["almost",{"2":{"16":1,"665":1,"716":1,"775":1}}],["also",{"2":{"14":1,"17":1,"18":3,"20":1,"21":1,"24":1,"25":1,"36":1,"41":1,"42":1,"49":1,"51":1,"62":1,"81":1,"88":2,"97":2,"100":1,"103":1,"110":1,"119":1,"129":1,"136":1,"137":1,"148":1,"151":1,"154":1,"166":2,"167":2,"168":2,"171":1,"174":1,"176":1,"178":1,"186":2,"187":1,"188":1,"190":1,"197":1,"199":6,"200":8,"201":11,"202":12,"203":6,"207":3,"208":1,"209":1,"211":1,"214":1,"216":2,"218":1,"220":2,"230":1,"231":1,"232":1,"233":2,"234":1,"235":1,"238":1,"241":1,"246":1,"249":1,"253":1,"255":1,"256":1,"259":1,"264":1,"273":2,"671":1,"732":1}}],["always",{"2":{"13":1,"41":1,"65":1,"126":1,"168":1,"190":2,"222":1,"243":1,"248":2,"253":1,"258":1,"261":1,"665":1,"716":1}}],["allaccountsignaturesand",{"2":{"574":1}}],["all$",{"2":{"168":1,"169":1,"170":1,"171":1}}],["allocation",{"2":{"218":1}}],["allocated",{"2":{"200":1,"699":1}}],["allocate",{"2":{"65":1}}],["alloc",{"2":{"104":5,"107":1,"108":1}}],["allowance",{"2":{"160":1}}],["allowall",{"2":{"62":1}}],["allowing",{"2":{"158":1}}],["allows",{"2":{"63":1,"69":1,"95":1,"141":1,"216":1,"241":1,"248":1,"255":1,"268":1,"685":1,"686":2,"687":1,"704":2,"705":2,"706":2,"708":2,"709":2,"710":2,"711":2,"712":2,"713":2,"714":2}}],["allow",{"2":{"39":1,"55":1,"60":1,"62":4,"137":3,"154":1,"212":1,"260":1,"668":1}}],["allowed",{"2":{"28":1,"62":1,"63":1,"82":1,"126":1,"137":1,"146":2,"230":1,"665":1}}],["all",{"2":{"5":2,"14":1,"16":1,"18":2,"21":1,"22":1,"25":1,"36":1,"41":1,"43":1,"62":8,"63":2,"65":2,"66":1,"67":1,"71":1,"72":1,"77":1,"79":1,"82":1,"83":1,"85":1,"86":1,"87":2,"88":3,"92":1,"97":2,"98":1,"100":2,"103":2,"104":2,"107":1,"110":1,"111":1,"112":2,"120":1,"125":2,"126":1,"132":1,"135":5,"136":1,"141":2,"147":1,"150":1,"166":1,"168":1,"169":1,"170":2,"171":1,"174":1,"183":1,"186":2,"187":1,"188":4,"189":1,"193":1,"195":1,"199":9,"200":7,"201":8,"202":6,"203":6,"204":1,"209":1,"210":2,"211":1,"216":1,"218":1,"219":1,"220":3,"222":2,"230":2,"232":1,"233":1,"238":1,"241":3,"243":1,"248":1,"249":1,"253":2,"256":1,"259":4,"260":2,"282":1,"283":1,"333":1,"665":2,"667":1,"669":1,"673":1,"696":2,"715":1,"717":1,"718":1,"719":1,"720":1,"721":1,"723":1,"724":1,"726":1,"729":1,"730":1,"731":1,"733":2,"734":1,"736":1,"737":1,"738":1,"739":1,"744":1,"746":1,"747":1,"750":1,"754":1,"755":1,"759":1,"760":1,"763":1,"767":1,"775":1}}],["apartment",{"2":{"261":1}}],["apt",{"2":{"182":2,"234":1,"282":1}}],["append",{"2":{"776":1}}],["appearance",{"2":{"673":1}}],["appears",{"2":{"265":2}}],["app",{"2":{"193":13,"259":1}}],["appointments",{"2":{"137":1}}],["applicable",{"2":{"137":1}}],["application",{"2":{"106":1,"193":2,"197":3,"217":2,"238":1,"241":1,"243":1,"249":1,"256":1,"265":1,"665":1,"775":4}}],["applications",{"0":{"166":1},"2":{"99":1,"238":1,"259":1,"671":1,"756":1}}],["applies",{"2":{"69":1,"137":1,"206":1,"256":1}}],["approved",{"2":{"260":1}}],["approval",{"2":{"255":1}}],["approximately",{"2":{"252":1,"253":1}}],["appropriate",{"2":{"20":1,"160":1,"186":1,"187":1,"256":1,"759":1}}],["appropriately",{"2":{"18":1}}],["approaches",{"2":{"19":1,"229":1,"233":1,"246":1,"247":1,"262":1}}],["approach",{"2":{"13":1,"65":1,"100":1,"137":2,"233":1,"238":1,"247":1,"248":1,"249":1,"253":1,"262":1,"263":1,"268":1}}],["apis",{"2":{"231":1,"268":1}}],["api`",{"2":{"193":2}}],["apiurl",{"2":{"187":7,"193":4}}],["api",{"0":{"119":1,"148":1,"764":1,"766":1},"2":{"12":1,"18":9,"20":2,"21":1,"94":1,"113":2,"118":1,"119":4,"143":3,"146":1,"147":1,"148":2,"161":1,"164":4,"168":2,"170":2,"174":2,"186":1,"187":3,"188":1,"193":2,"197":2,"198":6,"204":1,"205":4,"208":1,"217":4,"222":2,"268":1,"287":2,"665":1,"667":1,"686":1,"754":1,"764":1,"766":2}}],["average",{"2":{"771":2}}],["availability",{"2":{"65":1,"247":1}}],["available",{"0":{"244":1},"2":{"11":1,"13":2,"40":1,"65":2,"97":1,"120":1,"124":2,"125":1,"134":1,"147":1,"150":1,"153":1,"155":1,"169":1,"170":2,"171":1,"178":1,"182":1,"187":4,"193":1,"204":1,"216":1,"222":1,"238":1,"243":1,"244":2,"249":1,"262":1,"282":1,"665":1,"672":1,"716":2}}],["avoided",{"2":{"273":1}}],["avoiding",{"2":{"246":1}}],["avoid",{"2":{"6":1,"148":1,"187":3,"231":1,"249":4,"253":2,"256":1,"259":2,"261":1,"667":1}}],["asian",{"2":{"236":1}}],["aside",{"2":{"151":1,"673":1}}],["asassetid",{"2":{"201":2,"202":6,"203":2}}],["asassetdefinitionid",{"2":{"201":2}}],["asaccountid",{"2":{"200":2,"202":4,"203":2}}],["asdomainid",{"2":{"199":4}}],["asname",{"2":{"199":2}}],["async",{"2":{"188":3,"193":6,"216":1}}],["asynchronous",{"2":{"186":1,"199":1,"212":1}}],["ask",{"2":{"161":1,"189":1,"210":1,"219":1,"253":1}}],["asks",{"2":{"141":1}}],["asked",{"2":{"20":1,"21":1}}],["ascii",{"2":{"134":1,"273":1}}],["aspects",{"2":{"246":1,"250":1}}],["aspect",{"2":{"28":1}}],["assistant",{"2":{"263":1}}],["assigned",{"2":{"170":1}}],["asstring",{"2":{"199":2,"200":2,"201":2}}],["associated",{"2":{"57":1,"85":1,"86":1,"248":1}}],["associative",{"2":{"50":1}}],["assessment",{"2":{"260":1}}],["assessments",{"2":{"260":1}}],["assembly",{"2":{"100":1,"186":1,"233":1}}],["assertequals",{"2":{"205":10}}],["assertthrows",{"2":{"205":2}}],["assertions",{"2":{"205":14}}],["assertion",{"2":{"187":3}}],["assert",{"2":{"53":2,"219":2}}],["assetchanged",{"0":{"307":1},"2":{"315":2}}],["assetevent",{"0":{"315":1,"390":1,"525":1},"2":{"290":1,"317":1,"339":1,"390":1}}],["asseteventfilter",{"0":{"316":1,"383":1},"2":{"94":2,"317":1,"383":1}}],["assetkey",{"2":{"205":6}}],["assetmetadatavalue2",{"2":{"205":6}}],["assetmetadatavalue",{"2":{"205":4}}],["assetmetadatakey",{"2":{"205":8}}],["assetvalue",{"0":{"319":1},"2":{"199":2,"201":4,"202":4,"205":12,"306":1,"307":1,"741":1}}],["assetvaluetype",{"0":{"320":1,"488":1},"2":{"25":1,"190":4,"199":2,"201":6,"205":8,"220":1,"308":1,"488":2,"494":1,"626":2}}],["asset=",{"2":{"171":2,"173":2}}],["assetfilter",{"0":{"317":1,"384":1},"2":{"94":3,"291":1,"338":1,"384":1}}],["assetid",{"0":{"318":1,"350":1,"481":1,"591":1},"2":{"52":2,"92":16,"93":8,"128":4,"164":4,"190":6,"191":14,"202":4,"203":4,"205":12,"220":8,"221":4,"289":1,"306":1,"307":1,"315":3,"418":1,"421":1,"422":1,"431":1,"454":2,"481":1,"525":1,"591":1,"704":2,"706":2,"708":2,"709":2,"712":2,"713":2,"735":1,"741":1,"742":1,"774":1}}],["assetdefinitionfilter",{"0":{"311":1,"382":1},"2":{"338":1,"343":1,"382":1}}],["assetdefinitiontotalquantitychanged",{"0":{"314":1},"2":{"309":1}}],["assetdefinitionownerchanged",{"0":{"313":1},"2":{"309":1}}],["assetdefinitioneventfilter",{"0":{"310":1,"381":1},"2":{"311":1,"381":1}}],["assetdefinitionevent",{"0":{"309":1,"389":1,"524":1},"2":{"311":1,"339":1,"342":1,"389":1}}],["assetdefinitionvaluequery",{"2":{"205":8}}],["assetdefinition",{"0":{"308":1,"589":1},"2":{"52":2,"92":2,"190":6,"201":4,"220":4,"309":1,"339":1,"341":1,"342":1,"431":1,"455":2,"555":1,"589":1,"733":1,"734":1,"774":1}}],["assetdefinitionid",{"0":{"312":1,"349":1,"480":1,"487":1,"589":1,"590":1},"2":{"52":2,"92":2,"94":1,"190":8,"191":10,"220":2,"308":1,"309":4,"313":1,"314":1,"318":1,"341":2,"405":1,"419":1,"420":1,"424":1,"426":1,"431":1,"436":1,"454":2,"470":1,"480":1,"487":2,"494":1,"524":1,"589":1,"590":1,"626":2,"702":2,"703":2,"704":2,"705":2,"706":2,"708":2,"709":2,"712":2,"713":2,"738":1,"740":1,"743":1,"744":1,"774":1}}],["asset",{"0":{"26":1,"52":1,"306":1,"591":1,"672":1,"732":1,"774":1},"1":{"673":1,"674":1,"675":1,"676":1,"733":1,"734":1,"735":1,"736":1,"737":1,"738":1,"739":1,"740":1,"741":1,"742":1,"743":1,"744":1},"2":{"25":2,"29":8,"33":1,"41":5,"42":6,"43":1,"46":1,"50":3,"51":1,"52":4,"55":1,"57":4,"86":1,"92":1,"94":7,"97":1,"128":2,"137":5,"143":4,"164":4,"168":2,"169":2,"170":1,"171":13,"172":4,"173":2,"190":3,"191":1,"192":22,"199":6,"201":16,"202":27,"203":30,"205":26,"211":12,"220":17,"221":3,"289":1,"290":1,"307":1,"313":1,"314":1,"315":1,"339":1,"341":2,"405":1,"424":1,"426":1,"431":1,"455":2,"470":1,"555":2,"591":1,"669":1,"672":1,"675":1,"676":1,"699":1,"701":10,"702":1,"703":1,"704":5,"705":5,"706":5,"708":5,"709":5,"712":5,"713":5,"731":1,"733":3,"734":5,"735":2,"736":1,"737":1,"738":1,"739":1,"740":1,"741":2,"742":1,"743":1,"744":5,"774":3}}],["assets`",{"2":{"192":2}}],["assets",{"0":{"24":1,"137":1,"171":1,"172":1,"173":1,"190":1,"191":1,"192":1,"201":1,"202":1,"203":1,"211":1,"220":1,"221":1,"673":1,"674":1,"675":1,"676":1},"1":{"25":1,"26":1,"27":1},"2":{"24":3,"25":1,"27":2,"29":3,"33":1,"40":2,"41":7,"43":6,"44":4,"50":1,"51":1,"55":1,"56":1,"57":1,"66":1,"80":1,"86":1,"129":2,"130":3,"137":3,"168":2,"170":6,"171":2,"172":1,"173":1,"178":2,"190":1,"191":1,"192":8,"201":9,"202":6,"203":7,"211":1,"220":2,"221":1,"225":1,"227":2,"237":1,"247":1,"248":1,"289":1,"672":1,"673":3,"674":2,"692":1,"698":2,"699":1,"702":5,"703":5,"705":1,"707":1,"732":1,"733":1,"736":1,"737":1,"738":1,"739":1,"740":1,"749":1}}],["assuming",{"2":{"253":1}}],["assumptions",{"2":{"16":1,"24":1,"190":1,"201":1,"211":1,"220":1,"273":1}}],["assumed",{"2":{"43":1,"56":1,"100":1,"139":1,"722":1}}],["assumes",{"2":{"16":1,"185":1,"741":1}}],["assume",{"2":{"13":1,"137":1,"187":1,"197":3,"216":2,"272":1,"273":2}}],["as",{"2":{"5":3,"6":1,"8":1,"14":1,"16":2,"18":1,"24":2,"25":1,"28":1,"36":2,"39":1,"42":1,"43":2,"45":1,"50":1,"53":2,"56":1,"57":1,"62":1,"65":1,"67":3,"69":1,"72":1,"78":1,"81":1,"82":2,"86":1,"88":1,"90":4,"92":1,"94":1,"97":4,"99":1,"101":8,"103":1,"104":3,"105":2,"106":2,"107":1,"110":1,"112":2,"116":1,"119":1,"124":1,"125":4,"126":1,"127":2,"128":4,"129":5,"131":1,"134":1,"137":3,"138":1,"141":4,"143":1,"151":1,"160":3,"161":1,"166":3,"167":1,"168":3,"170":3,"171":1,"174":1,"178":2,"186":4,"187":15,"188":8,"189":1,"190":1,"192":24,"193":8,"195":3,"200":1,"209":1,"210":1,"211":2,"216":3,"218":1,"219":1,"220":4,"227":1,"228":2,"229":2,"231":4,"232":2,"234":2,"236":1,"238":5,"241":2,"243":3,"245":1,"247":3,"248":1,"249":2,"250":4,"252":3,"253":2,"254":1,"255":3,"256":3,"259":5,"261":2,"263":2,"264":2,"266":2,"269":2,"271":1,"273":1,"278":1,"281":1,"286":2,"287":1,"665":1,"666":3,"667":3,"670":1,"671":1,"673":1,"680":2,"690":1,"691":2,"693":2,"696":2,"716":3,"718":2,"719":2,"732":1,"733":2,"749":1,"750":1,"753":1,"766":1,"767":1,"770":1,"775":1}}],["a",{"0":{"6":1,"19":1,"60":1,"61":1,"66":1,"73":1,"93":1,"95":1,"96":1,"103":1,"104":1,"107":1,"133":1,"136":1,"161":1,"166":1,"169":1,"188":1,"209":1,"218":1,"264":1,"268":1,"269":1,"272":1},"1":{"7":1,"8":1,"9":1,"20":1,"21":1,"74":1,"75":1,"76":1,"77":1,"78":1,"79":1,"80":1,"134":1,"135":1,"265":1,"266":1},"2":{"2":1,"5":4,"6":2,"7":1,"10":2,"11":1,"13":1,"14":2,"16":5,"17":1,"18":5,"19":1,"22":2,"24":2,"25":2,"27":1,"28":11,"29":7,"31":2,"32":2,"33":1,"36":2,"38":6,"39":9,"40":4,"41":4,"42":15,"43":5,"45":4,"46":1,"49":5,"50":2,"51":1,"52":2,"53":1,"56":11,"57":5,"59":3,"60":1,"62":7,"65":4,"66":3,"67":3,"68":2,"69":3,"71":6,"72":9,"73":1,"74":5,"76":2,"77":1,"78":3,"79":2,"80":1,"81":3,"82":1,"83":3,"84":2,"85":2,"87":2,"88":4,"89":4,"90":1,"92":2,"93":3,"94":12,"95":1,"96":1,"97":9,"98":4,"99":1,"100":4,"101":5,"103":5,"104":2,"105":3,"106":6,"107":1,"110":1,"115":1,"117":2,"120":4,"121":1,"124":2,"125":11,"126":4,"127":3,"128":9,"130":3,"134":3,"135":2,"136":4,"137":18,"138":2,"139":2,"140":2,"141":13,"143":3,"145":1,"146":4,"150":4,"151":2,"153":2,"154":6,"155":1,"156":1,"157":2,"158":2,"159":1,"160":8,"161":3,"162":2,"163":3,"166":20,"167":3,"168":7,"169":3,"170":17,"174":2,"176":2,"177":2,"178":1,"180":1,"183":2,"186":4,"187":14,"188":7,"189":12,"190":3,"193":6,"194":1,"195":2,"197":3,"198":2,"199":6,"200":11,"201":6,"202":2,"203":1,"204":4,"207":2,"208":3,"209":3,"210":7,"211":7,"212":6,"213":1,"214":2,"216":12,"217":5,"218":18,"219":17,"220":13,"222":7,"224":1,"225":1,"226":3,"227":2,"228":2,"229":2,"230":6,"231":2,"232":2,"233":1,"234":4,"236":1,"237":1,"238":21,"239":4,"241":3,"243":1,"245":2,"246":4,"247":4,"248":6,"249":18,"250":10,"252":14,"253":12,"254":6,"255":3,"256":14,"258":7,"259":6,"260":4,"261":1,"263":8,"264":2,"265":4,"266":4,"268":12,"269":3,"270":5,"272":4,"273":5,"278":3,"280":2,"281":2,"282":2,"283":2,"286":3,"287":1,"664":2,"665":8,"666":7,"667":8,"671":8,"672":1,"674":2,"677":2,"683":2,"684":1,"686":1,"687":1,"689":1,"692":1,"693":1,"694":1,"695":3,"697":1,"698":2,"699":2,"702":3,"703":3,"704":3,"705":3,"706":3,"707":4,"708":3,"709":3,"710":4,"711":4,"712":3,"713":3,"714":3,"716":6,"717":1,"722":4,"730":1,"732":2,"737":1,"749":2,"753":2,"755":8,"759":1,"764":1,"766":2,"767":2,"768":1,"770":2,"771":2,"772":4,"776":1}}],["angle",{"2":{"282":1}}],["anomalies",{"2":{"260":1}}],["another",{"2":{"28":1,"38":1,"41":1,"42":1,"53":1,"60":1,"94":1,"100":1,"107":1,"137":3,"141":1,"154":1,"168":1,"170":1,"187":2,"188":1,"191":1,"202":1,"220":1,"249":1,"250":2,"256":1,"281":1,"710":2,"711":2}}],["answer",{"2":{"252":1,"268":1}}],["anticipating",{"2":{"250":1}}],["anticipate",{"2":{"88":1}}],["annotate",{"2":{"220":1}}],["annotations",{"2":{"211":1}}],["analysis",{"2":{"231":1,"247":1}}],["analyze",{"2":{"137":1}}],["anatomy",{"0":{"73":1},"1":{"74":1,"75":1,"76":1,"77":1,"78":1,"79":1,"80":1}}],["anyaccountsignatureor",{"2":{"574":1}}],["anyway",{"2":{"665":1}}],["anyways",{"2":{"105":2}}],["anywhere",{"2":{"166":1,"218":1,"253":1}}],["anything",{"2":{"56":1,"101":3,"139":1,"209":1,"212":1,"222":1,"722":1}}],["anyone",{"2":{"42":1,"141":1,"255":1,"256":1}}],["any",{"2":{"5":1,"6":2,"16":1,"18":1,"28":1,"39":1,"41":2,"42":1,"88":1,"94":1,"97":3,"101":1,"102":1,"108":1,"125":1,"135":1,"136":1,"137":1,"140":1,"141":2,"153":2,"156":1,"166":1,"170":2,"186":2,"187":1,"188":1,"190":2,"198":1,"208":1,"211":4,"216":1,"218":2,"224":1,"230":1,"234":1,"238":1,"241":1,"245":1,"249":1,"250":1,"256":1,"260":2,"264":2,"333":1,"665":1,"755":1}}],["an",{"0":{"94":1,"170":1,"189":1,"200":1,"210":1,"219":1},"2":{"2":1,"6":2,"16":1,"17":1,"18":1,"25":2,"28":1,"29":10,"32":2,"36":2,"38":2,"39":2,"40":1,"41":1,"42":14,"44":2,"52":2,"54":1,"59":1,"62":2,"63":2,"65":2,"66":1,"69":1,"71":1,"72":5,"75":1,"79":1,"85":1,"86":2,"91":1,"94":12,"95":2,"97":4,"106":1,"108":1,"109":1,"110":1,"125":1,"126":4,"127":2,"128":1,"135":1,"137":1,"138":1,"141":5,"143":1,"146":2,"153":2,"160":1,"166":2,"168":2,"169":1,"170":2,"174":1,"178":1,"180":1,"186":1,"187":4,"188":3,"189":7,"190":2,"195":2,"197":1,"199":1,"200":6,"204":1,"207":1,"208":2,"210":4,"211":3,"212":2,"217":3,"218":7,"219":13,"220":8,"222":2,"224":1,"225":1,"230":1,"232":1,"233":1,"234":1,"238":3,"245":1,"246":1,"248":2,"249":5,"250":2,"252":1,"253":2,"255":1,"256":2,"259":4,"260":2,"263":1,"264":2,"268":1,"272":3,"273":4,"282":1,"283":1,"287":3,"665":1,"666":2,"667":1,"671":2,"672":1,"673":1,"675":1,"676":1,"685":2,"693":1,"697":1,"698":1,"704":2,"705":2,"706":2,"708":2,"709":2,"712":2,"713":2,"714":2,"717":1,"734":2,"749":1,"757":1,"759":1,"768":2,"772":4,"775":2}}],["android",{"2":{"259":1}}],["and",{"0":{"115":1,"122":1,"137":1,"138":1,"142":1,"145":1,"171":1,"190":1,"192":1,"199":1,"201":1,"211":1,"220":1,"234":1,"255":1,"263":1,"273":1,"300":1,"682":2},"1":{"139":1,"140":1,"141":1},"2":{"0":2,"2":2,"3":2,"5":2,"6":2,"10":3,"11":3,"13":4,"14":4,"16":6,"18":4,"19":1,"20":3,"21":4,"22":4,"24":1,"25":1,"27":2,"28":7,"29":5,"32":1,"36":2,"39":6,"40":2,"41":1,"42":6,"43":4,"44":1,"45":3,"47":1,"52":1,"53":3,"56":2,"57":2,"59":1,"62":3,"63":1,"64":1,"65":2,"67":2,"69":1,"72":2,"74":1,"78":1,"79":1,"81":1,"82":3,"85":1,"87":2,"88":3,"90":2,"91":2,"94":4,"97":10,"98":3,"99":1,"100":5,"101":7,"103":5,"104":4,"106":2,"107":1,"108":1,"110":1,"115":1,"116":1,"117":3,"118":2,"119":1,"121":1,"122":1,"123":1,"124":2,"125":5,"126":2,"127":3,"128":5,"129":1,"130":3,"132":1,"134":2,"135":2,"137":18,"138":1,"141":4,"143":5,"145":1,"146":4,"147":2,"151":3,"152":1,"157":1,"160":1,"161":1,"162":2,"166":6,"168":5,"170":11,"171":2,"176":1,"178":17,"180":5,"183":3,"185":2,"186":2,"187":12,"188":2,"189":4,"190":3,"192":4,"193":2,"195":2,"197":6,"198":2,"199":1,"200":1,"201":2,"202":2,"204":4,"206":1,"207":2,"208":2,"209":2,"210":3,"211":4,"212":6,"216":7,"217":2,"218":4,"219":8,"220":14,"221":1,"222":4,"224":2,"225":4,"226":1,"227":5,"229":7,"230":2,"231":6,"232":2,"233":1,"234":3,"235":1,"236":2,"237":2,"238":13,"241":2,"243":1,"244":2,"245":2,"246":6,"247":11,"248":5,"249":17,"250":13,"251":1,"252":5,"253":5,"254":2,"255":4,"256":6,"257":1,"258":6,"259":8,"260":19,"261":2,"262":1,"263":6,"264":2,"265":1,"266":3,"268":4,"270":3,"272":4,"273":3,"279":3,"280":3,"282":3,"283":3,"287":1,"376":2,"445":1,"664":2,"665":5,"667":4,"668":1,"669":2,"670":2,"671":3,"673":2,"674":3,"676":1,"680":2,"683":1,"687":1,"690":1,"692":1,"693":1,"697":1,"699":1,"700":1,"702":1,"703":1,"715":2,"716":6,"722":1,"730":1,"749":2,"750":1,"753":2,"754":1,"755":1,"756":1,"766":1,"767":1,"770":1,"772":1,"773":1,"774":1,"775":2,"777":1}}],["atindex",{"0":{"321":1},"2":{"333":2}}],["atleastoneallow",{"2":{"62":1}}],["attempt",{"2":{"190":1,"211":1,"695":1}}],["attempts",{"2":{"151":1,"252":1,"253":1}}],["attention",{"2":{"46":1,"166":1,"190":1,"249":1}}],["attackers",{"2":{"253":1,"268":1}}],["attacker",{"2":{"248":1,"256":1}}],["attack",{"2":{"247":1,"249":1}}],["attacks",{"2":{"148":1,"238":1,"249":1,"253":1}}],["attachment",{"2":{"265":1}}],["attachments",{"2":{"265":1}}],["attaching",{"2":{"213":2}}],["attach",{"2":{"72":1,"265":1}}],["attached",{"2":{"50":1,"80":1,"97":1,"721":1,"728":1}}],["attribute",{"2":{"103":1}}],["at",{"2":{"2":3,"5":3,"11":1,"18":3,"20":1,"22":1,"37":1,"41":1,"50":1,"62":2,"63":1,"65":1,"72":1,"88":9,"94":1,"99":1,"105":1,"106":1,"110":1,"113":1,"128":1,"137":3,"158":1,"168":2,"186":1,"187":4,"189":1,"190":2,"192":2,"198":2,"210":1,"211":1,"212":2,"213":4,"217":2,"219":1,"228":3,"230":1,"232":1,"233":1,"253":1,"273":1,"664":1,"665":1,"671":1,"683":1,"685":1,"695":1,"715":1,"716":1,"725":1,"760":1,"766":2,"771":2}}],["s^l=2^entropy",{"2":{"252":1}}],["s^l",{"2":{"252":1}}],["ssh",{"0":{"263":2},"2":{"262":3,"263":21,"264":4,"265":3,"266":5,"268":1}}],["sss",{"2":{"252":1}}],["ssf",{"2":{"176":2}}],["sms",{"2":{"247":1,"259":2}}],["smallstr",{"2":{"217":2}}],["smallest",{"2":{"110":1}}],["smaller",{"2":{"14":1,"88":2,"90":1,"234":1}}],["small",{"2":{"65":1,"88":1,"154":1,"193":1,"194":1,"224":1,"238":2,"273":1}}],["smartly",{"2":{"234":1}}],["smartcontracts",{"2":{"664":1}}],["smartcontract",{"2":{"103":2,"104":2,"137":1,"665":1}}],["smart",{"0":{"102":1,"104":1,"105":1,"233":1,"684":1},"1":{"103":1,"104":1,"106":1,"107":1,"108":1,"109":1,"110":1},"2":{"36":1,"79":1,"88":1,"90":2,"93":1,"97":1,"100":1,"104":2,"105":1,"106":1,"110":1,"141":1,"217":1,"227":1,"233":3,"669":1,"684":2,"689":1}}],["swapped",{"2":{"673":1}}],["swarm",{"2":{"214":1}}],["swift",{"2":{"287":2}}],["switch",{"2":{"170":1,"193":2}}],["snip",{"2":{"187":4,"188":2}}],["snippet",{"0":{"2":1},"2":{"2":3,"3":1,"5":6,"170":1,"204":1}}],["snippets",{"0":{"0":1,"3":1,"4":1},"1":{"1":1,"2":1,"3":1,"4":1,"5":1},"2":{"0":1,"3":7,"4":6,"5":2,"178":1}}],["snapshot",{"2":{"143":2,"164":2,"197":12}}],["sl=2entropy",{"2":{"252":2}}],["sl",{"2":{"252":2}}],["sls^lsl",{"2":{"252":1}}],["sledgehammer",{"2":{"230":1}}],["slf4jlogconsumer",{"2":{"198":4}}],["slf4j",{"2":{"198":2}}],["slow",{"2":{"137":1}}],["slower",{"2":{"97":1}}],["slightly",{"2":{"87":1,"88":1,"207":1,"256":1}}],["sys",{"2":{"211":2}}],["systematic",{"2":{"247":1}}],["system",{"0":{"687":1},"1":{"690":1},"2":{"16":2,"17":1,"67":1,"82":2,"137":1,"176":1,"183":2,"197":2,"199":2,"200":2,"201":2,"202":2,"207":1,"232":1,"241":2,"260":4,"263":7,"266":2,"278":1,"282":1,"283":1,"669":1,"671":1,"687":1,"755":2}}],["systems",{"2":{"13":1,"16":2,"20":1,"238":1,"242":2,"263":1,"264":1,"670":2}}],["syntactic",{"2":{"218":1}}],["syntax",{"2":{"100":2,"211":1}}],["synthetic",{"2":{"130":3}}],["synchronize",{"2":{"272":1,"769":1}}],["synchronized",{"2":{"270":1}}],["synchronous",{"2":{"218":1,"222":1}}],["sync",{"2":{"127":1,"143":2,"164":2,"222":1,"226":1}}],["symbols",{"2":{"252":4,"253":1,"273":1,"287":1}}],["symbol",{"2":{"83":1,"220":1,"273":1}}],["scissors",{"2":{"256":1}}],["scenario",{"2":{"253":1}}],["scenarios",{"2":{"88":1,"250":1,"260":1}}],["scss",{"2":{"193":2}}],["scale",{"2":{"94":1,"103":1,"137":1,"186":1,"231":1,"767":1,"768":1,"772":1,"775":4,"777":2}}],["scan",{"2":{"82":2}}],["schedule",{"0":{"565":1},"2":{"87":1,"180":1,"375":2}}],["scheduled",{"0":{"88":1},"2":{"85":1,"87":2,"88":2,"97":1}}],["scheme",{"2":{"141":1,"238":2}}],["schemes",{"2":{"77":1}}],["schema",{"0":{"84":1,"288":1},"1":{"289":1,"290":1,"291":1,"292":1,"293":1,"294":1,"295":1,"296":1,"297":1,"298":1,"299":1,"300":1,"301":1,"302":1,"303":1,"304":1,"305":1,"306":1,"307":1,"308":1,"309":1,"310":1,"311":1,"312":1,"313":1,"314":1,"315":1,"316":1,"317":1,"318":1,"319":1,"320":1,"321":1,"322":1,"323":1,"324":1,"325":1,"326":1,"327":1,"328":1,"329":1,"330":1,"331":1,"332":1,"333":1,"334":1,"335":1,"336":1,"337":1,"338":1,"339":1,"340":1,"341":1,"342":1,"343":1,"344":1,"345":1,"346":1,"347":1,"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"367":1,"368":1,"369":1,"370":1,"371":1,"372":1,"373":1,"374":1,"375":1,"376":1,"377":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"401":1,"402":1,"403":1,"404":1,"405":1,"406":1,"407":1,"408":1,"409":1,"410":1,"411":1,"412":1,"413":1,"414":1,"415":1,"416":1,"417":1,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":1,"425":1,"426":1,"427":1,"428":1,"429":1,"430":1,"431":1,"432":1,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":1,"441":1,"442":1,"443":1,"444":1,"445":1,"446":1,"447":1,"448":1,"449":1,"450":1,"451":1,"452":1,"453":1,"454":1,"455":1,"456":1,"457":1,"458":1,"459":1,"460":1,"461":1,"462":1,"463":1,"464":1,"465":1,"466":1,"467":1,"468":1,"469":1,"470":1,"471":1,"472":1,"473":1,"474":1,"475":1,"476":1,"477":1,"478":1,"479":1,"480":1,"481":1,"482":1,"483":1,"484":1,"485":1,"486":1,"487":1,"488":1,"489":1,"490":1,"491":1,"492":1,"493":1,"494":1,"495":1,"496":1,"497":1,"498":1,"499":1,"500":1,"501":1,"502":1,"503":1,"504":1,"505":1,"506":1,"507":1,"508":1,"509":1,"510":1,"511":1,"512":1,"513":1,"514":1,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1,"522":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1,"530":1,"531":1,"532":1,"533":1,"534":1,"535":1,"536":1,"537":1,"538":1,"539":1,"540":1,"541":1,"542":1,"543":1,"544":1,"545":1,"546":1,"547":1,"548":1,"549":1,"550":1,"551":1,"552":1,"553":1,"554":1,"555":1,"556":1,"557":1,"558":1,"559":1,"560":1,"561":1,"562":1,"563":1,"564":1,"565":1,"566":1,"567":1,"568":1,"569":1,"570":1,"571":1,"572":1,"573":1,"574":1,"575":1,"576":1,"577":1,"578":1,"579":1,"580":1,"581":1,"582":1,"583":1,"584":1,"585":1,"586":1,"587":1,"588":1,"589":1,"590":1,"591":1,"592":1,"593":1,"594":1,"595":1,"596":1,"597":1,"598":1,"599":1,"600":1,"601":1,"602":1,"603":1,"604":1,"605":1,"606":1,"607":1,"608":1,"609":1,"610":1,"611":1,"612":1,"613":1,"614":1,"615":1,"616":1,"617":1,"618":1,"619":1,"620":1,"621":1,"622":1,"623":1,"624":1,"625":1,"626":1,"627":1,"628":1,"629":1,"630":1,"631":1,"632":1,"633":1,"634":1,"635":1,"636":1,"637":1,"638":1,"639":1,"640":1,"641":1,"642":1,"643":1,"644":1,"645":1,"646":1,"647":1,"648":1,"649":1,"650":1,"651":1,"652":1,"653":1,"654":1,"655":1,"656":1,"657":1,"658":1,"659":1,"660":1,"661":1,"662":1,"663":1},"2":{"81":1,"137":1,"540":1,"541":2}}],["scoped",{"0":{"83":1},"2":{"74":1,"82":7,"83":4,"718":1,"719":1,"749":1}}],["scope",{"0":{"82":1},"1":{"83":1},"2":{"65":1,"81":1,"186":1,"212":1}}],["scrutinizes",{"2":{"260":1}}],["scripts",{"2":{"186":1,"224":1}}],["script",{"2":{"176":2,"193":16,"208":1,"244":2}}],["scripting",{"2":{"5":2,"166":2,"208":1,"233":1}}],["screenshots",{"2":{"265":1}}],["screenshot",{"2":{"265":1}}],["screen",{"2":{"168":1,"248":1}}],["scratch",{"2":{"28":1}}],["split",{"2":{"135":1,"248":1}}],["spec",{"2":{"764":1}}],["specialists",{"2":{"249":1}}],["specialised",{"2":{"218":1}}],["special",{"0":{"40":1,"689":1,"690":1,"691":1,"692":1,"693":1,"698":1},"1":{"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"690":1,"691":1,"692":1,"693":1},"2":{"16":1,"28":2,"36":1,"39":1,"40":4,"52":2,"56":1,"72":1,"89":1,"98":2,"127":1,"178":1,"188":1,"190":1,"224":1,"227":1,"233":1,"252":2,"253":1,"669":5,"684":1,"685":1,"691":1,"693":1,"697":1,"698":1,"732":1}}],["specifying",{"2":{"16":1}}],["specify",{"2":{"16":2,"24":1,"36":2,"41":1,"120":1,"121":1,"146":1,"150":1,"170":1,"220":1,"238":1,"256":1,"772":1}}],["specifies",{"2":{"155":1,"170":2,"238":5}}],["specified",{"2":{"3":1,"16":1,"18":1,"41":1,"49":1,"57":1,"68":1,"71":1,"83":1,"128":1,"130":1,"131":1,"132":1,"134":1,"135":2,"166":1,"193":1,"204":1,"211":1,"238":4,"676":1,"704":1,"706":1,"708":1,"709":1,"710":1,"711":1,"724":1,"744":1,"772":1}}],["specifically",{"2":{"25":1,"42":1,"94":1,"98":1,"197":2,"216":2,"220":1,"247":1,"248":1,"259":1,"264":1,"753":1}}],["specification",{"0":{"764":1},"2":{"12":1,"166":1,"217":1,"775":1}}],["specific",{"0":{"692":1},"2":{"18":2,"27":1,"29":2,"36":1,"40":1,"42":1,"43":2,"65":1,"97":1,"100":1,"103":1,"137":4,"141":2,"148":1,"154":1,"156":1,"178":2,"186":1,"195":1,"216":1,"217":1,"225":1,"233":1,"238":1,"247":1,"269":1,"281":1,"669":1,"674":1,"684":1,"685":1,"691":1,"692":1,"699":1,"730":1,"753":1,"756":1,"772":1,"776":1}}],["sped",{"2":{"253":1}}],["speed",{"2":{"105":2,"238":1,"253":2}}],["spend",{"2":{"94":1,"160":1}}],["spin",{"2":{"91":1}}],["sparingly",{"2":{"750":1}}],["spaces",{"2":{"171":1}}],["space",{"2":{"90":1,"97":1,"100":1,"106":2,"699":1}}],["spades",{"2":{"24":1}}],["spoke",{"2":{"40":1}}],["saw",{"2":{"282":1}}],["sake",{"2":{"264":1}}],["safari",{"2":{"249":1}}],["safely",{"2":{"667":1}}],["safe",{"2":{"254":1,"258":1,"272":1,"692":1}}],["safeguards",{"2":{"258":1}}],["safeguard",{"2":{"237":1,"258":1,"259":1}}],["safety",{"2":{"97":1,"158":1,"178":1,"268":1}}],["sadness",{"2":{"222":2}}],["sans",{"2":{"193":2}}],["sanity",{"2":{"21":1,"279":1}}],["saving",{"2":{"154":1}}],["saved",{"2":{"154":1,"256":1,"258":1}}],["save",{"2":{"16":1,"249":1,"265":1}}],["saying",{"2":{"116":1,"128":1}}],["say",{"2":{"88":1,"99":1,"137":2,"222":1,"280":1}}],["said",{"2":{"40":1,"56":1,"81":1,"96":1,"128":1,"137":1,"139":1,"217":1,"252":1,"256":1,"282":1,"665":1,"694":1,"722":1}}],["samples",{"0":{"205":1},"2":{"775":1}}],["sample",{"0":{"164":1},"2":{"13":1,"125":3,"164":1,"187":1,"771":1,"775":1}}],["same",{"2":{"6":1,"16":1,"17":1,"20":1,"21":1,"24":2,"28":3,"56":1,"62":1,"72":1,"80":1,"84":1,"94":2,"104":1,"116":1,"119":1,"126":1,"128":1,"131":1,"135":2,"139":1,"166":1,"170":1,"171":1,"186":1,"189":1,"207":1,"238":2,"239":2,"249":1,"256":1,"671":1,"673":2,"674":1,"675":1,"722":1,"733":1,"775":1}}],["shut",{"2":{"759":1}}],["shh",{"2":{"266":1}}],["shielding",{"2":{"248":1}}],["ships",{"2":{"248":1}}],["shipped",{"2":{"237":1,"246":1}}],["shift",{"2":{"20":1}}],["sh$",{"2":{"176":1}}],["sh",{"2":{"176":3}}],["she",{"2":{"92":1,"168":1}}],["shell",{"0":{"244":1},"2":{"16":2,"166":3,"168":1,"186":1,"244":6,"262":1,"263":2,"282":2,"283":1}}],["sharing",{"2":{"259":1,"261":1,"668":1}}],["shared",{"2":{"667":1}}],["share",{"2":{"92":1,"254":1,"256":1,"259":1}}],["shacham",{"2":{"238":2}}],["shallowref",{"2":{"193":4}}],["shallowreactive",{"2":{"193":4}}],["shall",{"2":{"16":1,"46":1,"81":1,"85":1,"90":1,"110":1,"127":1,"197":3,"199":1,"204":1,"216":2,"218":2,"222":3,"224":1,"237":1,"664":1}}],["shortened",{"2":{"620":1}}],["shorthand",{"2":{"218":1}}],["short",{"2":{"197":1,"204":1,"222":1}}],["shortcut",{"2":{"20":1}}],["show",{"2":{"78":1,"265":2,"279":1,"667":1}}],["shown",{"2":{"65":1}}],["showcases",{"2":{"53":1}}],["shows",{"2":{"29":1,"88":1,"91":1,"193":1}}],["shouldclosenetwork",{"2":{"198":2}}],["shouldn",{"2":{"106":1,"168":1,"665":1}}],["should",{"2":{"14":2,"16":1,"18":2,"20":2,"21":4,"42":1,"45":1,"59":1,"62":1,"72":1,"84":1,"88":1,"97":1,"103":1,"104":1,"106":1,"110":1,"115":1,"116":3,"119":1,"125":1,"126":2,"127":1,"128":3,"130":1,"135":2,"136":2,"137":2,"141":1,"145":1,"146":1,"149":1,"159":1,"162":1,"166":2,"168":3,"169":1,"170":1,"188":1,"189":2,"190":1,"193":1,"197":1,"202":1,"204":1,"207":2,"208":1,"210":2,"212":1,"219":2,"222":1,"224":1,"229":1,"242":1,"247":1,"250":1,"252":1,"272":1,"280":3,"286":1,"665":1,"733":1,"750":1,"766":1,"767":1,"768":1}}],["suits",{"2":{"268":1}}],["suitable",{"2":{"14":1,"141":2,"224":1,"238":1}}],["suspicious",{"2":{"259":1,"260":1}}],["suspend",{"2":{"199":4,"200":2,"201":3,"202":2,"203":1}}],["susceptible",{"2":{"249":1}}],["surpasses",{"2":{"268":1}}],["surpassing",{"2":{"253":1}}],["survive",{"2":{"230":1}}],["sure",{"2":{"16":1,"20":1,"21":1,"22":2,"104":1,"135":1,"167":1,"168":4,"182":1,"190":1,"199":1,"211":2,"247":1,"249":1,"271":1,"272":1,"279":1,"282":1,"283":1}}],["sugar",{"2":{"218":1}}],["suggestion",{"2":{"270":1}}],["suggestions",{"2":{"260":1}}],["suggested",{"2":{"137":1}}],["suggests",{"2":{"106":1}}],["suggest",{"2":{"16":1,"178":1,"189":1,"210":1,"218":1,"219":1}}],["sufficient",{"2":{"186":1,"224":1,"247":1}}],["suffice",{"2":{"99":1}}],["supplied",{"2":{"241":1}}],["supplementary",{"2":{"224":1}}],["supporting",{"2":{"241":2,"246":1}}],["support",{"0":{"270":1},"2":{"180":2,"183":1,"216":2,"233":1,"273":1}}],["supports",{"2":{"82":1,"89":1,"204":1,"233":1}}],["supported",{"0":{"64":1,"98":1,"99":1},"2":{"62":1,"99":1,"180":1,"283":1,"698":1}}],["supposed",{"2":{"88":4,"137":1,"253":1}}],["suppose",{"2":{"22":1,"88":1,"137":1,"256":1}}],["super",{"2":{"128":1,"190":1}}],["success",{"2":{"222":2,"618":1,"619":1,"772":1}}],["successfully",{"2":{"169":1}}],["successful",{"2":{"168":1,"170":1,"245":1}}],["succeeds",{"2":{"62":3}}],["such",{"2":{"8":1,"14":1,"36":1,"42":1,"45":1,"50":1,"62":1,"67":1,"72":1,"84":1,"86":1,"101":1,"107":1,"125":1,"126":1,"129":1,"137":2,"141":1,"151":1,"161":1,"166":1,"174":1,"178":2,"186":2,"201":1,"209":1,"220":2,"227":1,"236":1,"247":1,"248":1,"249":1,"250":2,"252":3,"253":1,"254":1,"259":1,"272":1,"286":1,"287":1,"667":1,"670":1,"673":1,"733":1}}],["sum",{"2":{"744":1,"771":2}}],["summarised",{"2":{"97":1}}],["summarise",{"2":{"42":1}}],["summary",{"0":{"41":1},"2":{"40":1,"238":1}}],["sumeragi",{"0":{"127":1,"679":1},"2":{"18":8,"127":3,"143":2,"146":2,"164":2,"230":2,"669":1}}],["subnet",{"2":{"766":1}}],["subject",{"2":{"701":1}}],["subtle",{"2":{"273":1}}],["subtopic",{"2":{"243":1}}],["subtract",{"0":{"602":1},"2":{"190":1,"376":2}}],["subcommands",{"2":{"168":2}}],["subcommand",{"2":{"168":20}}],["submission",{"2":{"151":2,"218":1}}],["submitgenesis",{"2":{"198":2}}],["submitexecutable",{"2":{"187":2,"188":2,"193":2}}],["submitting",{"2":{"162":1,"200":1,"204":1,"222":1}}],["submitted",{"2":{"32":1,"39":1,"97":1,"141":1,"151":1,"189":1,"204":2,"209":1,"212":1,"218":1,"219":1,"757":1,"766":1}}],["submits",{"2":{"126":1,"186":1,"220":1}}],["submit$",{"2":{"20":1}}],["submit",{"0":{"162":1},"2":{"18":2,"20":3,"21":4,"22":1,"36":1,"53":8,"67":1,"72":1,"104":1,"128":1,"162":1,"187":2,"188":1,"209":2,"210":3,"211":2,"218":13,"220":13,"221":3,"273":1}}],["sub",{"0":{"776":1},"2":{"65":1,"87":1}}],["subscription",{"2":{"195":2,"767":1}}],["subscribing",{"0":{"195":1}}],["subscribe",{"2":{"98":1}}],["subscriber",{"2":{"65":1}}],["substrate",{"2":{"177":1,"231":1,"777":1}}],["subsequent",{"2":{"28":1,"287":1}}],["subvolume",{"2":{"8":1}}],["sudo",{"2":{"17":2,"182":2,"234":1,"242":2}}],["skip",{"2":{"6":1,"62":1}}],["sticking",{"2":{"171":1}}],["still",{"2":{"11":1,"97":1,"101":1,"241":1,"716":1}}],["style",{"2":{"117":1,"193":4,"218":1}}],["steal",{"2":{"248":1}}],["stem",{"2":{"107":1}}],["steps",{"2":{"16":1,"110":1,"159":1,"168":1,"186":3,"250":2,"263":1,"265":2}}],["step",{"2":{"6":3,"97":1,"107":1,"167":2,"168":1,"187":2,"216":3,"247":1,"250":2,"283":1}}],["stdout",{"2":{"154":1}}],["std",{"0":{"107":1},"2":{"104":5,"107":2,"108":4}}],["stoplistening",{"2":{"193":6}}],["stopping",{"2":{"166":1,"256":1}}],["stopped",{"2":{"62":2,"166":1}}],["stop",{"2":{"89":1,"98":1,"193":4,"213":1,"230":1}}],["stops",{"2":{"63":1}}],["story",{"2":{"287":3}}],["stories",{"2":{"287":2}}],["storing",{"0":{"261":1,"262":1,"267":1},"1":{"262":1,"263":2,"264":2,"265":2,"266":2,"267":1,"268":2,"269":2},"2":{"25":1,"46":1,"137":1,"246":1,"262":1,"267":1,"268":2,"681":1}}],["storage",{"2":{"52":1,"80":1,"97":1,"106":1,"137":2,"143":6,"155":2,"164":6,"256":1,"264":2,"681":1,"732":1,"753":1}}],["stores",{"2":{"106":2,"180":1,"256":1,"264":1}}],["store",{"0":{"52":1,"137":1},"2":{"25":2,"46":1,"50":1,"52":5,"57":1,"97":1,"105":1,"137":8,"143":2,"155":1,"164":2,"166":1,"178":1,"201":2,"205":4,"217":1,"248":1,"249":1,"252":1,"253":2,"256":1,"264":1,"265":1,"319":1,"320":1,"734":1,"744":1}}],["stored",{"2":{"6":1,"7":1,"51":1,"106":2,"128":1,"155":1,"249":1,"250":1,"258":1,"263":1,"264":1,"266":3,"268":1,"670":1,"734":1}}],["stuff",{"2":{"36":1}}],["strucuname",{"2":{"668":1}}],["structname",{"2":{"668":9}}],["structs",{"2":{"667":1}}],["struct",{"2":{"73":1,"75":1,"289":1,"292":1,"293":1,"294":1,"295":1,"296":1,"297":1,"298":1,"300":1,"306":1,"307":1,"308":1,"311":1,"312":1,"313":1,"314":1,"317":1,"318":1,"321":1,"322":1,"323":1,"324":1,"325":1,"327":1,"330":1,"331":1,"334":1,"335":1,"336":1,"337":1,"340":1,"341":1,"344":1,"345":1,"347":1,"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"372":1,"373":1,"374":1,"377":1,"401":1,"402":1,"403":1,"404":1,"405":1,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":1,"425":1,"426":1,"427":1,"428":1,"429":1,"430":1,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":1,"441":1,"444":1,"446":1,"447":1,"456":1,"460":1,"462":1,"463":1,"470":1,"471":1,"472":1,"474":1,"475":1,"478":1,"479":1,"480":1,"481":1,"482":1,"484":1,"487":1,"488":1,"489":1,"490":1,"491":1,"493":1,"494":1,"495":1,"496":1,"497":1,"501":1,"522":1,"530":1,"531":1,"532":1,"533":1,"536":1,"537":1,"538":1,"539":1,"540":1,"541":1,"543":1,"544":1,"548":1,"551":1,"552":1,"553":1,"554":1,"556":1,"558":1,"559":1,"560":1,"563":1,"564":1,"565":1,"566":1,"567":1,"568":1,"570":1,"571":1,"572":1,"573":1,"578":1,"579":1,"580":1,"581":1,"582":1,"583":1,"585":1,"586":1,"587":1,"602":1,"603":1,"605":1,"606":1,"607":1,"608":1,"609":1,"611":1,"612":1,"613":1,"614":1,"616":1,"617":1,"622":1,"623":1,"624":1,"627":1,"629":1,"631":1,"635":1,"653":1,"654":1,"656":1,"666":2,"668":5,"775":3}}],["structured",{"2":{"154":1,"231":1,"250":1}}],["structures",{"2":{"42":1,"50":1,"188":2,"210":1,"229":1}}],["structure",{"0":{"26":1},"2":{"29":1,"94":1,"189":1,"193":1,"217":1,"683":1,"715":1,"716":1,"775":2}}],["street",{"2":{"674":1}}],["strengthen",{"2":{"258":1}}],["strength",{"0":{"252":1},"2":{"252":2}}],["streamline",{"2":{"263":1}}],["stream",{"0":{"195":1,"767":1},"2":{"187":1,"195":7,"767":2}}],["streaming",{"2":{"168":4,"195":2,"767":1}}],["strategy",{"2":{"250":1}}],["strategies",{"2":{"110":1,"247":2}}],["straightforward",{"2":{"93":1,"176":1}}],["strikes",{"2":{"234":1}}],["strict",{"2":{"143":2,"164":2}}],["strip",{"2":{"105":2}}],["stringpredicate",{"0":{"600":1},"2":{"636":2}}],["stringwithjson",{"0":{"601":1},"2":{"539":1}}],["stringified",{"2":{"187":1}}],["strings",{"2":{"50":1,"137":1,"238":1}}],["string",{"0":{"361":1,"517":1,"599":1},"2":{"36":1,"53":2,"65":1,"74":1,"128":6,"134":1,"137":2,"164":6,"167":2,"188":4,"193":8,"198":6,"199":4,"200":3,"201":4,"202":8,"203":2,"205":22,"217":2,"219":2,"220":2,"238":6,"273":6,"367":2,"377":1,"444":1,"458":1,"459":3,"460":1,"464":1,"465":1,"475":1,"476":1,"492":1,"517":1,"540":1,"550":3,"585":1,"599":1,"600":4,"601":1,"606":1,"618":1,"630":1,"633":1,"634":2,"653":1,"766":1,"770":1,"772":4}}],["strongly",{"2":{"190":1,"209":1,"218":1,"220":1,"231":1}}],["strong",{"2":{"97":1,"103":1,"246":1,"248":1,"252":3,"259":1}}],["str",{"2":{"52":2,"53":8,"60":2,"66":2,"217":2,"220":2,"273":2,"702":4,"703":4,"704":8,"705":6,"706":8,"708":8,"709":8,"710":2,"711":2,"712":8,"713":8,"714":2}}],["stack",{"2":{"699":1}}],["stackexchange",{"2":{"268":1}}],["staff",{"2":{"260":1}}],["staggering",{"2":{"252":1}}],["stage",{"2":{"176":1,"285":1,"671":1}}],["stability",{"2":{"247":1}}],["stable$",{"2":{"183":1,"279":1,"280":1,"281":1,"282":1}}],["stable",{"2":{"2":4,"5":6,"103":1,"180":6,"183":4,"185":1,"279":1,"280":2,"281":2,"282":5,"665":1}}],["stale",{"2":{"193":8}}],["stay",{"2":{"126":1,"127":1,"248":1}}],["stamp",{"2":{"88":1,"255":1}}],["standalone",{"2":{"241":1}}],["standards",{"2":{"260":1}}],["standard",{"2":{"57":1,"100":1,"107":1,"108":1,"110":1,"231":1,"238":1,"241":2,"247":1,"282":1,"665":1}}],["stands",{"2":{"38":1,"94":1}}],["startlistening",{"2":{"193":4}}],["startswith",{"2":{"600":1}}],["starts",{"2":{"28":1,"88":1,"195":1,"214":2,"767":1}}],["starting",{"2":{"22":1,"67":4,"125":1,"137":1,"195":1,"213":20,"224":1,"238":1,"767":1}}],["started",{"0":{"178":1},"2":{"18":2,"88":2,"103":1,"146":1,"167":1,"169":1,"203":1,"223":1,"272":1}}],["startup",{"2":{"16":2}}],["start",{"0":{"213":1},"1":{"214":1},"2":{"11":1,"18":2,"28":1,"40":1,"53":2,"72":1,"88":1,"91":1,"135":1,"138":1,"146":1,"162":1,"178":1,"213":4,"216":1,"222":1,"229":1,"462":1,"463":1,"565":1,"566":1,"567":1,"568":1,"772":1}}],["statuses",{"2":{"769":2}}],["statuschecker",{"2":{"193":9}}],["status",{"0":{"775":1},"1":{"776":1},"2":{"20":2,"21":1,"32":2,"113":2,"120":1,"122":3,"150":1,"164":2,"170":2,"174":10,"186":1,"188":2,"193":24,"212":2,"287":2,"543":1,"544":1,"769":2,"770":1,"772":1,"775":5,"776":4,"777":1}}],["stateless",{"2":{"187":1}}],["statement",{"2":{"137":1}}],["statements",{"2":{"100":1}}],["state",{"0":{"7":1,"112":1,"696":1},"2":{"6":1,"28":5,"34":1,"39":2,"40":1,"65":2,"72":1,"98":1,"112":2,"180":2,"193":8,"217":2,"222":1,"224":1,"226":1,"233":3,"272":1,"669":1,"683":1,"692":1,"694":1,"696":1,"750":1,"756":1,"759":1}}],["statically",{"2":{"101":1,"107":1,"216":1,"231":1,"241":2}}],["static",{"0":{"234":1},"2":{"6":2,"103":1,"187":1,"205":2,"218":1,"231":1}}],["searches",{"2":{"716":2,"736":1,"738":1,"740":1}}],["seamless",{"2":{"287":1}}],["semantics",{"2":{"667":1}}],["semi",{"2":{"722":1}}],["semirange",{"0":{"569":1},"2":{"636":1}}],["semiinterval",{"0":{"566":1,"567":1,"568":1},"2":{"569":3,"636":1}}],["selected",{"2":{"697":1}}],["select",{"2":{"265":10}}],["self",{"2":{"116":1,"666":4,"668":8}}],["sextillion",{"2":{"252":1}}],["severely",{"2":{"208":1}}],["several",{"2":{"5":1,"62":1,"88":1,"97":1,"128":1,"137":1,"671":1}}],["session",{"2":{"166":2,"244":1,"263":2}}],["serde",{"2":{"216":1,"217":2,"231":1}}],["serialized",{"2":{"654":1,"775":1}}],["serialise",{"2":{"208":1,"217":1}}],["serialisation",{"2":{"103":2,"186":1,"231":1}}],["series",{"2":{"269":1}}],["seriously",{"2":{"247":1,"248":1}}],["serif",{"2":{"193":2}}],["service",{"2":{"137":1,"229":1,"266":1,"287":1}}],["services",{"2":{"18":2,"137":1,"249":2}}],["serve",{"2":{"160":1}}],["serves",{"2":{"125":1,"137":1,"199":1,"238":1,"255":2,"263":1}}],["servers",{"2":{"260":1}}],["server",{"2":{"117":1,"195":1,"753":1,"767":2,"768":1}}],["secret",{"2":{"256":1}}],["secp256k1",{"2":{"238":2,"239":4,"299":1}}],["secs",{"2":{"193":2,"775":4,"776":3}}],["sec",{"2":{"193":2}}],["seconds",{"2":{"205":28,"253":1}}],["second",{"2":{"187":2,"189":1,"210":1,"219":1,"248":1,"259":1,"272":1}}],["secondly",{"2":{"106":1,"154":1}}],["securing",{"2":{"246":3,"248":1}}],["security",{"0":{"245":1,"247":1,"251":1,"257":1,"258":1,"259":1,"260":1},"1":{"246":1,"248":1,"249":1,"250":1,"252":1,"253":1,"258":1,"259":1,"260":1},"2":{"42":1,"128":1,"132":1,"158":1,"163":1,"178":3,"198":2,"199":2,"230":1,"234":3,"237":1,"238":3,"245":3,"246":4,"247":9,"248":1,"249":13,"250":9,"251":2,"253":1,"259":1,"260":23,"261":1,"262":1,"264":2,"267":2,"268":4}}],["securely",{"2":{"178":1,"252":1,"253":1,"268":1}}],["secure",{"2":{"170":1,"238":2,"246":1,"247":1,"248":2,"249":5,"250":1,"253":1,"254":4,"256":2,"257":1,"258":4,"262":1,"263":6,"692":1,"732":1}}],["sections",{"2":{"29":1,"110":1,"197":1}}],["section",{"2":{"18":1,"80":1,"98":1,"124":1,"135":1,"139":1,"141":1,"149":1,"150":1,"177":1,"178":3,"189":1,"199":1,"208":1,"217":1,"219":1,"228":1,"237":1,"246":1,"247":1,"262":1,"264":1,"265":2,"271":1,"274":1,"277":1,"284":1,"285":1,"667":1,"700":1,"715":1,"716":2,"717":1}}],["sequencebox",{"0":{"570":1},"2":{"457":1}}],["sequences",{"2":{"137":1}}],["sequence",{"2":{"39":1,"40":1,"41":1,"49":2,"71":1,"100":1,"104":1,"128":2,"164":2,"457":1,"461":1,"690":1,"698":1}}],["sensitive",{"2":{"116":1,"128":1,"245":2,"247":2,"248":2,"249":3,"255":1,"258":2,"260":2,"261":1,"264":1}}],["sense",{"2":{"28":1,"88":1,"126":1,"154":1,"160":1,"166":1,"187":1,"273":1,"722":1}}],["sentences",{"2":{"253":1}}],["sent",{"2":{"65":1}}],["sends",{"2":{"263":2,"767":1,"768":1}}],["sender",{"2":{"255":3,"259":2}}],["sendtransactionasync",{"2":{"205":18}}],["sendtransaction",{"2":{"199":11,"200":5,"201":10,"202":9,"203":4}}],["sendqueryasync",{"2":{"205":10}}],["sendquery",{"2":{"199":2,"200":2,"201":2,"202":2}}],["sending",{"2":{"195":1,"217":1,"247":1,"256":1,"767":1}}],["send",{"2":{"11":2,"28":1,"187":2,"195":1,"255":1,"755":2,"767":1,"768":1}}],["seek",{"2":{"267":1}}],["sees",{"2":{"256":1}}],["seed",{"2":{"238":7,"239":1}}],["seems",{"2":{"103":1}}],["seen",{"2":{"40":1,"94":1,"252":1}}],["see",{"2":{"18":1,"88":2,"91":1,"99":1,"104":1,"115":1,"128":1,"132":1,"134":1,"137":1,"145":1,"151":1,"166":1,"168":3,"170":5,"171":3,"188":1,"193":1,"198":1,"199":2,"200":2,"201":1,"202":1,"203":1,"204":1,"209":1,"252":1,"261":1,"263":3,"264":1,"265":2,"268":1,"270":1,"272":2,"668":1,"683":1,"775":1}}],["sets",{"2":{"138":3,"168":2,"232":1,"676":1}}],["setparameterbox",{"0":{"572":1},"2":{"457":1}}],["setparameter",{"0":{"47":1},"2":{"40":1,"41":1,"47":1,"457":1,"461":1,"698":1}}],["setkeyvalueinstructioncommitted",{"2":{"205":2}}],["setkeyvaluebox",{"0":{"571":1},"2":{"52":2,"53":2,"457":1}}],["setkeyvalue",{"0":{"46":1},"2":{"40":1,"41":1,"52":2,"205":4,"457":1,"461":1,"698":1}}],["set",{"0":{"161":1},"2":{"18":1,"28":2,"36":1,"40":1,"42":1,"52":2,"53":4,"55":1,"56":2,"57":1,"59":1,"84":1,"88":1,"97":1,"118":1,"120":1,"123":1,"125":1,"135":1,"139":1,"141":2,"146":1,"150":1,"154":1,"161":1,"170":1,"178":1,"186":2,"187":1,"190":1,"211":1,"213":1,"216":1,"225":1,"230":1,"249":1,"250":1,"252":5,"253":3,"259":3,"260":2,"264":1,"281":3,"668":2,"684":1,"690":1,"698":1,"701":3,"708":9,"709":4,"710":5,"712":9,"725":1,"757":1,"775":1}}],["settings",{"2":{"137":1,"166":1,"265":2}}],["setting",{"0":{"133":1},"1":{"134":1,"135":1},"2":{"16":1,"52":2,"141":1,"158":1,"160":1,"186":1,"211":1,"218":1,"258":1}}],["setupeventsreturn",{"2":{"193":4}}],["setups",{"2":{"154":1,"182":1}}],["setup",{"0":{"15":1,"16":1,"17":1,"167":1,"197":1,"207":1,"216":1,"272":1},"1":{"16":1,"17":1},"2":{"13":1,"154":1,"176":1,"193":9,"253":2,"263":1,"268":1,"283":1}}],["setcrypto",{"2":{"5":4,"186":4,"193":4}}],["separately",{"2":{"46":1}}],["separate",{"2":{"10":1,"102":1,"160":1,"166":1,"207":1,"220":1,"238":1,"249":2,"667":1,"734":1}}],["soars",{"2":{"253":1}}],["social",{"2":{"253":1,"259":1,"261":1}}],["socketaddrhost",{"0":{"585":1},"2":{"584":1}}],["socketaddrv6",{"0":{"587":1},"2":{"584":1}}],["socketaddrv4",{"0":{"586":1},"2":{"584":1}}],["socketaddr",{"0":{"584":1},"2":{"537":1}}],["socket",{"2":{"10":1,"18":1,"22":1,"161":1,"187":4,"193":2}}],["solve",{"2":{"665":1}}],["solution",{"2":{"280":1,"282":1}}],["solutions",{"2":{"236":1}}],["solo",{"2":{"250":1}}],["solokeys",{"2":{"250":1}}],["solid",{"2":{"249":1}}],["solidity",{"2":{"101":1}}],["soup",{"2":{"146":1}}],["sourced",{"2":{"287":1}}],["sources",{"0":{"2":1},"2":{"0":1,"2":3,"3":1,"5":4,"186":1,"193":1,"205":12,"249":1}}],["source",{"0":{"242":1},"2":{"0":1,"2":1,"4":1,"99":1,"141":1,"191":2,"193":1,"241":1,"244":4,"248":2,"249":4,"271":1,"612":1,"693":1,"716":1}}],["sooner",{"2":{"88":1,"247":1}}],["sorts",{"2":{"160":1,"273":1}}],["sort",{"2":{"69":3,"772":2}}],["sorting",{"0":{"69":1},"2":{"65":1,"69":3}}],["sortedvec",{"0":{"594":1,"595":1,"596":1,"597":1,"598":1},"2":{"289":2,"493":1,"560":1,"578":1,"579":1}}],["sortedmap",{"0":{"588":1,"589":1,"590":1,"591":1,"592":1,"593":1},"2":{"289":1,"341":3,"478":1,"608":1,"656":1}}],["sorted",{"2":{"65":1,"69":1,"715":1}}],["soramitsu",{"2":{"6":2,"168":4,"197":2,"198":16,"199":24,"204":8,"205":40}}],["sophisticated",{"2":{"36":1}}],["somewhere",{"2":{"216":1,"258":1}}],["somewhat",{"2":{"95":1,"120":1,"150":1}}],["sometimes",{"2":{"65":1,"278":1}}],["something",{"2":{"41":1,"56":1,"72":1,"100":1,"103":1,"137":2,"153":1,"166":1,"187":1,"211":1,"218":1,"258":1,"285":1,"286":1}}],["someone",{"2":{"28":1,"137":1,"230":1,"261":1}}],["some",{"2":{"16":2,"18":1,"28":3,"36":2,"41":1,"42":1,"43":1,"57":2,"65":1,"67":5,"93":1,"94":2,"97":2,"104":1,"106":1,"110":1,"137":3,"138":1,"143":1,"148":1,"155":1,"166":2,"168":1,"172":1,"187":1,"189":2,"193":6,"197":1,"202":1,"204":1,"205":6,"212":1,"220":1,"235":1,"241":1,"249":1,"256":1,"273":1,"506":1,"507":1,"508":1,"509":1,"510":1,"511":1,"512":1,"513":1,"514":1,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1,"665":1,"685":1,"691":1,"707":1,"769":2}}],["software",{"2":{"14":3,"39":1,"157":1,"230":1,"248":5,"253":1,"259":1,"260":2,"686":2}}],["so",{"2":{"5":1,"14":1,"17":1,"18":2,"39":2,"40":1,"41":1,"43":1,"46":1,"56":1,"62":2,"79":1,"82":1,"88":3,"90":2,"94":3,"97":3,"103":1,"105":2,"106":1,"135":1,"137":3,"141":1,"146":1,"163":1,"170":1,"171":1,"186":2,"187":2,"188":2,"198":1,"211":1,"212":1,"216":3,"220":2,"224":1,"234":1,"241":1,"243":1,"252":1,"253":2,"256":1,"263":2,"268":1,"270":1,"273":1,"283":1,"665":1,"734":1,"766":1}}],["sigkill",{"2":{"266":2}}],["signal",{"2":{"266":1}}],["signatory",{"2":{"219":2}}],["signatories=",{"2":{"210":2}}],["signatories",{"2":{"128":6,"164":6,"170":12,"189":2,"200":4,"289":1,"493":1}}],["signatories|",{"2":{"29":2}}],["signatureof",{"0":{"575":1,"576":1,"577":1,"597":1,"598":1},"2":{"578":1,"579":1,"581":1,"597":1,"598":1}}],["signaturecheckcondition",{"0":{"574":1},"2":{"170":4,"289":1,"634":2}}],["signature",{"0":{"573":1},"2":{"41":1,"128":1,"137":1,"170":4,"227":1,"238":2,"255":2,"256":1,"259":2,"289":1,"550":1,"575":1,"576":1,"577":1,"581":1,"772":2}}],["signaturesof",{"0":{"578":1,"579":1},"2":{"580":1,"582":1}}],["signatures",{"0":{"255":1},"2":{"11":8,"187":1,"238":3,"246":1,"248":1,"249":1,"259":2,"578":1,"579":1,"580":1,"582":1}}],["signing",{"2":{"256":1}}],["signifies",{"2":{"125":1}}],["significant",{"2":{"25":1,"137":1}}],["significantly",{"2":{"14":1,"97":1,"104":1,"106":1,"109":1,"248":1,"252":1}}],["signer",{"2":{"187":19,"193":8}}],["signedtransaction",{"0":{"582":1},"2":{"652":1}}],["signedquery",{"0":{"581":1},"2":{"651":1}}],["signedblock",{"0":{"580":1},"2":{"650":1}}],["signed",{"2":{"24":2,"25":1,"128":2,"249":1,"256":1,"777":1}}],["sign",{"2":{"53":2,"61":2,"71":2,"187":4,"249":1,"259":2}}],["signs",{"2":{"18":1}}],["six",{"2":{"249":1}}],["sieves",{"2":{"204":1,"222":1}}],["silence",{"2":{"153":1}}],["sip",{"2":{"97":1}}],["site",{"2":{"249":1}}],["sit",{"2":{"91":1}}],["situation",{"2":{"88":2}}],["sided",{"2":{"163":1}}],["side",{"0":{"256":1},"2":{"65":1,"187":1,"212":1,"273":1,"715":1}}],["sizeerror",{"0":{"583":1},"2":{"483":2}}],["sizes",{"2":{"161":1,"664":1}}],["size",{"0":{"105":1,"109":1},"1":{"106":1,"107":1,"108":1,"109":1,"110":1},"2":{"39":1,"103":1,"105":8,"106":1,"107":2,"109":2,"110":4,"113":2,"121":3,"137":1,"143":18,"164":20,"170":2,"192":6,"252":1,"268":1,"406":1,"407":1,"408":1,"409":1,"410":1,"411":1,"412":1,"413":1,"414":1,"415":1,"416":1,"417":1,"432":1,"474":1,"607":1,"772":1,"775":4}}],["simultaneously",{"2":{"123":1}}],["similarity",{"2":{"137":1}}],["similarly",{"2":{"59":1,"63":1,"180":1}}],["similar",{"2":{"20":1,"21":1,"44":1,"173":1,"188":1,"202":1,"203":1,"210":1,"216":1,"221":1,"278":1}}],["simplify",{"2":{"161":1}}],["simplicity",{"2":{"16":1}}],["simply",{"2":{"9":1,"197":1,"253":1,"268":1,"272":1}}],["simplest",{"2":{"282":1}}],["simpler",{"2":{"211":1}}],["simple",{"0":{"102":1},"1":{"103":1,"104":1},"2":{"5":1,"28":1,"62":2,"74":1,"79":1,"88":1,"100":1,"101":1,"137":1,"186":1,"187":1,"199":1,"208":1,"233":1,"665":1,"732":1}}],["single",{"2":{"18":1,"38":1,"45":1,"74":1,"92":1,"93":1,"94":1,"96":1,"124":1,"125":2,"126":2,"137":1,"143":1,"166":1,"188":1,"200":2,"214":2,"219":2,"220":2,"241":1,"253":2,"671":1,"716":1}}],["since",{"2":{"5":1,"21":1,"57":1,"82":1,"88":1,"101":1,"103":1,"106":1,"116":1,"128":2,"137":1,"146":1,"180":1,"187":1,"188":1,"190":1,"211":1,"238":1,"247":1,"248":1,"256":1,"268":2,"272":1,"273":1,"282":1,"605":1,"771":6,"775":3}}],["sdks",{"2":{"134":1,"180":1,"197":1,"287":3}}],["sdk",{"2":{"5":5,"198":1,"201":1,"206":1,"287":3,"716":1}}],["s",{"0":{"184":1},"2":{"2":1,"5":1,"14":1,"25":1,"28":2,"29":6,"37":1,"39":2,"40":1,"42":1,"50":2,"52":4,"53":5,"60":2,"65":2,"66":1,"72":1,"88":2,"89":1,"91":1,"97":1,"104":1,"105":3,"113":1,"115":2,"128":2,"134":1,"135":1,"136":1,"137":7,"141":2,"143":2,"145":2,"146":2,"148":1,"150":2,"152":1,"154":1,"159":1,"160":1,"161":1,"163":1,"166":2,"167":1,"168":4,"170":3,"172":1,"176":1,"183":1,"186":1,"187":4,"188":3,"193":1,"197":1,"202":2,"203":1,"207":1,"209":1,"211":1,"212":1,"213":1,"216":3,"217":3,"218":3,"222":2,"238":2,"239":4,"243":1,"244":2,"249":2,"250":1,"251":1,"253":9,"254":1,"255":2,"256":3,"259":2,"260":2,"262":1,"268":2,"270":1,"272":2,"280":2,"282":3,"283":1,"286":1,"664":1,"665":3,"667":1,"674":2,"683":1,"687":1,"710":2,"711":2,"766":1,"772":1,"775":2,"777":1}}],["src",{"2":{"2":7,"3":1,"5":8,"104":1}}],["f71ea9d897c4338cbf4f1dc7b492aad0bf6ce896b803d7cdb9cf25ecc15109826b0f56f58761060056355dba0e0fc489cfb2f974481ed64873082e6032796235",{"2":{"239":2}}],["fficonvert",{"2":{"667":2}}],["ffireturn",{"2":{"666":2}}],["ffitype",{"2":{"666":2,"667":4}}],["ffi",{"0":{"664":1,"665":1,"667":1},"1":{"665":1,"666":1,"667":1,"668":2},"2":{"103":1,"665":5,"666":2,"667":11,"668":9}}],["friend",{"2":{"161":1}}],["frequently",{"2":{"218":1}}],["freestanding",{"2":{"668":1}}],["free",{"2":{"168":1,"170":1}}],["freely",{"2":{"125":1,"254":1}}],["fresh",{"2":{"135":1,"167":1,"273":1}}],["framework",{"2":{"134":1,"232":1,"249":1}}],["franca",{"2":{"103":1}}],["front",{"2":{"18":1,"79":1}}],["fromaccount",{"2":{"191":4}}],["fromjson",{"2":{"187":2,"193":2}}],["from",{"0":{"183":1},"2":{"0":1,"3":1,"4":1,"5":6,"11":1,"16":2,"28":1,"40":1,"51":2,"52":2,"53":8,"60":2,"65":2,"66":2,"89":1,"92":8,"93":1,"97":2,"98":2,"101":1,"104":1,"105":2,"107":1,"116":1,"125":2,"126":1,"127":1,"128":1,"134":1,"135":1,"137":2,"151":1,"154":1,"166":2,"170":1,"172":2,"174":1,"175":1,"179":1,"181":1,"186":4,"187":30,"188":4,"189":3,"190":5,"191":2,"192":4,"193":55,"195":3,"198":1,"199":1,"202":8,"203":2,"207":2,"208":3,"209":3,"210":5,"211":2,"213":1,"216":1,"217":10,"219":5,"220":3,"229":2,"230":3,"238":1,"241":1,"248":2,"249":5,"252":2,"254":1,"256":3,"258":2,"259":1,"264":1,"265":6,"266":1,"267":1,"270":1,"272":1,"273":4,"287":3,"667":3,"668":1,"673":1,"698":1,"702":4,"703":4,"704":8,"705":6,"706":8,"708":8,"709":12,"710":2,"711":4,"712":8,"713":16,"714":2,"734":1,"764":1,"766":1,"767":1,"769":1,"771":2,"772":1}}],["flash",{"2":{"249":1,"268":1}}],["flags",{"2":{"168":2}}],["flag1",{"2":{"166":1}}],["flag",{"2":{"22":1,"166":2}}],["flexible",{"2":{"232":1}}],["flexibility",{"0":{"232":1},"2":{"264":1}}],["floating",{"2":{"211":1}}],["flow",{"2":{"137":1}}],["flowers",{"2":{"128":8,"164":8,"199":4,"200":4,"201":6,"202":6,"203":4}}],["flux",{"2":{"97":1}}],["f64",{"2":{"92":8,"93":2,"220":5,"775":1}}],["fujitsu",{"2":{"236":1}}],["fuzz",{"2":{"235":1}}],["fulfilled",{"2":{"193":6}}],["fully",{"2":{"227":1}}],["full",{"2":{"16":3,"40":1,"67":1,"71":1,"106":1,"124":1,"143":1,"187":2,"193":1,"727":1,"730":1,"750":1,"757":1}}],["fuel",{"2":{"143":2,"164":2}}],["further",{"2":{"105":2,"197":1,"247":1,"252":1}}],["fun",{"2":{"199":5,"200":4,"201":6,"202":4,"203":2}}],["fundamental",{"2":{"197":1,"216":1,"238":1}}],["fungible",{"0":{"673":1,"674":1},"2":{"24":2,"190":2,"201":2,"211":2,"220":2,"227":2,"673":1,"674":1}}],["functioning",{"2":{"156":1,"677":1}}],["functions",{"2":{"65":1,"200":1,"216":1,"255":1,"664":2,"665":2,"667":1}}],["functionality",{"2":{"188":1,"249":1,"268":1,"678":1,"688":1}}],["functional",{"2":{"19":1}}],["function",{"0":{"664":1},"1":{"665":1,"666":1,"667":1,"668":1},"2":{"18":10,"20":2,"103":1,"113":2,"137":1,"153":1,"164":2,"170":4,"174":2,"187":3,"188":6,"189":2,"193":10,"198":2,"218":1,"239":2,"548":1,"665":5,"666":2,"667":1,"668":3,"677":1}}],["future",{"2":{"21":1,"63":1,"97":1,"99":1,"103":1,"143":2,"164":2,"166":2,"186":1,"205":24,"237":1,"272":1,"701":1}}],["f",{"2":{"18":1,"146":1}}],["famous",{"2":{"278":1}}],["family",{"2":{"193":2}}],["familiarity",{"2":{"223":1}}],["familiarise",{"2":{"216":1}}],["familiar",{"2":{"100":1,"185":1,"225":1,"229":1}}],["favour",{"2":{"249":1}}],["fabric",{"2":{"236":1}}],["fashioned",{"2":{"670":1}}],["fashion",{"2":{"190":1}}],["fast",{"2":{"42":1,"100":1,"232":1}}],["faster",{"2":{"14":2,"186":1}}],["fatal",{"2":{"183":1}}],["fallible",{"2":{"667":1}}],["fall",{"2":{"253":1,"775":1}}],["fallback",{"2":{"125":2}}],["falkon",{"2":{"249":1}}],["falsify",{"2":{"230":1}}],["false",{"2":{"3":1,"113":2,"143":6,"164":8,"170":2,"174":4,"211":1}}],["facing",{"2":{"250":1,"756":1}}],["facilities",{"2":{"154":1}}],["facilitates",{"2":{"6":1}}],["factor",{"2":{"259":2}}],["factors",{"2":{"137":1,"247":1,"699":1}}],["factory",{"2":{"137":1,"190":1}}],["fact",{"2":{"105":1,"141":1,"166":1,"177":1,"190":1,"207":1,"216":1}}],["fairly",{"2":{"72":1,"187":1}}],["failbox",{"0":{"377":1},"2":{"457":1}}],["failing",{"2":{"272":2}}],["failure",{"2":{"125":1,"135":1,"235":1,"273":1,"618":1,"619":1}}],["fails",{"2":{"57":1}}],["fail",{"2":{"28":1,"98":1,"135":1,"205":4,"211":1,"457":1,"459":1,"461":1,"741":1}}],["failed",{"2":{"6":1,"28":1,"217":2,"218":2,"220":2,"272":3,"287":1,"695":1}}],["fake",{"2":{"28":1,"259":2}}],["faulty",{"2":{"755":2}}],["fault",{"0":{"230":1,"677":1},"2":{"18":1,"146":1,"163":1,"230":3,"669":1,"697":1}}],["faults",{"2":{"18":3,"146":2,"693":1}}],["fare",{"2":{"287":1}}],["far",{"2":{"14":2,"46":1,"94":2,"161":1,"188":1,"211":1,"253":1,"268":1}}],["figure",{"2":{"261":1}}],["firewall",{"2":{"258":2}}],["firefox",{"2":{"249":1}}],["firedragon",{"2":{"249":1}}],["firstly",{"2":{"106":1,"154":1,"280":1}}],["first",{"0":{"18":1},"2":{"5":1,"14":1,"16":1,"18":1,"20":2,"21":2,"43":1,"59":1,"62":1,"63":1,"81":1,"97":1,"119":1,"126":1,"128":1,"135":1,"136":1,"141":1,"166":1,"167":1,"171":1,"187":1,"188":1,"189":1,"195":1,"207":1,"210":1,"211":1,"212":1,"216":2,"219":1,"220":1,"221":1,"222":1,"230":1,"233":1,"248":1,"265":2,"271":1,"272":5,"283":1,"285":1,"286":1,"287":1,"767":1}}],["fish",{"2":{"244":3}}],["field",{"2":{"127":2,"289":2,"292":2,"293":2,"294":2,"295":2,"296":2,"297":2,"298":2,"300":2,"306":2,"307":2,"308":2,"311":2,"312":2,"313":2,"314":2,"317":2,"318":2,"321":2,"322":2,"323":2,"324":2,"325":2,"327":2,"330":2,"331":2,"334":2,"335":2,"336":2,"337":2,"340":2,"341":2,"344":2,"345":2,"347":2,"348":2,"349":2,"350":2,"351":2,"352":2,"353":2,"354":2,"355":2,"356":2,"357":2,"358":2,"359":2,"360":2,"361":2,"362":2,"363":2,"364":2,"365":2,"366":2,"372":2,"373":2,"374":2,"377":2,"401":2,"402":2,"403":2,"404":2,"405":2,"418":2,"419":2,"420":2,"421":2,"422":2,"423":2,"424":2,"425":2,"426":2,"427":2,"428":2,"429":2,"430":2,"433":2,"434":2,"435":2,"436":2,"437":2,"438":2,"439":2,"440":2,"441":2,"444":2,"446":2,"447":2,"456":2,"460":2,"462":2,"463":2,"470":2,"471":2,"472":2,"474":2,"475":2,"478":2,"479":2,"480":2,"481":2,"482":2,"484":2,"487":2,"488":2,"489":2,"490":2,"491":2,"493":2,"494":2,"495":2,"496":2,"497":2,"501":2,"522":2,"530":2,"531":2,"532":2,"533":2,"536":2,"537":2,"538":2,"539":2,"540":2,"541":2,"543":2,"544":2,"548":2,"551":2,"552":2,"553":2,"554":2,"556":2,"558":2,"559":2,"560":2,"563":2,"564":2,"565":2,"566":2,"567":2,"568":2,"570":2,"571":2,"572":2,"573":2,"578":2,"579":2,"580":2,"581":2,"582":2,"583":2,"585":2,"586":2,"587":2,"602":2,"603":2,"605":2,"606":2,"607":2,"608":2,"609":2,"611":2,"612":2,"613":2,"614":2,"616":2,"617":2,"622":2,"623":2,"624":2,"627":2,"629":2,"631":2,"635":2,"653":2,"654":2,"656":2,"665":1,"668":9,"776":2}}],["fields",{"2":{"50":1,"126":1,"127":4,"188":1,"775":2}}],["fit",{"2":{"101":1}}],["filled",{"2":{"117":1}}],["filterbox",{"0":{"378":1},"2":{"193":4,"222":2,"370":1}}],["filtering",{"2":{"65":1}}],["filtered",{"2":{"65":1,"715":1}}],["filteropt",{"0":{"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1},"2":{"38":2,"94":4,"291":1,"292":2,"311":2,"317":2,"338":7,"343":2,"344":2,"378":1,"536":2,"563":2,"622":2,"625":1}}],["filter",{"0":{"79":1,"94":1},"2":{"37":1,"38":4,"65":2,"68":2,"75":2,"79":1,"81":1,"85":1,"93":2,"94":20,"95":3,"97":3,"154":1,"174":2,"193":2,"204":2,"212":7,"222":6,"292":2,"296":1,"297":1,"311":2,"317":2,"344":2,"536":2,"551":1,"563":2,"622":2}}],["filters",{"0":{"37":1,"38":1,"68":1},"1":{"38":1},"2":{"33":1,"37":1,"51":1,"69":1,"79":2,"87":1,"94":1,"97":2,"204":1,"212":1,"222":2,"759":1}}],["file2",{"2":{"166":1}}],["filename",{"2":{"2":4,"5":4}}],["file",{"0":{"154":1},"2":{"2":3,"4":1,"5":4,"13":1,"16":1,"21":1,"22":3,"115":1,"125":5,"126":3,"127":1,"128":1,"130":1,"131":2,"132":3,"135":1,"143":8,"145":1,"149":1,"150":1,"151":1,"152":1,"154":4,"164":6,"166":4,"167":3,"168":5,"170":1,"174":2,"183":2,"186":10,"193":1,"197":1,"199":1,"208":2,"217":11,"238":1,"244":1,"256":1,"265":3,"272":5}}],["files",{"0":{"8":1,"17":1,"21":1,"164":1},"2":{"0":1,"4":1,"6":1,"8":1,"13":2,"16":1,"17":1,"21":1,"132":1,"135":2,"146":1,"164":1,"168":1,"178":1,"186":1,"207":1,"265":1}}],["fingerprint",{"2":{"259":1}}],["financial",{"2":{"245":2,"247":1,"252":1,"670":2}}],["final",{"2":{"205":70}}],["finalization",{"2":{"127":1}}],["finally",{"2":{"18":1,"96":1,"109":1,"136":1,"137":1,"166":1,"187":1,"193":1,"204":1,"207":1,"209":1,"211":1,"212":1,"222":1,"270":1}}],["finite",{"2":{"141":1}}],["finished",{"2":{"22":1,"88":1,"141":1,"166":1}}],["fine",{"2":{"28":1,"131":1,"143":1,"151":1,"187":1,"279":1}}],["findzbyxandy",{"2":{"716":2}}],["findtransactionsbyaccountid",{"0":{"438":1,"757":1},"2":{"549":2}}],["findtransactionbyhash",{"0":{"437":1,"758":1},"2":{"549":2}}],["findtriggerbyid",{"0":{"439":1,"761":1},"2":{"549":2}}],["findtriggersbydomainid",{"0":{"441":1,"763":1},"2":{"83":1,"549":2}}],["findtriggerkeyvaluebyidandkey",{"0":{"440":1,"762":1},"2":{"54":1,"549":2}}],["findtotalassetquantitybyassetdefinitionid",{"0":{"436":1,"744":1},"2":{"549":2}}],["findpermissiontokenschema",{"0":{"432":1},"2":{"549":2}}],["findpermissiontokensbyaccountid",{"0":{"433":1,"724":1},"2":{"64":1,"549":2}}],["findblockheaderbyhash",{"0":{"428":1,"748":1},"2":{"549":2}}],["finding",{"2":{"208":1}}],["finddomainbyid",{"0":{"429":1,"751":1},"2":{"205":2,"549":2}}],["finddomainkeyvaluebyidandkey",{"0":{"430":1,"752":1},"2":{"54":1,"549":2}}],["finderror",{"0":{"431":1},"2":{"459":1,"550":1,"610":1,"772":1,"773":4,"774":6}}],["finder",{"2":{"167":1}}],["finds",{"2":{"66":1,"72":1,"716":1,"723":1,"726":1,"744":2,"760":1,"761":1,"762":1,"763":1}}],["findrolesbyaccountid",{"0":{"435":1,"721":1},"2":{"64":1,"549":2}}],["findrolebyroleid",{"0":{"434":1,"720":1},"2":{"64":1,"549":2}}],["findaccountswithasset",{"0":{"405":1,"731":1},"2":{"549":2}}],["findaccountsbyname",{"0":{"404":1,"729":1},"2":{"549":2}}],["findaccountsbydomainid",{"0":{"403":1,"730":1},"2":{"549":2}}],["findaccountbyid",{"0":{"401":1,"727":1},"2":{"202":2,"205":4,"549":2}}],["findaccountkeyvaluebyidandkey",{"0":{"402":1,"728":1},"2":{"54":1,"549":2}}],["findalltransactions",{"0":{"417":1},"2":{"549":2}}],["findallpeers",{"0":{"414":1,"754":1},"2":{"549":2}}],["findallpermissiontokendefinitions",{"0":{"723":1},"2":{"64":1}}],["findallparameters",{"0":{"413":1,"755":1},"2":{"549":2}}],["findallblocks",{"0":{"411":1,"746":1},"2":{"549":2}}],["findallblockheaders",{"0":{"410":1,"747":1},"2":{"549":2}}],["findallassetdefinitions",{"0":{"734":1}}],["findallassetsdefinitions",{"0":{"409":1},"2":{"549":2}}],["findallassets",{"0":{"408":1,"733":1},"2":{"192":2,"201":6,"549":2}}],["findallactivetriggerids",{"0":{"407":1,"760":1},"2":{"549":2}}],["findallaccounts",{"0":{"406":1,"726":1},"2":{"66":4,"192":2,"200":6,"549":2}}],["findalldomains",{"0":{"412":1,"750":1},"2":{"104":4,"188":2,"192":2,"199":6,"549":2}}],["findallroleids",{"0":{"415":1,"719":1},"2":{"64":1,"549":2}}],["findallroles",{"0":{"416":1,"718":1},"2":{"64":1,"549":2,"721":1}}],["findassetsbyname",{"0":{"427":1,"736":1},"2":{"549":2}}],["findassetsbydomainidandassetdefinitionid",{"0":{"426":1,"740":1},"2":{"549":2}}],["findassetsbydomainid",{"0":{"425":1,"739":1},"2":{"549":2}}],["findassetsbyassetdefinitionid",{"0":{"424":1,"738":1},"2":{"549":2}}],["findassetsbyaccountid",{"0":{"423":1,"737":1},"2":{"66":4,"549":2}}],["findassetquantitybyid",{"0":{"422":1,"741":1},"2":{"549":2}}],["findassetdefinitionbyid",{"0":{"419":1},"2":{"549":2}}],["findassetdefinitionkeyvaluebyidandkey",{"0":{"420":1,"743":1},"2":{"54":1,"205":2,"549":2}}],["findassetbyid",{"0":{"418":1,"735":1},"2":{"549":2}}],["findassetkeyvaluebyidandkey",{"0":{"421":1,"742":1},"2":{"54":1,"205":2,"549":2}}],["find",{"2":{"10":1,"14":1,"53":2,"58":1,"66":1,"83":1,"124":1,"151":1,"164":1,"178":1,"188":2,"268":1,"367":1,"459":1,"550":1,"669":1,"772":2}}],["fixing",{"2":{"166":1}}],["fixedwidth",{"2":{"658":1,"659":1,"660":1,"661":1,"662":1,"663":1}}],["fixedpointconversion",{"2":{"476":1}}],["fixedpoint",{"0":{"443":1},"2":{"442":1}}],["fixed",{"0":{"442":1,"566":1},"2":{"25":2,"50":1,"57":1,"92":11,"93":2,"190":2,"211":2,"220":4,"319":2,"320":1,"443":1,"504":2,"566":2,"569":2,"707":1}}],["fix",{"2":{"6":1,"14":1,"183":1,"189":1,"210":1,"219":1,"244":3,"273":1,"280":1}}],["fn",{"2":{"4":1,"53":2,"104":2,"666":4,"668":8}}],["feedback",{"2":{"204":1,"260":1}}],["feed",{"2":{"174":1}}],["feel",{"2":{"168":1,"216":1}}],["fees",{"2":{"89":1}}],["few",{"2":{"24":1,"57":1,"99":1,"100":2,"137":1,"189":1,"190":1,"200":1,"201":1,"211":1,"219":1,"220":1,"226":1,"231":1}}],["featured",{"2":{"227":1}}],["features=panic",{"2":{"108":2}}],["features",{"2":{"5":1,"6":3,"178":1,"187":3,"204":1,"222":3,"226":1,"229":1,"232":1,"249":3,"268":1,"272":1}}],["feature",{"2":{"4":2,"36":1,"69":1,"97":1,"108":1,"232":1,"249":3,"717":1,"722":1}}],["fetched",{"2":{"3":1,"287":1}}],["fetching",{"0":{"3":1}}],["fetch",{"2":{"0":1,"3":1,"143":2,"164":2,"183":1,"187":27,"193":4,"195":1,"772":1}}],["fostering",{"2":{"260":1}}],["focuses",{"2":{"262":1}}],["focused",{"2":{"249":1}}],["focus",{"2":{"204":1,"222":1}}],["font",{"2":{"193":2,"238":1}}],["footprint",{"2":{"186":1,"233":1}}],["foundation",{"2":{"228":1,"238":1,"245":1}}],["found",{"0":{"773":1,"774":1},"2":{"53":4,"188":2,"202":2,"270":1,"282":1,"769":1,"773":1,"774":1}}],["four",{"2":{"20":1,"22":1,"135":5,"146":2,"170":1}}],["folders",{"2":{"21":3,"258":1}}],["folder",{"2":{"16":1,"17":2,"21":1,"183":4,"193":1,"207":3,"216":1,"243":1,"282":1,"681":1}}],["followed",{"2":{"156":1,"168":1,"177":1,"250":1}}],["follow",{"2":{"90":1,"166":1,"167":1,"175":1,"178":2,"179":1,"213":1,"225":2,"248":1,"250":1,"252":1,"286":1}}],["follows",{"2":{"5":1,"67":1,"97":1,"170":1,"263":1}}],["following",{"2":{"2":1,"29":1,"32":1,"33":1,"50":1,"53":1,"62":1,"63":1,"73":1,"78":1,"85":1,"96":1,"104":2,"134":1,"141":1,"143":1,"151":1,"170":2,"180":1,"186":5,"190":1,"199":1,"200":1,"201":3,"203":1,"204":1,"208":1,"211":1,"218":1,"238":3,"241":1,"243":1,"244":6,"246":1,"247":3,"249":1,"250":1,"252":3,"253":1,"263":1,"264":1,"265":4,"268":1,"272":1,"279":1,"666":1,"698":1,"699":1,"701":1,"715":1,"716":1,"775":1}}],["forbidmintonmintable",{"2":{"485":1}}],["forwardcursor",{"0":{"444":1},"2":{"322":1,"323":1}}],["forwarding",{"2":{"263":1}}],["forgot",{"2":{"281":1}}],["forget",{"2":{"16":1}}],["fortifying",{"2":{"260":1}}],["fortify",{"2":{"250":1}}],["fortunately",{"2":{"211":1}}],["forking",{"2":{"693":1}}],["forks",{"2":{"249":1}}],["fork",{"2":{"218":1}}],["for=",{"2":{"193":4}}],["foreign",{"0":{"664":1},"1":{"665":1,"666":1,"667":1,"668":1},"2":{"103":1,"665":1}}],["forming",{"2":{"697":1}}],["formula",{"2":{"252":4}}],["forms",{"2":{"245":1}}],["former",{"2":{"204":1,"222":1,"282":1}}],["formed",{"2":{"88":1}}],["form",{"2":{"51":1,"73":1,"88":1,"168":1,"193":1,"199":1,"216":1,"218":1,"775":2}}],["formats",{"2":{"273":1}}],["formation",{"2":{"87":1}}],["format",{"0":{"273":1},"2":{"2":1,"101":1,"103":3,"130":3,"134":1,"136":1,"154":1,"200":1,"217":2,"238":2,"256":1,"273":1}}],["forces",{"2":{"6":1}}],["force",{"2":{"3":1,"252":1,"253":1}}],["for",{"0":{"105":1,"132":1,"133":1,"135":1,"192":1,"259":1,"260":1},"1":{"106":1,"107":1,"108":1,"109":1,"110":1,"133":1,"134":2,"135":2,"136":1},"2":{"3":1,"5":2,"6":3,"9":1,"11":1,"13":4,"14":3,"16":4,"17":1,"18":2,"20":2,"22":1,"25":1,"28":3,"32":1,"36":1,"38":1,"39":1,"40":2,"41":4,"42":5,"45":1,"47":1,"50":1,"51":1,"52":4,"53":4,"56":2,"57":1,"62":6,"64":2,"65":2,"66":1,"67":1,"69":1,"70":1,"78":4,"80":1,"82":1,"83":2,"84":1,"86":1,"88":3,"89":1,"93":1,"94":4,"97":3,"98":2,"99":2,"100":2,"104":3,"105":4,"106":2,"110":2,"111":1,"112":1,"115":2,"117":1,"118":1,"119":2,"121":1,"122":3,"123":1,"124":1,"125":4,"126":3,"128":4,"134":2,"135":2,"136":2,"137":3,"138":1,"139":1,"140":2,"141":8,"143":4,"145":2,"146":1,"147":1,"149":1,"151":4,"153":1,"154":2,"155":3,"158":2,"160":2,"161":1,"163":1,"164":4,"166":7,"167":1,"168":1,"170":7,"176":1,"177":1,"178":1,"180":1,"182":1,"183":1,"186":9,"187":6,"189":1,"192":6,"193":1,"195":2,"197":2,"200":2,"201":2,"202":2,"206":1,"207":3,"209":2,"210":2,"211":1,"212":3,"214":4,"216":3,"217":1,"218":2,"219":3,"220":2,"222":6,"224":1,"228":2,"231":2,"233":3,"238":10,"244":4,"245":3,"246":2,"247":2,"248":4,"249":14,"250":2,"252":3,"254":1,"256":1,"258":1,"259":4,"260":6,"261":2,"262":1,"263":5,"264":4,"265":3,"266":1,"267":1,"268":2,"270":2,"271":1,"272":5,"273":2,"274":1,"277":1,"282":3,"283":2,"284":1,"285":1,"286":1,"287":3,"664":1,"667":2,"668":8,"670":3,"673":1,"676":1,"679":1,"680":1,"682":1,"683":1,"696":1,"699":5,"700":1,"710":1,"711":1,"716":2,"720":1,"728":1,"729":1,"732":1,"736":1,"738":1,"740":1,"744":2,"747":1,"750":1,"755":7,"756":2,"763":1,"769":1,"775":4,"777":1}}],["d9",{"2":{"775":2}}],["d1fd9fe",{"2":{"279":2}}],["daemon",{"2":{"272":1}}],["daily",{"2":{"250":1}}],["damage",{"2":{"250":2}}],["day",{"2":{"246":2}}],["dayssinceequinox",{"2":{"666":9}}],["days",{"2":{"226":1}}],["date",{"2":{"222":1,"226":1,"281":1,"664":1,"764":2}}],["dates",{"2":{"141":1}}],["dataevent",{"0":{"339":1},"2":{"368":1}}],["dataeventfilter",{"2":{"204":1,"222":1}}],["dataentityfilter",{"0":{"338":1,"385":1},"2":{"378":1,"385":1,"625":1}}],["database",{"2":{"260":1,"265":7,"266":1,"268":2}}],["datamodel",{"2":{"198":2,"199":18,"204":6,"205":22}}],["data",{"0":{"29":1,"33":1,"38":1,"86":1,"288":1},"1":{"289":1,"290":1,"291":1,"292":1,"293":1,"294":1,"295":1,"296":1,"297":1,"298":1,"299":1,"300":1,"301":1,"302":1,"303":1,"304":1,"305":1,"306":1,"307":1,"308":1,"309":1,"310":1,"311":1,"312":1,"313":1,"314":1,"315":1,"316":1,"317":1,"318":1,"319":1,"320":1,"321":1,"322":1,"323":1,"324":1,"325":1,"326":1,"327":1,"328":1,"329":1,"330":1,"331":1,"332":1,"333":1,"334":1,"335":1,"336":1,"337":1,"338":1,"339":1,"340":1,"341":1,"342":1,"343":1,"344":1,"345":1,"346":1,"347":1,"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"367":1,"368":1,"369":1,"370":1,"371":1,"372":1,"373":1,"374":1,"375":1,"376":1,"377":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"401":1,"402":1,"403":1,"404":1,"405":1,"406":1,"407":1,"408":1,"409":1,"410":1,"411":1,"412":1,"413":1,"414":1,"415":1,"416":1,"417":1,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":1,"425":1,"426":1,"427":1,"428":1,"429":1,"430":1,"431":1,"432":1,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":1,"441":1,"442":1,"443":1,"444":1,"445":1,"446":1,"447":1,"448":1,"449":1,"450":1,"451":1,"452":1,"453":1,"454":1,"455":1,"456":1,"457":1,"458":1,"459":1,"460":1,"461":1,"462":1,"463":1,"464":1,"465":1,"466":1,"467":1,"468":1,"469":1,"470":1,"471":1,"472":1,"473":1,"474":1,"475":1,"476":1,"477":1,"478":1,"479":1,"480":1,"481":1,"482":1,"483":1,"484":1,"485":1,"486":1,"487":1,"488":1,"489":1,"490":1,"491":1,"492":1,"493":1,"494":1,"495":1,"496":1,"497":1,"498":1,"499":1,"500":1,"501":1,"502":1,"503":1,"504":1,"505":1,"506":1,"507":1,"508":1,"509":1,"510":1,"511":1,"512":1,"513":1,"514":1,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1,"522":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1,"530":1,"531":1,"532":1,"533":1,"534":1,"535":1,"536":1,"537":1,"538":1,"539":1,"540":1,"541":1,"542":1,"543":1,"544":1,"545":1,"546":1,"547":1,"548":1,"549":1,"550":1,"551":1,"552":1,"553":1,"554":1,"555":1,"556":1,"557":1,"558":1,"559":1,"560":1,"561":1,"562":1,"563":1,"564":1,"565":1,"566":1,"567":1,"568":1,"569":1,"570":1,"571":1,"572":1,"573":1,"574":1,"575":1,"576":1,"577":1,"578":1,"579":1,"580":1,"581":1,"582":1,"583":1,"584":1,"585":1,"586":1,"587":1,"588":1,"589":1,"590":1,"591":1,"592":1,"593":1,"594":1,"595":1,"596":1,"597":1,"598":1,"599":1,"600":1,"601":1,"602":1,"603":1,"604":1,"605":1,"606":1,"607":1,"608":1,"609":1,"610":1,"611":1,"612":1,"613":1,"614":1,"615":1,"616":1,"617":1,"618":1,"619":1,"620":1,"621":1,"622":1,"623":1,"624":1,"625":1,"626":1,"627":1,"628":1,"629":1,"630":1,"631":1,"632":1,"633":1,"634":1,"635":1,"636":1,"637":1,"638":1,"639":1,"640":1,"641":1,"642":1,"643":1,"644":1,"645":1,"646":1,"647":1,"648":1,"649":1,"650":1,"651":1,"652":1,"653":1,"654":1,"655":1,"656":1,"657":1,"658":1,"659":1,"660":1,"661":1,"662":1,"663":1},"2":{"10":1,"11":1,"28":1,"31":1,"33":1,"38":1,"42":2,"46":1,"51":3,"52":1,"53":2,"60":2,"65":1,"67":1,"80":1,"85":1,"86":1,"87":1,"88":1,"94":3,"97":1,"99":1,"103":1,"104":2,"137":8,"174":2,"178":1,"185":1,"186":8,"187":6,"188":3,"189":2,"190":4,"191":2,"192":2,"193":9,"204":1,"209":4,"210":4,"211":4,"216":6,"217":4,"218":2,"219":4,"220":1,"222":3,"229":1,"230":1,"231":1,"237":1,"245":2,"246":1,"247":3,"248":2,"249":6,"251":1,"254":1,"255":3,"258":4,"260":2,"261":1,"272":4,"287":2,"368":1,"378":1,"625":1,"665":1,"670":1,"682":1,"683":1,"716":6,"732":1,"735":1,"775":1}}],["dyn",{"2":{"218":2}}],["dynamically",{"2":{"234":1,"241":1,"287":1}}],["dynamic",{"0":{"234":1},"2":{"6":2,"90":1,"218":2,"234":1,"664":1}}],["due",{"2":{"232":1,"249":1,"674":1,"769":1}}],["duck",{"2":{"209":1,"211":1}}],["dummy",{"2":{"167":1}}],["during",{"2":{"69":1,"97":1,"128":2,"220":1,"232":1,"263":1,"670":1}}],["duration",{"0":{"346":1,"507":1},"2":{"57":1,"198":4,"263":1,"507":1,"565":2,"605":2}}],["drinks",{"2":{"94":1,"97":2}}],["drives",{"2":{"247":1}}],["driven",{"2":{"233":1,"759":1}}],["drive",{"2":{"39":1,"249":1,"268":1}}],["drawbacks",{"2":{"216":1}}],["drawback",{"2":{"101":1,"268":1}}],["draw",{"2":{"39":1}}],["d",{"2":{"17":1,"78":1,"88":1,"101":2,"103":1,"137":1,"166":2,"199":4,"272":1}}],["diligence",{"2":{"665":1}}],["diligently",{"2":{"211":1}}],["did",{"2":{"213":4}}],["didn",{"2":{"16":1,"88":2,"154":1,"211":1,"220":2,"278":1,"280":1}}],["dividebyzero",{"2":{"476":1}}],["divide",{"0":{"340":1},"2":{"376":2}}],["div",{"2":{"193":12}}],["diverse",{"2":{"267":1,"725":1}}],["dive",{"2":{"178":1,"246":1}}],["dictionaries",{"2":{"253":1}}],["dictionary",{"2":{"137":1,"208":1,"238":1}}],["dictate",{"2":{"189":1,"210":1,"219":1}}],["difficult",{"2":{"146":1,"254":1}}],["differing",{"2":{"674":1}}],["differ",{"2":{"137":1}}],["differences",{"2":{"42":1,"138":1,"141":1,"178":1,"225":1,"229":1}}],["difference",{"2":{"17":1,"56":1,"124":1}}],["differentiate",{"2":{"286":1}}],["differently",{"2":{"87":1,"166":1,"229":1}}],["different",{"2":{"10":1,"16":1,"21":1,"25":1,"28":2,"31":1,"37":1,"42":1,"44":1,"50":1,"88":2,"90":1,"100":1,"103":1,"116":2,"123":1,"124":1,"127":1,"128":2,"135":2,"137":1,"138":1,"141":1,"143":1,"166":1,"170":1,"207":1,"210":1,"217":2,"229":1,"238":2,"239":2,"246":1,"248":1,"249":1,"256":2,"258":1,"268":1,"287":1,"665":2,"674":1,"686":1,"722":1}}],["dir",{"2":{"143":2,"164":2,"183":1,"186":2}}],["direct",{"2":{"65":1,"168":2}}],["directory",{"0":{"243":1,"244":1},"1":{"244":1},"2":{"3":1,"6":2,"16":4,"17":1,"21":1,"134":1,"166":1,"167":3,"168":2,"170":3,"177":2,"183":1,"186":1,"198":1,"238":1,"241":1,"242":1,"243":1,"244":1,"283":2}}],["directly",{"2":{"0":1,"13":1,"16":2,"36":1,"42":1,"56":1,"97":1,"127":1,"154":1,"176":1,"187":1,"273":1,"287":1}}],["distributed",{"2":{"769":1}}],["distribution",{"2":{"282":1}}],["distributions",{"2":{"241":1,"243":1}}],["distinction",{"2":{"126":1}}],["distinguished",{"2":{"126":1}}],["dispatched",{"2":{"231":1}}],["dispatch",{"2":{"218":3}}],["displayed",{"2":{"238":1}}],["displaystatus",{"2":{"193":4}}],["display",{"2":{"188":2,"636":1}}],["disks",{"2":{"248":1}}],["disk",{"2":{"174":1,"199":1}}],["disabling",{"2":{"250":1}}],["disable",{"2":{"143":2,"164":2,"249":1,"259":1}}],["disallowed",{"2":{"62":1}}],["discipline",{"2":{"665":1}}],["discriminant",{"2":{"290":1,"291":1,"299":1,"309":1,"310":1,"315":1,"316":1,"319":1,"320":1,"328":1,"332":1,"333":1,"338":1,"339":1,"342":1,"343":1,"367":1,"368":1,"371":1,"375":1,"376":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"431":1,"445":1,"453":1,"454":1,"455":1,"457":1,"458":1,"459":1,"461":1,"464":1,"473":1,"476":1,"483":1,"485":1,"486":1,"502":1,"503":1,"504":1,"505":1,"534":1,"535":1,"542":1,"545":1,"546":1,"547":1,"549":1,"550":1,"555":1,"557":1,"561":1,"562":1,"569":1,"574":1,"584":1,"600":1,"610":1,"615":1,"618":1,"619":1,"620":1,"621":1,"625":1,"626":1,"628":1,"630":1,"632":1,"633":1,"634":1,"636":1,"648":1,"649":1,"650":1,"651":1,"652":1}}],["disconnect",{"2":{"268":1}}],["disconnected",{"2":{"267":1}}],["discord",{"2":{"249":2,"270":3}}],["discover",{"2":{"222":1}}],["discovered",{"2":{"151":1,"157":1,"193":1}}],["discovery",{"2":{"140":1,"146":1,"157":1,"158":1}}],["disclosed",{"2":{"248":1}}],["discusses",{"2":{"249":1}}],["discuss",{"2":{"42":1,"137":2,"159":1,"160":1,"166":1,"270":1}}],["diagram",{"2":{"29":1,"667":1,"698":1}}],["digitally",{"0":{"262":1},"1":{"263":1,"264":1,"265":1,"266":1},"2":{"262":1}}],["digital",{"2":{"237":1,"238":4,"255":2,"258":2,"259":1,"267":1,"670":1}}],["digits",{"2":{"25":1}}],["digest",{"2":{"18":10,"20":2,"113":2,"164":2,"170":8,"174":2,"187":2,"189":4,"193":2,"239":2,"548":1}}],["doubt",{"2":{"286":1}}],["doubly",{"2":{"188":1}}],["double",{"2":{"124":1,"126":2,"143":1,"668":1}}],["doomed",{"2":{"190":1,"211":1}}],["doing",{"2":{"176":1,"248":1,"256":1,"265":1}}],["does",{"2":{"104":1,"137":1,"162":1,"166":1,"168":1,"186":1,"187":3,"204":1,"238":1,"719":1,"721":1}}],["doesn",{"2":{"5":1,"102":1,"125":1,"160":1,"163":1,"166":2,"171":1,"187":2,"188":1,"189":2,"198":1,"208":1,"210":2,"219":2,"243":1,"280":1,"283":1,"285":1,"676":1}}],["dominate",{"2":{"103":1}}],["domain=",{"2":{"771":4}}],["domainviolation",{"2":{"476":1}}],["domaineventfilter",{"0":{"343":1,"386":1},"2":{"344":1,"386":1}}],["domainevent",{"0":{"342":1,"391":1,"526":1},"2":{"339":1,"344":1,"391":1}}],["domainfilter",{"0":{"344":1,"387":1},"2":{"338":1,"387":1}}],["domainname",{"2":{"188":8,"193":6}}],["domainid",{"0":{"345":1,"351":1,"482":1,"506":1},"2":{"187":4,"188":4,"189":4,"190":10,"191":12,"193":4,"205":14,"218":4,"219":2,"293":1,"312":1,"341":1,"342":3,"403":1,"425":1,"426":1,"429":1,"430":1,"431":1,"441":1,"454":2,"482":1,"495":1,"506":1,"526":1,"623":1,"730":1,"739":1,"740":1,"751":1,"752":1,"763":1,"773":1,"774":1}}],["domain",{"0":{"83":1,"169":1,"188":1,"209":1,"218":1,"341":1,"692":1,"749":1},"1":{"750":1,"751":1,"752":1},"2":{"29":5,"38":1,"41":4,"42":5,"56":1,"65":1,"74":1,"82":4,"83":6,"100":1,"104":8,"128":1,"130":6,"137":4,"139":1,"143":2,"164":2,"168":4,"169":10,"170":7,"171":8,"174":2,"178":1,"187":4,"188":16,"189":9,"190":7,"191":6,"192":18,"193":14,"199":10,"200":3,"201":1,"202":2,"203":2,"205":20,"209":10,"210":4,"211":3,"216":2,"218":13,"219":8,"220":2,"225":1,"227":1,"233":1,"293":1,"312":1,"339":1,"342":1,"403":1,"425":1,"426":1,"431":1,"441":1,"455":2,"555":1,"623":1,"667":1,"669":1,"691":1,"692":1,"699":2,"701":1,"718":1,"719":1,"722":1,"729":1,"730":1,"739":1,"740":1,"749":4,"750":1,"751":2,"752":1,"763":2,"773":2,"774":2}}],["domainsaccountsassets",{"2":{"192":1}}],["domains",{"0":{"30":1,"192":1,"199":1},"2":{"11":8,"29":3,"33":1,"41":3,"42":1,"50":1,"51":1,"80":1,"104":6,"111":2,"116":1,"128":1,"130":3,"168":2,"188":2,"192":4,"199":8,"200":2,"201":2,"202":2,"203":2,"220":1,"692":1,"699":1,"714":9,"750":1,"771":8}}],["dormouse",{"2":{"91":1,"92":9}}],["don",{"2":{"16":2,"57":1,"82":1,"88":2,"97":2,"101":1,"102":1,"103":1,"141":1,"166":2,"187":1,"189":1,"210":1,"212":1,"216":1,"219":1,"220":1,"232":1,"272":1,"273":1,"278":1,"282":1}}],["done",{"2":{"13":1,"18":1,"22":1,"39":1,"65":2,"71":1,"98":1,"105":1,"127":1,"135":1,"161":1,"213":1,"218":1,"230":1,"233":1,"236":1,"282":1,"728":1}}],["do",{"2":{"13":1,"16":3,"17":1,"18":2,"20":1,"21":2,"36":1,"40":1,"41":1,"44":1,"56":4,"62":1,"65":1,"82":1,"88":1,"94":2,"97":2,"101":3,"104":1,"105":2,"135":1,"137":1,"139":1,"151":1,"154":3,"163":1,"166":3,"167":1,"171":1,"187":1,"189":1,"199":1,"202":1,"207":1,"208":2,"211":1,"212":2,"217":1,"218":2,"219":1,"220":1,"222":1,"226":1,"243":1,"247":1,"272":2,"273":1,"279":1,"283":1,"286":1,"665":4,"715":1,"722":1,"729":1}}],["downs",{"2":{"759":1}}],["downside",{"2":{"187":1}}],["download",{"2":{"249":1}}],["downtime",{"2":{"137":1}}],["down",{"2":{"6":1,"91":1,"104":1,"110":1,"137":1,"222":1,"253":2,"282":1}}],["documents",{"2":{"258":2}}],["document",{"2":{"222":1,"223":1,"236":1,"250":1}}],["documentation",{"2":{"0":1,"2":1,"4":2,"5":1,"99":1,"178":1,"186":1,"207":1,"208":1,"216":1,"222":1,"224":1,"228":1,"235":1,"244":1,"270":1,"287":2,"682":1}}],["doctors",{"2":{"137":1}}],["dockerhub",{"2":{"213":1}}],["docker$",{"2":{"167":1,"168":1}}],["docker",{"0":{"6":1,"213":1,"214":1,"272":1,"275":1},"1":{"7":1,"8":1,"9":1,"214":1},"2":{"6":11,"8":1,"9":3,"13":1,"18":4,"19":1,"21":1,"128":1,"167":7,"168":4,"174":1,"184":1,"207":2,"213":5,"214":3,"216":1,"224":1,"241":1,"272":6,"671":1}}],["docs",{"2":{"5":4,"216":1}}],["derive",{"2":{"666":2}}],["derived",{"2":{"127":1,"252":1,"256":1}}],["deep",{"2":{"246":1}}],["deal",{"2":{"230":1}}],["dealing",{"2":{"79":1,"260":1}}],["dear",{"2":{"220":1}}],["demonstration",{"2":{"211":1}}],["demo",{"0":{"194":1},"2":{"194":1,"272":1}}],["de757bcb79f4c63e8fa0795edc26f86dfdba189b846e903d0b732bb644607720e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3",{"2":{"187":2}}],["de",{"2":{"186":1,"208":1,"217":1,"273":1}}],["delve",{"2":{"251":1}}],["delimiter$whiterabbit",{"2":{"202":2}}],["delimiter$madhatter",{"2":{"201":2}}],["delimiter$domain",{"2":{"200":2,"201":2,"202":2}}],["delivered",{"2":{"170":1}}],["delay",{"2":{"143":2,"151":2,"164":2}}],["delayed",{"2":{"89":1}}],["deleted",{"2":{"3":1,"290":1,"309":1,"315":1,"332":1,"342":1,"561":1,"620":1}}],["denoted",{"2":{"124":1,"143":1,"287":1}}],["denies",{"2":{"63":1}}],["denyall",{"2":{"62":1}}],["deny",{"2":{"62":5,"63":1}}],["desktop",{"2":{"249":2}}],["described",{"2":{"271":1,"274":1,"277":1,"284":1,"285":1}}],["describes",{"2":{"237":1,"247":1}}],["describe",{"2":{"236":1}}],["description",{"2":{"62":1,"180":1,"186":1,"667":1,"777":1}}],["descriptions",{"2":{"40":1,"698":1,"716":2}}],["destroyed",{"2":{"230":1}}],["destination",{"2":{"41":2,"128":6,"164":6,"190":2,"191":2,"211":4,"330":1,"446":1,"484":1,"559":1,"612":1}}],["deserialization",{"2":{"775":1}}],["deserialize",{"2":{"272":2}}],["deserialising",{"2":{"218":1,"273":1}}],["deserves",{"2":{"166":1}}],["despite",{"2":{"137":1,"218":1,"235":1}}],["desirable",{"2":{"155":1}}],["desired",{"2":{"89":1}}],["designs",{"2":{"674":1}}],["designate",{"2":{"260":1}}],["design",{"2":{"90":1,"189":1,"210":1,"219":1,"232":1}}],["designed",{"2":{"52":1,"223":1,"230":1,"233":1,"248":1}}],["dedicated",{"2":{"29":1,"80":1,"98":1,"136":1,"139":1,"270":1,"717":1,"722":1}}],["declarations",{"2":{"289":1,"292":1,"293":1,"294":1,"295":1,"296":1,"297":1,"298":1,"300":1,"306":1,"307":1,"308":1,"311":1,"312":1,"313":1,"314":1,"317":1,"318":1,"321":1,"322":1,"323":1,"324":1,"325":1,"327":1,"330":1,"331":1,"334":1,"335":1,"336":1,"337":1,"340":1,"341":1,"344":1,"345":1,"347":1,"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"372":1,"373":1,"374":1,"377":1,"401":1,"402":1,"403":1,"404":1,"405":1,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":1,"425":1,"426":1,"427":1,"428":1,"429":1,"430":1,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":1,"441":1,"444":1,"446":1,"447":1,"456":1,"460":1,"462":1,"463":1,"470":1,"471":1,"472":1,"474":1,"475":1,"478":1,"479":1,"480":1,"481":1,"482":1,"484":1,"487":1,"488":1,"489":1,"490":1,"491":1,"493":1,"494":1,"495":1,"496":1,"497":1,"501":1,"522":1,"530":1,"531":1,"532":1,"533":1,"536":1,"537":1,"538":1,"539":1,"540":1,"541":1,"543":1,"544":1,"548":1,"551":1,"552":1,"553":1,"554":1,"556":1,"558":1,"559":1,"560":1,"563":1,"564":1,"565":1,"566":1,"567":1,"568":1,"570":1,"571":1,"572":1,"573":1,"578":1,"579":1,"580":1,"581":1,"582":1,"583":1,"585":1,"586":1,"587":1,"602":1,"603":1,"605":1,"606":1,"607":1,"608":1,"609":1,"611":1,"612":1,"613":1,"614":1,"616":1,"617":1,"622":1,"623":1,"624":1,"627":1,"629":1,"631":1,"635":1,"653":1,"654":1,"656":1}}],["declare",{"2":{"187":10,"188":4,"192":4,"195":2}}],["declared",{"2":{"43":1}}],["decreasing",{"2":{"252":1}}],["decrease",{"2":{"98":1,"105":2,"246":1}}],["decreases",{"2":{"97":3}}],["decouple",{"2":{"186":1}}],["decoding",{"2":{"28":1}}],["deciphered",{"2":{"255":1}}],["decisions",{"2":{"262":1}}],["decision",{"2":{"65":1}}],["decided",{"2":{"86":1,"100":1}}],["decides",{"2":{"62":1}}],["decide",{"2":{"42":1,"108":1,"138":1,"141":1,"160":1}}],["decimal",{"2":{"25":1,"50":1,"443":1}}],["detect",{"2":{"260":1}}],["deterministic",{"2":{"238":1}}],["determining",{"2":{"97":1,"247":1}}],["determined",{"2":{"85":2,"110":1,"128":1}}],["determines",{"2":{"79":1,"81":2,"143":1}}],["determine",{"2":{"22":1,"81":1,"153":1}}],["detour",{"2":{"224":1}}],["detail",{"2":{"25":1,"42":1,"99":1,"160":1,"178":1,"224":1,"270":1,"664":1,"722":1}}],["detailed",{"2":{"16":1,"29":1,"70":1,"85":1,"224":1,"263":1}}],["details",{"2":{"13":2,"51":1,"57":1,"65":1,"81":1,"97":1,"115":1,"139":1,"140":1,"141":2,"143":1,"145":1,"151":1,"155":1,"164":3,"176":1,"182":1,"186":1,"248":1,"249":1,"252":1,"683":1,"700":1,"716":1,"718":1,"719":1,"720":1,"721":1,"723":1,"724":1,"726":1,"727":1,"728":1,"729":1,"730":1,"731":1,"733":1,"734":1,"735":1,"736":1,"737":1,"738":1,"739":1,"740":1,"741":1,"742":1,"743":1,"744":1,"746":1,"747":1,"748":1,"750":1,"751":1,"752":1,"754":1,"755":1,"757":1,"758":1,"760":1,"761":1,"762":1,"763":1,"775":1}}],["dependent",{"2":{"231":1}}],["dependencies",{"2":{"3":1,"103":2,"197":3,"216":3,"231":1,"234":1}}],["depend",{"2":{"187":1}}],["depends",{"2":{"79":1,"137":1,"186":1,"253":1,"272":1}}],["depending",{"2":{"42":1,"141":1,"166":1,"213":1,"217":1,"244":1,"249":1,"272":1,"716":1,"769":1}}],["deployed",{"2":{"137":1,"166":1,"247":1,"286":1}}],["deploying",{"2":{"132":1}}],["deploy$",{"2":{"17":2}}],["deployments",{"2":{"20":1,"227":1,"234":1,"717":1}}],["deployment",{"0":{"22":1,"132":1,"274":1},"1":{"133":1,"134":1,"135":1,"136":1,"275":1,"276":1},"2":{"14":1,"65":1,"146":1,"207":1,"230":1,"274":1,"285":1,"691":1,"693":1}}],["deploy",{"0":{"19":1},"1":{"20":1,"21":1},"2":{"14":1,"16":2,"17":2,"22":2,"178":1,"197":1,"260":1}}],["debugging",{"0":{"106":1},"2":{"105":2,"106":1,"750":1,"755":1}}],["debuginfo",{"2":{"105":2}}],["debug",{"2":{"14":4,"16":7,"17":2,"106":4,"110":1,"143":2,"153":1,"164":2,"167":2,"170":2,"177":1,"188":1,"193":2,"241":1,"242":2,"243":2,"473":1}}],["devoid",{"2":{"253":1}}],["devastating",{"2":{"245":1}}],["dev$",{"2":{"182":1}}],["devices",{"2":{"222":1,"247":1,"248":5,"258":1}}],["device",{"2":{"137":1,"248":2,"249":2,"268":2}}],["developing",{"2":{"250":1}}],["develop",{"2":{"248":1,"260":1}}],["developed",{"2":{"229":1}}],["developer",{"2":{"218":1}}],["developers",{"2":{"108":1,"224":1,"268":1}}],["development",{"2":{"3":1,"137":1,"186":1,"197":1,"208":1,"230":1,"235":1,"236":1}}],["dev",{"2":{"5":2,"18":8,"103":2,"154":1,"180":2,"182":1,"183":1,"197":1,"272":1,"764":1,"777":1}}],["def",{"2":{"220":6}}],["definitionid",{"2":{"171":4,"211":6,"220":1}}],["definition",{"2":{"52":12,"57":5,"128":6,"143":2,"164":8,"171":4,"190":2,"191":4,"192":4,"201":5,"202":2,"203":2,"205":12,"211":2,"313":1,"314":1,"318":1,"405":1,"424":1,"426":1,"470":1,"539":1,"701":5,"702":7,"703":7,"705":1,"712":5,"713":5,"734":2,"738":1,"740":1,"743":1,"744":1,"774":1}}],["definitions`",{"2":{"192":2}}],["definitions",{"2":{"33":1,"41":3,"42":1,"50":1,"51":1,"55":1,"57":1,"111":1,"169":2,"170":1,"192":4,"286":1,"341":1,"669":1,"723":1,"734":1}}],["defining",{"2":{"36":1}}],["define",{"0":{"94":1},"2":{"5":1,"137":1,"188":1,"219":2}}],["defines",{"2":{"2":2,"252":1,"667":2}}],["defined",{"2":{"2":1,"5":1,"75":1,"190":3,"260":1,"286":1,"667":1,"668":1}}],["defaultgenesis",{"2":{"205":14}}],["defaults",{"2":{"125":1,"166":1,"176":1}}],["default",{"0":{"127":1,"130":1,"281":1},"2":{"2":2,"3":1,"5":4,"11":1,"63":1,"114":1,"124":1,"125":2,"127":1,"128":1,"130":1,"137":2,"141":1,"143":1,"144":1,"153":1,"155":1,"168":2,"198":18,"205":18,"211":1,"216":1,"217":4,"218":4,"238":2,"249":1,"272":1,"281":3,"717":1}}],["i64",{"0":{"443":1,"658":1},"2":{"442":1,"443":1}}],["icecat",{"2":{"249":1}}],["i2p",{"2":{"197":2}}],["ignore",{"2":{"137":1,"187":1}}],["ignored",{"2":{"117":1}}],["io",{"2":{"197":2,"216":2}}],["iot",{"2":{"137":1}}],["ios",{"2":{"136":1}}],["ilovetea",{"2":{"113":2,"117":2,"164":2,"170":2}}],["illustrative",{"2":{"200":1}}],["illustration",{"2":{"29":1,"222":1}}],["illustrates",{"2":{"253":1}}],["illustrated",{"2":{"29":1}}],["illustrate",{"2":{"29":1}}],["irrelevant",{"2":{"97":1}}],["irohatest",{"2":{"205":4}}],["irohacontainer",{"2":{"198":8}}],["irohaconfig",{"2":{"198":2}}],["irohacargo",{"2":{"14":1}}],["irohax",{"2":{"20":1,"21":1}}],["iroha3",{"2":{"18":16,"146":2,"213":14}}],["iroha1",{"2":{"18":16,"146":2,"213":20}}],["iroha$",{"2":{"18":1,"21":1,"22":1,"177":1}}],["iroha0",{"2":{"6":2,"18":16,"146":2,"213":14}}],["iroha2asyncclient",{"2":{"205":4}}],["iroha2config",{"2":{"198":1}}],["iroha2client",{"2":{"198":1}}],["iroha2ver",{"2":{"197":2}}],["iroha2$",{"2":{"186":1}}],["iroha2",{"2":{"2":2,"5":4,"16":8,"18":24,"103":2,"146":2,"168":4,"180":3,"183":5,"185":1,"186":26,"187":25,"188":4,"189":2,"190":4,"191":2,"192":4,"193":21,"195":2,"198":16,"199":24,"204":8,"205":40,"206":5,"207":2,"208":2,"209":4,"210":4,"211":4,"213":20,"272":2,"764":1}}],["iroha",{"0":{"6":1,"13":1,"18":1,"39":1,"40":1,"118":1,"147":1,"167":1,"168":1,"175":1,"177":1,"179":1,"183":1,"197":1,"198":1,"207":1,"208":1,"216":1,"217":1,"227":1,"229":2,"678":1,"688":1,"689":1,"690":1,"691":1,"692":1,"693":1,"694":1,"698":1},"1":{"7":1,"8":1,"9":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"119":1,"120":1,"148":1,"149":1,"150":1,"176":1,"177":1,"180":1,"181":1,"182":1,"183":1,"184":1,"228":1,"230":2,"231":2,"232":2,"233":2,"234":2,"235":2,"679":1,"680":1,"681":1,"682":1,"683":1,"684":1,"685":1,"686":1,"687":1,"690":1,"691":1,"692":1,"693":1},"2":{"2":4,"5":16,"6":9,"9":3,"10":2,"11":3,"13":5,"14":7,"16":24,"17":17,"18":31,"19":1,"20":6,"21":4,"22":8,"24":1,"28":2,"33":1,"36":1,"37":2,"38":1,"39":5,"40":4,"41":1,"47":1,"49":1,"52":3,"53":2,"55":1,"60":2,"62":1,"63":1,"65":2,"69":1,"72":1,"82":1,"88":1,"89":1,"98":2,"99":1,"100":1,"103":8,"104":6,"106":2,"111":1,"116":1,"117":1,"120":2,"123":1,"124":1,"127":1,"128":1,"132":1,"134":1,"136":5,"137":1,"138":2,"141":2,"143":2,"146":4,"150":2,"151":3,"154":1,"155":1,"158":1,"164":1,"166":14,"167":5,"168":19,"169":4,"170":11,"171":7,"172":2,"173":2,"174":4,"175":3,"177":7,"178":10,"179":3,"180":3,"181":1,"183":8,"184":2,"185":2,"186":10,"187":8,"190":2,"193":2,"195":1,"197":14,"198":2,"199":2,"200":1,"201":2,"204":1,"206":1,"207":17,"208":2,"209":1,"211":3,"212":1,"213":134,"214":2,"216":27,"217":14,"218":12,"219":2,"220":7,"221":1,"222":6,"223":2,"224":2,"225":4,"226":2,"227":3,"228":6,"229":9,"230":7,"231":1,"232":3,"233":4,"234":2,"235":3,"236":1,"237":2,"238":1,"241":2,"245":1,"246":2,"247":2,"248":1,"249":1,"256":1,"257":1,"262":2,"270":3,"271":2,"272":12,"274":1,"277":1,"283":1,"284":1,"285":1,"665":4,"667":1,"669":9,"671":5,"676":1,"677":1,"678":1,"679":1,"683":1,"684":2,"685":1,"686":2,"687":1,"688":1,"689":1,"691":1,"693":3,"697":1,"698":2,"699":1,"700":1,"701":2,"717":1,"725":1,"749":1,"759":2,"764":1,"766":1}}],["id`",{"2":{"702":2,"703":2,"704":4,"705":4,"706":4,"708":4,"709":4,"712":4,"713":4}}],["idioms",{"2":{"209":1}}],["idiomatically",{"2":{"208":1}}],["idx",{"2":{"198":6}}],["id=asset",{"2":{"211":2}}],["id=",{"2":{"169":2,"170":4,"171":2,"193":2}}],["idle",{"2":{"143":2,"164":2}}],["idfilter",{"2":{"94":12}}],["idbox",{"0":{"354":1,"454":1},"2":{"92":8,"93":4,"190":4,"191":4,"220":4,"221":2,"330":1,"446":1,"484":1,"556":1,"558":1,"559":1,"571":1,"612":2,"627":1,"634":1,"704":2,"705":2,"706":2,"708":2,"709":2,"712":2,"713":2,"714":2}}],["id$domain",{"2":{"74":1}}],["ids",{"2":{"53":4,"170":1,"540":1,"719":2,"721":1}}],["ideographic",{"2":{"238":1}}],["ident",{"2":{"143":2,"164":2}}],["identify",{"2":{"260":1}}],["identifier",{"2":{"741":1,"742":1,"743":1,"751":1,"752":1}}],["identifies",{"2":{"260":1}}],["identified",{"2":{"216":1,"754":1}}],["identifiablebox",{"0":{"455":1},"2":{"92":8,"96":2,"188":4,"189":4,"190":4,"193":4,"218":1,"219":2,"634":1}}],["identifiable",{"2":{"42":2,"53":2,"188":4,"189":2,"190":2,"192":6,"193":2,"209":3,"210":2,"211":2,"634":1,"636":1}}],["identical",{"2":{"256":1,"673":1}}],["identity",{"2":{"222":2,"255":1,"259":1}}],["identities",{"2":{"94":1}}],["ideally",{"2":{"72":1,"248":1}}],["idea",{"2":{"16":1,"117":1,"136":1,"161":1,"189":1,"210":1,"219":1,"268":1,"670":1}}],["id",{"0":{"74":1},"2":{"36":1,"40":1,"42":1,"52":14,"53":30,"57":12,"60":8,"61":6,"66":4,"71":7,"72":1,"73":2,"74":1,"83":2,"93":2,"94":2,"95":2,"104":10,"113":2,"115":1,"116":1,"127":2,"128":32,"143":2,"145":1,"164":36,"170":17,"171":20,"172":2,"174":2,"187":2,"188":4,"189":4,"190":16,"191":20,"192":28,"193":4,"199":6,"200":8,"201":14,"202":4,"205":50,"216":1,"217":4,"218":2,"219":12,"220":8,"289":1,"293":1,"294":2,"295":2,"306":1,"307":1,"308":1,"312":1,"313":1,"314":1,"318":2,"330":1,"341":1,"372":1,"373":1,"374":1,"401":1,"402":1,"403":1,"405":1,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":1,"425":1,"426":2,"429":1,"430":1,"433":1,"434":1,"435":1,"436":1,"438":1,"439":1,"440":1,"441":1,"444":1,"446":1,"470":2,"479":1,"480":1,"481":1,"482":1,"484":1,"493":1,"494":1,"495":1,"531":1,"533":1,"538":2,"539":1,"556":1,"558":1,"559":1,"560":1,"571":1,"612":2,"613":1,"614":1,"616":1,"617":1,"623":1,"624":1,"627":1,"634":1,"698":1,"702":8,"703":8,"704":10,"705":10,"706":10,"708":10,"709":10,"710":4,"711":4,"712":10,"713":10,"714":6,"720":1,"738":1,"740":1,"761":1,"762":1,"763":1,"772":1}}],["immediately",{"2":{"199":1,"218":1}}],["immediate",{"2":{"108":2,"193":2,"216":1}}],["imagine",{"2":{"88":1,"137":1,"220":1}}],["imagename",{"2":{"198":2}}],["imagetag",{"2":{"198":2}}],["imagepullpolicy",{"2":{"198":4}}],["images",{"2":{"198":4}}],["image",{"2":{"18":8,"198":4,"213":1,"241":1}}],["improperly",{"2":{"777":1}}],["improving",{"2":{"252":1}}],["improvement",{"2":{"260":2}}],["improves",{"2":{"259":1}}],["improve",{"2":{"247":1}}],["improved",{"2":{"230":1}}],["impartial",{"2":{"260":1}}],["impact",{"2":{"39":1,"716":1}}],["impersonate",{"2":{"259":2}}],["impervious",{"2":{"251":1}}],["impl",{"2":{"666":2,"668":3}}],["implicit",{"2":{"188":1,"220":1}}],["implements",{"2":{"187":1}}],["implementing",{"2":{"97":1,"187":1,"260":1,"268":1}}],["implemented",{"2":{"90":1,"97":1,"161":1,"198":1,"227":1,"229":1,"272":1,"684":1}}],["implement",{"2":{"36":1,"187":3,"199":1,"200":1,"201":2,"203":1,"260":2,"262":1}}],["implementations",{"2":{"62":1,"103":1,"229":1,"664":1}}],["implementation",{"2":{"6":1,"97":1,"197":14,"256":1,"273":1,"666":2,"668":1,"683":1,"716":1}}],["impostor",{"2":{"256":1}}],["impossible",{"2":{"254":1}}],["imposed",{"2":{"137":1}}],["importance",{"2":{"230":1}}],["important",{"2":{"105":1,"110":1,"209":1,"234":1,"248":1,"249":1,"253":1,"258":1,"268":1,"671":1}}],["imports",{"2":{"189":1,"199":1}}],["imported",{"2":{"104":1}}],["import",{"2":{"5":3,"186":3,"187":22,"188":3,"189":1,"190":2,"191":1,"192":2,"193":53,"195":1,"198":34,"199":30,"204":7,"205":54,"208":3,"209":4,"210":4,"211":3}}],["ipv6",{"2":{"584":1}}],["ipv6predicate",{"0":{"469":1},"2":{"636":1}}],["ipv6addr",{"0":{"468":1},"2":{"137":1,"587":1,"634":2,"636":1}}],["ipv4",{"2":{"584":1}}],["ipv4predicate",{"0":{"467":1},"2":{"636":1}}],["ipv4addr",{"0":{"466":1},"2":{"586":1,"634":2,"636":1}}],["ipfspath",{"0":{"465":1,"512":1},"2":{"308":1,"341":1,"494":1,"495":1,"512":1}}],["ip",{"2":{"11":1,"65":1,"586":1,"587":1}}],["ipsum",{"2":{"4":11}}],["if=",{"2":{"193":2}}],["if",{"0":{"282":1,"456":1},"2":{"6":4,"11":1,"13":1,"14":1,"16":2,"18":2,"21":1,"28":6,"32":1,"36":2,"39":2,"40":2,"41":2,"43":1,"49":2,"57":2,"62":4,"69":1,"78":1,"84":1,"88":4,"89":1,"90":1,"91":1,"94":3,"97":2,"98":1,"100":1,"104":1,"120":1,"123":1,"125":2,"128":3,"132":1,"134":1,"135":2,"136":1,"137":3,"148":1,"150":1,"154":3,"155":1,"156":1,"160":2,"163":1,"166":4,"168":4,"170":1,"176":2,"177":2,"178":1,"183":2,"186":4,"187":1,"188":4,"189":4,"190":3,"193":1,"197":1,"198":1,"204":2,"207":1,"208":2,"210":4,"212":2,"216":2,"217":1,"218":5,"219":4,"220":2,"225":1,"228":1,"230":1,"232":1,"233":1,"238":6,"243":1,"244":4,"248":1,"249":2,"250":2,"253":2,"254":1,"256":1,"258":1,"261":1,"263":1,"265":1,"271":1,"272":5,"273":7,"274":1,"277":1,"278":1,"279":2,"280":2,"281":1,"282":1,"283":4,"284":1,"285":4,"286":3,"376":2,"457":1,"461":1,"675":1,"676":1,"690":1,"698":1,"729":1,"733":1,"734":2,"755":1,"769":1}}],["i",{"2":{"3":1,"41":1,"42":1,"84":1,"125":1,"141":1,"186":16,"187":1,"195":1,"220":1,"230":1,"252":2,"282":1,"690":1,"767":1}}],["isassetdefinitionowner",{"0":{"470":1},"2":{"549":2}}],["islistening",{"2":{"193":6}}],["isn",{"2":{"137":1,"160":1,"189":1,"210":1,"211":2,"212":1,"218":1,"219":1,"220":1,"253":1,"273":1}}],["isi",{"0":{"98":1,"689":1},"1":{"690":1,"691":1,"692":1,"693":1},"2":{"28":1,"39":1,"41":1,"72":1,"97":1,"100":3,"101":5,"103":1,"209":4,"210":4,"211":2,"218":2,"233":1,"669":1,"689":1,"690":1,"702":2,"703":2}}],["issued",{"2":{"675":1}}],["issue",{"2":{"6":2,"97":1,"128":1,"218":1,"270":1,"271":1,"272":2,"274":1,"277":1,"283":1,"284":1,"285":1,"775":1}}],["issues",{"0":{"271":1,"274":1,"277":1,"284":1},"1":{"272":1,"273":1,"275":1,"276":1,"278":1,"279":1,"280":1,"281":1,"282":1,"283":1},"2":{"6":1,"14":1,"176":1,"177":1,"212":1,"217":1,"249":1,"271":2,"274":1,"277":1,"284":1,"285":5,"286":1}}],["is",{"2":{"0":1,"2":1,"3":1,"4":1,"5":1,"6":1,"10":1,"11":2,"13":4,"14":1,"16":7,"17":2,"18":5,"19":1,"20":2,"21":3,"22":2,"24":2,"25":5,"28":10,"29":1,"31":2,"32":1,"33":1,"34":1,"35":1,"36":4,"38":4,"39":9,"40":1,"41":4,"42":4,"45":1,"46":1,"48":1,"50":1,"51":2,"52":2,"56":1,"57":2,"59":1,"61":1,"62":9,"63":2,"65":5,"66":1,"67":1,"69":3,"71":3,"72":3,"74":1,"75":2,"77":1,"78":2,"79":3,"80":2,"81":4,"82":1,"84":3,"85":3,"87":1,"88":6,"89":3,"90":3,"93":1,"94":10,"97":5,"98":2,"99":2,"100":3,"101":5,"103":7,"104":1,"106":5,"108":1,"110":4,"111":2,"112":1,"116":4,"117":1,"118":1,"119":2,"120":1,"124":1,"125":3,"126":4,"128":9,"131":1,"134":1,"135":4,"137":6,"140":1,"141":4,"146":6,"147":1,"148":3,"149":1,"150":1,"151":1,"153":1,"154":4,"155":2,"157":2,"158":2,"160":2,"161":3,"162":1,"163":2,"166":15,"168":2,"169":1,"170":10,"171":2,"173":1,"176":2,"177":1,"180":7,"182":1,"183":3,"186":2,"187":16,"188":6,"189":5,"190":4,"193":5,"194":1,"195":2,"198":1,"199":2,"200":1,"201":1,"203":1,"204":3,"207":3,"208":6,"209":8,"210":3,"211":8,"212":3,"213":1,"214":1,"216":4,"217":3,"218":9,"219":7,"220":8,"221":1,"222":4,"223":1,"224":2,"226":1,"227":1,"229":1,"230":3,"231":3,"232":2,"233":5,"234":2,"235":2,"236":1,"238":10,"241":2,"242":1,"243":2,"244":1,"245":2,"247":6,"248":7,"249":4,"250":3,"251":1,"252":6,"253":3,"254":1,"255":1,"256":3,"259":6,"260":1,"262":1,"263":6,"264":3,"265":3,"266":5,"268":6,"271":2,"272":6,"273":5,"274":1,"277":1,"280":2,"281":2,"282":1,"283":5,"284":1,"285":2,"286":2,"287":3,"600":1,"665":6,"666":1,"667":6,"670":1,"672":2,"675":1,"676":2,"677":1,"680":1,"683":1,"684":1,"686":1,"693":1,"697":1,"699":6,"701":1,"716":5,"717":1,"725":1,"728":1,"729":1,"732":1,"733":1,"734":2,"741":1,"749":1,"753":1,"755":1,"756":1,"759":1,"760":1,"775":3}}],["iteration",{"2":{"226":1,"253":2}}],["items",{"2":{"69":1}}],["itself",{"2":{"72":1,"135":1,"163":1,"187":1,"734":1,"775":2}}],["its",{"2":{"18":1,"32":1,"81":1,"106":1,"127":2,"135":1,"136":1,"137":1,"146":1,"168":1,"180":1,"186":1,"187":2,"207":2,"208":1,"211":1,"218":1,"220":1,"221":1,"230":1,"232":1,"233":1,"238":1,"247":1,"249":1,"256":1,"261":1,"264":1,"272":1,"671":1,"677":1,"719":1,"734":1,"753":1}}],["it",{"0":{"1":1,"97":1},"1":{"2":1,"3":1,"4":1},"2":{"0":1,"3":1,"4":1,"5":4,"6":5,"14":4,"16":4,"17":2,"18":1,"28":2,"38":1,"41":1,"42":1,"61":1,"63":2,"65":3,"69":1,"72":1,"75":1,"81":2,"82":1,"84":1,"88":5,"89":2,"90":2,"94":3,"97":1,"98":3,"99":1,"101":3,"102":1,"103":2,"104":3,"105":4,"106":1,"109":1,"110":2,"116":1,"125":5,"126":1,"127":1,"128":5,"136":2,"137":6,"141":6,"146":3,"148":1,"150":2,"154":3,"155":1,"159":1,"160":1,"161":2,"163":3,"166":10,"168":2,"169":1,"170":4,"180":4,"182":1,"186":1,"187":7,"188":3,"189":6,"190":2,"193":1,"199":6,"200":6,"201":8,"202":2,"203":2,"204":2,"206":1,"207":3,"208":2,"209":3,"210":5,"211":1,"216":2,"217":2,"218":2,"219":6,"220":4,"222":1,"226":1,"229":1,"230":1,"232":2,"234":2,"238":4,"241":2,"243":2,"245":1,"247":2,"248":3,"249":3,"250":1,"251":1,"252":1,"253":10,"254":1,"256":2,"259":4,"261":1,"262":1,"263":4,"264":2,"266":1,"268":7,"270":1,"271":1,"272":2,"273":8,"279":1,"280":3,"281":2,"282":3,"283":2,"286":2,"665":5,"671":1,"674":2,"676":1,"680":1,"681":1,"682":1,"686":2,"687":1,"710":2,"711":2,"717":1,"719":1,"720":1,"721":1,"729":1,"734":1,"756":1,"764":1}}],["inherent",{"2":{"668":2}}],["inline",{"2":{"633":1}}],["inner",{"2":{"497":1}}],["inactive",{"2":{"230":1}}],["inadvertently",{"2":{"97":1,"268":1}}],["inject",{"2":{"186":1}}],["invariantviolation",{"2":{"459":1}}],["invalidsegment",{"2":{"483":1}}],["invalidparametererror",{"0":{"464":1},"2":{"459":1}}],["invalidparameter",{"2":{"459":1}}],["invalid",{"2":{"171":1,"273":1}}],["invite",{"2":{"225":1}}],["invoking",{"2":{"685":1}}],["invoked",{"2":{"689":1}}],["invoke",{"2":{"166":1}}],["involving",{"2":{"249":1}}],["involved",{"2":{"101":1,"137":1,"189":1,"200":1,"219":1,"771":2}}],["involves",{"2":{"13":1,"107":1}}],["invocation",{"2":{"238":1}}],["ineffective",{"2":{"137":1,"247":1}}],["influenced",{"2":{"262":1}}],["inflates",{"2":{"252":1}}],["infamous",{"2":{"249":1}}],["infrastructure",{"2":{"101":1,"260":2}}],["infinitely",{"2":{"128":4,"164":4,"190":3,"201":2,"486":1}}],["infinite",{"2":{"97":1,"212":1,"222":2}}],["infinitesimally",{"2":{"88":1}}],["information",{"2":{"14":1,"28":1,"32":1,"39":2,"65":4,"70":1,"72":1,"85":1,"106":4,"137":3,"154":1,"161":2,"166":1,"168":4,"169":1,"202":1,"216":1,"228":2,"253":1,"254":1,"255":1,"258":4,"259":5,"260":1,"261":1,"263":2,"264":1,"268":1,"287":1,"670":1,"672":1,"676":1,"700":1,"716":1,"727":1,"732":1,"733":1,"769":1,"777":1}}],["info",{"0":{"106":1},"2":{"13":1,"14":1,"42":2,"78":1,"82":1,"89":1,"90":1,"94":1,"97":2,"105":2,"120":1,"139":1,"140":1,"141":1,"143":3,"150":1,"153":1,"154":1,"157":1,"160":1,"164":2,"166":2,"171":1,"174":2,"177":1,"185":2,"186":3,"187":2,"193":1,"201":1,"208":1,"213":48,"214":1,"216":1,"217":1,"218":1,"220":2,"229":1,"230":1,"234":1,"235":1,"268":1,"272":1,"287":1,"473":1,"665":1,"701":1,"715":1,"733":1,"759":1,"764":1}}],["industry",{"2":{"245":1}}],["indicating",{"2":{"221":1,"287":3}}],["individuals",{"2":{"246":1,"247":1,"255":1,"259":2,"261":1}}],["individually",{"2":{"216":1}}],["individual",{"0":{"259":1},"2":{"65":1,"257":1}}],["indexed",{"2":{"772":2}}],["index",{"2":{"321":1,"325":1}}],["independently",{"2":{"259":1}}],["indeed",{"2":{"106":1,"256":1}}],["indefinitely",{"2":{"77":2,"84":1,"88":1,"93":2,"557":1}}],["inserted",{"2":{"51":2}}],["inside",{"2":{"39":1,"98":1,"101":1,"166":2,"248":1,"749":1}}],["instincts",{"2":{"218":1}}],["instructionexecution",{"2":{"610":1}}],["instructionexecutionfail",{"0":{"460":1},"2":{"610":1}}],["instructionexecutionerror",{"0":{"459":1},"2":{"630":1}}],["instructionevaluationerror",{"0":{"458":1},"2":{"459":1}}],["instructiontype",{"0":{"461":1},"2":{"458":1,"558":1}}],["instructionbox",{"0":{"457":1,"511":1,"639":1,"645":1},"2":{"218":4,"331":2,"371":1,"460":1,"505":1,"511":1,"530":2,"553":1,"570":1,"639":1,"645":1}}],["instructionfailed",{"2":{"205":2,"630":1}}],["instruction",{"0":{"162":1,"693":1},"2":{"35":1,"36":2,"40":2,"41":5,"45":1,"48":1,"52":3,"56":2,"59":2,"62":1,"65":1,"71":1,"72":2,"76":1,"78":1,"90":1,"94":1,"96":2,"97":2,"98":1,"104":3,"111":1,"113":2,"121":2,"128":1,"136":1,"141":2,"143":2,"162":2,"164":4,"170":2,"174":2,"188":5,"189":5,"190":8,"191":4,"193":4,"200":2,"208":1,"209":3,"212":1,"218":2,"219":1,"220":2,"221":2,"273":1,"460":1,"530":2,"558":1,"607":1,"669":1,"685":1,"698":1,"704":4,"705":4,"706":4,"708":4,"709":4,"712":4,"713":4,"714":4}}],["instructions",{"0":{"27":1,"40":1,"49":1,"689":1,"690":1,"691":1,"692":1,"698":1},"1":{"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"690":1,"691":1,"692":1,"693":1},"2":{"13":1,"16":4,"28":1,"36":1,"39":5,"40":7,"41":3,"42":1,"44":1,"45":2,"46":1,"47":1,"49":5,"52":1,"62":1,"71":2,"97":2,"98":2,"100":2,"101":1,"104":1,"121":1,"128":1,"129":1,"136":1,"141":3,"167":1,"168":3,"170":1,"175":1,"177":1,"178":1,"179":1,"188":2,"193":3,"197":1,"204":1,"218":4,"222":1,"224":1,"227":1,"233":2,"246":1,"262":1,"264":1,"265":2,"285":1,"371":1,"505":1,"570":1,"608":1,"669":4,"680":1,"684":1,"690":2,"691":2,"692":1,"693":3,"698":3}}],["instead",{"2":{"16":2,"17":1,"65":1,"87":1,"88":1,"90":1,"106":1,"107":1,"136":1,"137":3,"189":1,"197":1,"198":1,"209":1,"210":1,"212":1,"216":2,"219":1,"220":1,"222":1,"243":1,"252":1,"253":1}}],["instantaneous",{"2":{"287":1}}],["instantiation",{"2":{"273":1}}],["instantiate",{"2":{"162":1,"199":1,"217":2}}],["instances",{"2":{"137":3,"218":1,"734":1}}],["instance",{"0":{"95":1},"2":{"11":1,"18":1,"95":1,"154":1,"170":1,"186":1,"188":1,"217":1,"219":4,"221":2,"249":1,"272":1,"665":1,"671":1,"693":1}}],["installations",{"2":{"257":1,"280":1}}],["installation",{"0":{"186":1,"277":1,"280":1},"1":{"278":1,"279":1,"280":1,"281":1,"282":1,"283":1},"2":{"176":1,"177":1,"182":1,"186":2,"216":1,"277":1,"285":1}}],["installing",{"0":{"242":1},"2":{"157":1,"176":1,"177":1,"186":2,"238":1}}],["install",{"0":{"176":1,"179":1,"181":1,"182":1,"183":1},"1":{"180":1,"181":1,"182":2,"183":1,"184":1},"2":{"3":1,"5":8,"17":1,"175":1,"176":2,"178":1,"179":3,"181":1,"182":6,"186":4,"207":6,"213":2,"225":1,"243":1,"249":2,"282":2,"283":2}}],["installed",{"2":{"3":1,"175":1,"182":1,"248":1,"249":3,"280":1,"281":1,"282":3,"283":1}}],["initiate",{"2":{"767":1}}],["initiated",{"2":{"255":1}}],["initialize",{"2":{"187":1}}],["initialized",{"2":{"127":1,"272":1}}],["initialise",{"2":{"220":2}}],["initialised",{"2":{"186":1}}],["initialisation",{"2":{"6":1}}],["initially",{"2":{"100":1}}],["initial",{"2":{"20":2,"21":3,"22":1,"24":1,"216":1,"676":1}}],["init",{"2":{"18":8,"143":2,"164":2,"186":1,"193":4,"213":4}}],["introduction",{"0":{"223":1},"1":{"224":1,"225":1,"226":1},"2":{"229":1,"246":1}}],["introduced",{"2":{"238":1}}],["introduce",{"2":{"178":1,"222":1,"224":1}}],["intuitive",{"2":{"209":1}}],["int",{"2":{"198":2,"202":2,"203":2,"658":1,"659":1,"660":1,"661":1,"662":1,"663":1}}],["intentional",{"2":{"220":1}}],["intended",{"2":{"255":1,"259":1,"285":1}}],["intend",{"2":{"14":1,"97":2}}],["integrity",{"2":{"245":1}}],["integration",{"0":{"284":1},"2":{"193":1,"265":1,"284":1,"285":1}}],["integers",{"2":{"50":1,"190":1,"775":1}}],["integer",{"2":{"25":2,"84":1,"211":2}}],["interoperate",{"2":{"686":1}}],["interchangeable",{"2":{"673":1}}],["intercept",{"2":{"266":1}}],["interval",{"0":{"301":1,"302":1,"462":1,"463":1},"2":{"301":1,"302":1,"467":1,"469":1,"603":2}}],["inter",{"2":{"231":1}}],["interior",{"2":{"217":1}}],["interim",{"2":{"100":1}}],["interruptedexception",{"2":{"205":2}}],["interpreter",{"2":{"212":1}}],["interpreted",{"2":{"166":1}}],["interpret",{"2":{"193":1}}],["intermediate",{"2":{"180":1}}],["intermediary",{"2":{"13":1}}],["interacting",{"2":{"166":1}}],["interactions",{"2":{"71":1,"233":1,"257":1}}],["interaction",{"2":{"39":1,"199":1}}],["interact",{"2":{"166":1,"168":3,"187":1,"198":1,"224":1,"263":1}}],["interface",{"2":{"166":4,"168":1,"187":3,"193":2,"665":2}}],["interfaces",{"0":{"664":1},"1":{"665":1,"666":1,"667":1,"668":1},"2":{"103":1}}],["interesting",{"2":{"99":1}}],["interested",{"2":{"87":1,"214":1}}],["interest",{"2":{"65":1,"229":1,"279":1,"674":1}}],["internalerror",{"2":{"630":1}}],["internal",{"2":{"18":1,"149":1,"188":1,"211":1,"217":1,"282":1}}],["into",{"0":{"242":1},"2":{"3":1,"4":2,"13":1,"16":2,"17":3,"20":1,"28":1,"39":1,"53":4,"61":4,"71":5,"81":1,"92":8,"93":2,"100":1,"104":3,"107":1,"138":1,"167":1,"168":1,"178":1,"183":1,"186":2,"187":2,"190":1,"200":1,"207":2,"217":4,"218":5,"224":1,"238":1,"242":1,"246":2,"251":1,"261":1,"273":1,"282":1,"664":1,"667":4,"702":4,"703":4,"704":2,"705":6,"706":2,"707":2,"708":2,"709":2,"710":2,"711":2,"712":2,"713":2,"714":2}}],["increasing",{"2":{"252":1}}],["increases",{"2":{"105":2,"106":1}}],["increase",{"2":{"98":1,"252":1}}],["incidents",{"2":{"250":1}}],["incident",{"2":{"246":1,"250":1}}],["inclined",{"2":{"198":1}}],["including",{"2":{"4":1,"97":1,"135":1,"137":1,"183":1,"231":1,"246":1,"248":1,"252":1,"253":1,"268":1,"699":1}}],["includes",{"2":{"86":1,"112":1,"170":1,"235":1,"247":1,"696":1}}],["included",{"2":{"4":1,"149":1,"260":1}}],["include",{"2":{"4":1,"5":1,"42":1,"235":1,"249":1,"664":1,"691":1,"732":1}}],["incorrectly",{"2":{"272":1,"755":1}}],["incorrect",{"2":{"135":1,"220":1}}],["incomplete",{"2":{"141":1}}],["incompatibility",{"2":{"6":1}}],["incoming",{"2":{"118":1,"119":1,"147":1,"680":2}}],["input",{"2":{"4":1,"5":1,"109":2,"193":2,"239":14,"263":1,"716":1,"749":1}}],["in",{"0":{"4":1,"6":1,"193":1,"205":1},"1":{"7":1,"8":1,"9":1,"194":1},"2":{"0":2,"2":1,"3":1,"4":1,"5":6,"6":3,"11":15,"13":3,"14":7,"16":4,"18":6,"20":3,"21":6,"22":4,"24":1,"25":3,"27":1,"28":6,"29":5,"32":1,"33":1,"36":1,"37":1,"38":1,"39":6,"40":1,"41":1,"42":9,"43":2,"44":1,"46":1,"49":3,"50":2,"51":2,"52":1,"55":2,"56":3,"57":5,"58":1,"60":1,"62":2,"63":3,"67":2,"71":3,"72":1,"78":2,"80":1,"81":1,"82":1,"83":1,"85":1,"86":1,"87":2,"88":3,"89":3,"92":1,"94":4,"96":1,"97":9,"98":1,"99":4,"100":3,"101":2,"102":1,"103":2,"104":4,"105":5,"106":5,"108":1,"112":1,"116":2,"118":1,"119":1,"120":1,"121":1,"122":1,"124":6,"125":7,"126":4,"127":4,"128":6,"130":4,"131":1,"132":1,"134":3,"135":4,"136":1,"137":11,"138":3,"139":2,"141":10,"143":9,"146":3,"147":1,"148":1,"149":1,"150":1,"151":1,"154":2,"157":1,"158":2,"159":1,"160":3,"161":1,"163":1,"164":6,"166":8,"168":2,"170":6,"171":4,"174":1,"176":1,"177":4,"178":3,"180":4,"182":1,"184":1,"186":7,"187":10,"188":2,"189":3,"190":6,"191":1,"193":8,"195":2,"197":3,"198":1,"199":6,"200":2,"201":3,"202":1,"203":1,"204":2,"207":4,"208":6,"209":7,"210":1,"211":4,"212":4,"214":2,"216":7,"217":8,"218":3,"219":3,"220":4,"222":6,"224":2,"225":1,"226":1,"229":4,"230":2,"231":2,"233":5,"234":1,"235":2,"236":2,"237":3,"238":7,"241":2,"243":1,"244":1,"245":1,"246":2,"247":2,"248":3,"249":6,"250":4,"251":1,"252":5,"253":7,"255":1,"256":3,"258":3,"259":1,"260":2,"263":2,"264":1,"265":6,"266":3,"268":3,"269":1,"270":2,"271":1,"272":4,"273":5,"278":1,"279":1,"280":1,"282":3,"283":1,"286":2,"287":2,"664":1,"665":7,"667":4,"668":6,"670":1,"671":2,"672":1,"673":2,"674":1,"677":1,"684":1,"686":2,"692":1,"693":2,"695":1,"696":1,"697":2,"698":1,"699":2,"700":2,"701":3,"708":9,"709":5,"710":5,"711":5,"712":9,"713":1,"715":1,"716":4,"717":2,"718":1,"719":1,"722":5,"725":1,"726":1,"729":1,"734":1,"739":1,"742":1,"743":1,"746":1,"747":1,"749":1,"752":1,"753":2,"755":3,"756":1,"762":1,"767":1,"769":2,"771":4,"772":4,"775":9}}],["r3",{"2":{"236":1}}],["risking",{"2":{"254":1}}],["risk",{"2":{"247":2,"248":1,"260":1,"267":1}}],["risks",{"2":{"230":1,"248":1,"250":1}}],["rights",{"2":{"249":1}}],["right",{"2":{"13":2,"16":1,"20":1,"21":1,"87":1,"273":1,"279":2,"298":1,"300":1,"324":1,"340":1,"347":1,"447":1,"472":1,"490":1,"491":1,"522":1,"530":1,"552":1,"602":1}}],["r",{"2":{"217":2}}],["rc",{"2":{"185":1,"216":8,"244":1}}],["rtx",{"2":{"253":4}}],["rtgs",{"0":{"236":1},"2":{"236":1}}],["rt",{"2":{"53":2}}],["rarity",{"2":{"674":1}}],["raiseto",{"0":{"552":1},"2":{"376":2}}],["raise",{"2":{"273":1}}],["raised",{"2":{"199":1,"253":1}}],["rabbit",{"2":{"170":5,"172":3,"189":6,"190":2,"210":7,"211":1,"219":10,"220":3,"222":1,"282":1}}],["randomly",{"2":{"697":1}}],["randomness",{"2":{"238":1,"252":1}}],["random",{"2":{"238":1,"256":1}}],["randomise",{"2":{"148":1}}],["range",{"2":{"238":1,"252":1,"674":1}}],["ran",{"2":{"138":1,"167":1,"282":1}}],["ram",{"2":{"106":1}}],["rather",{"2":{"46":1,"90":1,"166":1,"250":1,"254":1,"273":1,"665":1,"734":1}}],["rawgenesisblock",{"0":{"553":1},"2":{"702":2,"703":2}}],["raw",{"2":{"2":2,"5":6,"188":2,"189":2,"190":6,"191":4,"193":2,"272":2,"376":1,"445":1,"615":1,"665":2}}],["rudimentary",{"2":{"233":1}}],["rules",{"2":{"259":1}}],["rule",{"2":{"42":2,"220":1,"286":1}}],["rustpub",{"2":{"666":1,"755":1,"757":1}}],["rustfor",{"2":{"222":1}}],["rustup",{"2":{"176":5,"279":2,"280":7,"281":2,"282":12}}],["rustuse",{"2":{"94":1,"217":1,"222":1}}],["rustconst",{"2":{"707":1}}],["rustc",{"2":{"176":1,"279":5,"280":2,"282":6}}],["rustimpl",{"2":{"668":1}}],["rustiroha",{"2":{"174":1,"218":2,"221":1}}],["rustinstruction",{"2":{"96":1}}],["rustaccount",{"2":{"170":2}}],["rustdomain",{"2":{"169":1}}],["rustenum",{"2":{"77":1}}],["ruststruct",{"2":{"73":1,"75":1,"775":1}}],["rustlet",{"2":{"60":1,"61":1,"66":2,"67":1,"71":1,"92":1,"93":1,"94":2,"95":2,"219":2,"702":1,"703":1,"704":1,"705":1,"706":1,"708":1,"709":1,"710":1,"711":1,"712":1,"713":1,"714":1}}],["rust",{"0":{"102":1,"176":1,"215":1,"278":1,"279":1,"281":1,"282":1},"1":{"103":1,"104":1,"216":1,"217":1,"218":1,"219":1,"220":1,"221":1,"222":1,"279":1,"280":1,"281":1,"282":1},"2":{"27":1,"29":1,"42":1,"43":2,"52":1,"53":1,"57":1,"92":1,"97":1,"101":1,"102":1,"103":2,"104":1,"106":2,"107":1,"108":1,"136":1,"171":1,"175":1,"176":3,"177":1,"178":2,"188":1,"193":1,"199":1,"200":1,"204":1,"207":1,"209":3,"211":1,"212":1,"216":5,"217":1,"218":1,"222":1,"225":1,"229":1,"231":1,"278":1,"281":1,"282":2,"665":2,"666":1,"667":6,"678":1,"716":2,"772":1}}],["runs",{"2":{"671":1}}],["runblocking",{"2":{"199":5}}],["runtimeexception",{"2":{"202":2}}],["runtime",{"0":{"63":1},"2":{"53":2,"63":3,"101":1,"111":1,"143":2,"164":2,"186":1,"232":1}}],["running",{"2":{"11":1,"13":1,"16":1,"18":1,"22":1,"162":1,"168":1,"171":1,"174":1,"213":2,"230":1,"234":1,"244":4,"253":1,"264":1,"272":1,"282":1,"693":1,"753":1,"766":1}}],["run",{"0":{"18":1},"2":{"0":1,"6":1,"13":2,"14":3,"16":4,"18":2,"20":1,"21":3,"22":2,"39":1,"41":1,"88":5,"89":1,"100":1,"104":4,"134":3,"141":1,"166":2,"168":5,"169":1,"170":6,"171":2,"174":1,"176":1,"177":1,"178":1,"183":1,"184":1,"186":2,"193":6,"213":1,"232":1,"238":3,"239":8,"241":2,"243":1,"279":1,"280":1,"281":1,"282":2,"283":1,"680":1,"684":1}}],["rm",{"2":{"7":2}}],["rotation",{"2":{"681":2}}],["roadmap",{"2":{"250":1}}],["rogue",{"2":{"249":1}}],["roses",{"2":{"211":2,"220":7,"221":6}}],["rose",{"2":{"128":5,"164":4,"171":2,"192":2,"201":2,"202":2,"211":2,"702":14,"703":14}}],["routing",{"0":{"776":1}}],["routine",{"2":{"245":1,"248":1,"260":1}}],["route",{"2":{"680":1}}],["round",{"2":{"88":1,"97":1,"128":2,"187":1,"220":1,"769":1,"771":2,"775":2}}],["roughly",{"2":{"73":1,"193":1}}],["roleeventfilter",{"0":{"397":1,"562":1},"2":{"397":1,"563":1}}],["roleevent",{"0":{"393":1,"528":1,"561":1},"2":{"339":1,"393":1,"563":1}}],["rolefilter",{"0":{"398":1,"563":1},"2":{"338":1,"398":1}}],["rolegranted",{"2":{"290":1}}],["rolerevoked",{"2":{"290":1}}],["roleid",{"0":{"360":1,"564":1,"596":1},"2":{"60":2,"289":1,"295":1,"431":1,"434":1,"454":2,"528":1,"538":1,"560":1,"561":1,"596":1,"720":1,"721":1}}],["role",{"0":{"60":1,"61":1,"560":1,"717":1},"1":{"718":1,"719":1,"720":1,"721":1},"2":{"41":1,"45":1,"53":43,"56":1,"59":3,"60":15,"61":9,"71":10,"132":1,"160":2,"237":1,"246":1,"260":1,"295":1,"339":1,"431":1,"455":2,"497":1,"538":1,"555":1,"561":1,"720":3,"721":1}}],["roles",{"0":{"59":1},"1":{"60":1,"61":1},"2":{"29":1,"33":1,"41":3,"42":1,"45":2,"53":2,"56":1,"59":1,"64":2,"111":2,"128":1,"129":1,"160":2,"170":4,"260":1,"289":1,"700":1,"717":1,"718":2,"719":2,"720":1,"721":1}}],["root",{"2":{"6":2,"134":1,"170":1,"238":1,"243":1}}],["robust",{"2":{"0":1,"232":1,"238":1,"259":1,"665":1,"667":2}}],["rsiroha",{"2":{"220":1}}],["rslet",{"2":{"217":1,"220":1}}],["rsuse",{"2":{"217":1,"218":1,"219":1}}],["rsprintln",{"2":{"4":1}}],["rsfn",{"2":{"4":1}}],["rs",{"2":{"2":2,"4":4,"104":1,"176":2,"216":1,"217":1,"218":4,"220":3,"221":1}}],["reuse",{"2":{"259":1}}],["reinstate",{"2":{"250":1}}],["reinvent",{"2":{"101":1}}],["regulations",{"2":{"260":1}}],["regularly",{"2":{"260":2}}],["regular",{"2":{"243":1,"248":1,"253":1,"258":1,"260":1}}],["regarding",{"2":{"671":1}}],["regard",{"2":{"256":1}}],["regardless",{"2":{"220":1,"223":1,"282":1}}],["registry=https",{"2":{"186":2}}],["registry",{"2":{"186":4}}],["registration",{"2":{"42":1,"43":1,"57":1,"69":1,"170":1,"200":1,"218":4,"220":4}}],["registrablebox",{"0":{"359":1,"555":1},"2":{"554":1}}],["registrable",{"2":{"42":2}}],["registerassettx",{"2":{"205":12}}],["registerasset",{"2":{"201":6,"202":2}}],["registerassetdefinition",{"2":{"201":6,"205":6}}],["registeraccount",{"2":{"200":6,"202":2,"205":2}}],["registeraccountinstructioncommitted",{"2":{"205":2}}],["registeraccountinstruction",{"2":{"189":2}}],["registerdomaininstructioncommitted",{"2":{"205":2}}],["registerdomain",{"2":{"186":2,"188":4,"193":4,"199":6,"205":2}}],["registers",{"2":{"160":1}}],["registerbox",{"0":{"554":1},"2":{"52":2,"53":4,"60":2,"72":1,"92":8,"96":2,"104":2,"188":8,"189":4,"190":4,"193":4,"218":8,"219":2,"220":2,"457":1}}],["registering",{"0":{"141":1,"159":1,"169":1,"170":1,"171":1,"188":1,"189":1,"190":1,"199":1,"200":1,"201":1,"209":1,"210":1,"211":1,"218":1,"219":1,"220":1},"1":{"160":1,"161":1,"162":1},"2":{"27":1,"29":1,"41":1,"42":6,"52":2,"53":4,"128":1,"129":1,"141":7,"160":3,"189":2,"199":1,"200":2,"201":1,"210":2,"218":1,"219":2,"714":2}}],["registered",{"2":{"25":1,"27":1,"36":2,"41":1,"42":4,"43":1,"57":1,"59":1,"61":1,"69":1,"72":1,"78":1,"84":1,"86":1,"97":3,"111":5,"116":1,"128":1,"129":1,"141":1,"146":2,"159":1,"168":1,"170":3,"171":1,"187":2,"189":1,"199":2,"200":1,"202":1,"220":2,"256":1,"689":1,"718":1,"719":1,"723":1,"726":1,"729":1,"739":1,"749":1,"750":1,"771":2}}],["register",{"0":{"42":1,"60":1,"92":1,"93":1,"136":1},"2":{"22":1,"29":6,"39":1,"40":2,"41":9,"42":8,"52":2,"53":11,"60":3,"72":2,"78":1,"81":1,"83":1,"84":1,"85":1,"93":1,"96":2,"98":1,"104":2,"111":1,"128":16,"129":1,"136":2,"141":4,"146":1,"162":2,"164":16,"169":4,"170":7,"171":4,"178":2,"188":6,"189":3,"190":4,"193":4,"199":1,"200":2,"201":1,"202":1,"209":9,"210":7,"218":2,"219":1,"220":5,"225":2,"256":2,"457":1,"461":1,"698":1,"701":1,"702":1,"714":9}}],["region",{"2":{"4":3}}],["reducing",{"2":{"253":1,"687":1}}],["reduced",{"2":{"94":1}}],["reduces",{"2":{"93":1,"94":1,"105":2}}],["reduce",{"2":{"5":1,"67":1,"105":2,"107":1,"109":1,"110":1,"137":1,"664":1,"734":1}}],["redundancy",{"2":{"250":1}}],["redacted",{"2":{"161":1}}],["rendering",{"2":{"137":1}}],["reflected",{"2":{"670":1}}],["reflect",{"2":{"226":1,"260":1}}],["reflects",{"2":{"220":1}}],["ref",{"2":{"193":4}}],["refresh",{"2":{"93":2}}],["refers",{"2":{"44":1}}],["reference",{"0":{"70":1},"2":{"13":1,"58":1,"124":1,"125":1,"143":1,"147":1,"151":1,"155":1,"177":1,"193":1,"200":1,"256":1,"265":2,"716":1}}],["refer",{"2":{"12":1,"13":1,"27":1,"36":1,"41":1,"42":1,"43":2,"44":1,"139":1,"140":1,"141":1,"143":2,"164":3,"186":1,"193":1,"671":1,"700":1}}],["retrieved",{"2":{"287":1}}],["retrieve",{"2":{"238":1}}],["retrying",{"2":{"151":1}}],["retry",{"2":{"143":4,"151":2,"164":4}}],["retain",{"2":{"97":1,"222":1}}],["retains",{"2":{"14":1}}],["returned",{"2":{"766":1,"772":1}}],["return",{"2":{"63":1,"67":2,"68":1,"168":1,"193":8,"202":2,"218":1,"716":2,"719":1,"720":1,"721":1,"772":2}}],["returns",{"2":{"62":1,"72":1,"220":1,"716":4,"718":2,"719":2,"720":2,"721":2,"723":1,"724":2,"726":1,"727":2,"728":2,"729":2,"730":3,"731":2,"733":2,"734":2,"735":2,"736":1,"737":2,"738":1,"739":2,"740":1,"741":2,"742":1,"743":1,"744":1,"746":2,"747":2,"748":1,"750":3,"751":1,"752":2,"754":2,"755":2,"757":2,"758":2,"760":1,"761":1,"762":1,"763":1,"776":1}}],["requiring",{"2":{"243":1,"259":1}}],["requirement",{"2":{"243":1}}],["requirements",{"2":{"187":4,"195":4}}],["requires",{"2":{"137":1,"256":1,"269":1,"665":1}}],["required",{"2":{"62":1,"65":1,"143":2,"147":1,"151":1,"252":2,"265":1,"266":1,"268":1}}],["require",{"2":{"41":1,"52":1,"63":1,"265":1,"266":1}}],["requested",{"2":{"189":1,"210":1,"219":1}}],["requestwithquerybox",{"2":{"188":2,"192":6}}],["requests",{"2":{"18":1,"148":1,"186":1,"193":1,"259":1}}],["request",{"2":{"11":2,"50":1,"53":4,"67":2,"120":2,"150":2,"161":1,"187":3,"188":2,"195":2,"220":1,"238":1,"270":1,"680":1,"686":1,"694":1,"766":1,"767":1,"775":1}}],["rejection",{"2":{"32":1,"757":2}}],["rejectedtransaction",{"2":{"757":2}}],["rejected",{"2":{"32":2,"98":1,"193":4,"204":1,"212":1,"218":1,"546":1,"547":1,"757":2,"769":3,"771":2,"775":6,"777":2}}],["remotely",{"2":{"263":1}}],["remote",{"2":{"263":6}}],["removable",{"2":{"249":1}}],["removes",{"2":{"105":2}}],["removed",{"2":{"45":1,"51":2,"56":1,"137":1,"249":1,"264":1,"266":1,"315":1,"534":1}}],["removekeyvaluebox",{"0":{"556":1},"2":{"457":1}}],["removekeyvalue",{"0":{"46":1},"2":{"40":1,"41":1,"52":1,"457":1,"461":1,"698":1}}],["remove",{"0":{"106":1},"2":{"6":1,"7":1,"39":1,"40":1,"55":1,"98":1,"105":2,"108":1,"110":1,"128":1,"163":1,"232":2,"249":1,"265":1,"282":4,"698":1,"701":3,"709":5,"711":5,"713":9}}],["remain",{"2":{"251":1,"266":1,"267":1,"272":1}}],["remains",{"2":{"249":1,"250":1,"254":1,"261":1}}],["remaining",{"2":{"20":1,"98":1,"234":1}}],["remembering",{"2":{"269":1}}],["remembers",{"2":{"263":1}}],["remember",{"2":{"20":1,"189":1,"209":1,"210":1,"212":1,"219":1,"249":1,"250":1,"729":1}}],["reach",{"2":{"253":1}}],["reaches",{"2":{"84":2,"163":1}}],["react",{"2":{"250":1}}],["reasonable",{"2":{"268":1}}],["reasons",{"2":{"42":1,"154":1,"163":1}}],["reasoning",{"2":{"28":1}}],["reason",{"2":{"28":2,"32":1,"62":1,"79":1,"88":1,"97":1,"148":1,"155":1,"187":1,"193":2,"231":1,"272":1,"460":1,"606":1,"653":1,"757":2}}],["readily",{"2":{"716":2}}],["readiness",{"2":{"250":1}}],["reading",{"2":{"125":1}}],["readers",{"2":{"287":1}}],["reader",{"2":{"217":2,"222":1}}],["reads",{"2":{"198":1}}],["readable",{"2":{"150":1}}],["read",{"2":{"16":1,"39":1,"40":1,"178":1,"183":1,"208":2,"225":1,"253":1,"268":1,"694":1,"764":1}}],["ready",{"2":{"16":2,"34":1,"198":1,"265":1}}],["realm",{"2":{"237":1,"251":1}}],["really",{"2":{"18":1,"88":1,"89":1,"253":1}}],["real",{"0":{"22":1},"2":{"0":1,"4":1,"16":1,"22":1,"88":1,"183":1,"261":1}}],["re",{"0":{"108":1},"2":{"16":2,"39":2,"79":1,"88":2,"90":1,"108":2,"120":1,"132":1,"137":2,"150":1,"176":1,"177":1,"187":1,"213":1,"217":1,"218":1,"229":1,"244":1,"256":1,"263":1,"279":1,"282":1,"717":1}}],["reset",{"2":{"272":1}}],["research",{"2":{"268":1}}],["researching",{"2":{"259":1}}],["reserved",{"2":{"74":1,"261":1,"699":5}}],["resilient",{"2":{"238":1}}],["resident",{"2":{"198":1}}],["resistant",{"2":{"18":1}}],["resource",{"2":{"238":1}}],["resources",{"2":{"137":1,"258":1,"259":1,"260":1,"671":1}}],["resolve",{"2":{"6":1}}],["respond",{"2":{"79":1,"250":1,"263":1,"769":2}}],["responsible",{"2":{"78":1,"119":1,"160":1,"679":1}}],["responsibility",{"2":{"63":1,"160":1,"208":1}}],["responses",{"2":{"246":1,"766":1,"770":1,"771":1,"772":1,"775":2,"777":1}}],["response",{"2":{"65":1,"250":1,"263":2,"268":2,"287":1,"772":1,"775":3}}],["respectively",{"2":{"279":1,"716":1}}],["respective",{"2":{"17":1,"186":1}}],["restore",{"2":{"272":1}}],["restored",{"2":{"272":1}}],["restricted",{"2":{"260":1}}],["restrict",{"2":{"137":1,"141":1,"249":1,"260":1}}],["restriction",{"2":{"220":2}}],["restrictions",{"2":{"42":1,"137":1}}],["restricting",{"2":{"89":1}}],["rest",{"2":{"92":1,"220":1,"247":1}}],["restart$",{"2":{"9":1}}],["restarting",{"2":{"7":1,"8":1,"9":1}}],["restarted",{"2":{"6":1}}],["restart",{"2":{"6":3,"7":1,"8":1,"9":2,"128":2}}],["resulting",{"2":{"252":1}}],["results",{"0":{"266":1},"2":{"50":1,"65":1,"68":1,"69":1,"104":1,"125":1,"137":1,"188":1,"282":1,"715":1,"772":7}}],["result",{"2":{"11":1,"14":1,"53":2,"67":5,"127":1,"135":1,"187":2,"188":8,"190":1,"192":18,"201":1,"202":1,"211":1,"238":1,"286":1}}],["recurring",{"2":{"260":1}}],["recipient",{"2":{"255":1}}],["recipes",{"2":{"5":4}}],["recreating",{"2":{"193":1}}],["receipt",{"2":{"755":2}}],["receiving",{"2":{"217":1}}],["receive",{"0":{"270":1},"2":{"56":1,"169":1,"170":1,"680":1,"733":1,"769":1}}],["received",{"2":{"46":1,"259":2}}],["recent",{"2":{"201":1}}],["recently",{"2":{"169":1}}],["reconnect",{"2":{"268":1}}],["recognize",{"2":{"260":1}}],["recognise",{"2":{"259":1}}],["recovery",{"0":{"250":1},"2":{"250":4,"253":1}}],["records",{"2":{"670":2}}],["record",{"2":{"137":4,"249":2,"670":1,"687":1}}],["recorded",{"2":{"39":1,"106":1}}],["recommendations",{"2":{"252":2,"327":1}}],["recommends",{"2":{"206":1}}],["recommended",{"0":{"248":1},"2":{"16":1,"17":1,"82":1,"224":1,"264":1,"283":1,"693":1}}],["recommend",{"2":{"10":1,"16":1,"67":1,"101":1,"171":1,"180":1,"186":2,"188":1,"216":3,"264":1,"286":1}}],["recommited",{"2":{"7":1,"128":1}}],["recommit",{"0":{"7":1},"2":{"6":1,"7":1,"128":1}}],["recall",{"2":{"36":1,"729":1}}],["reputation",{"0":{"687":1},"1":{"690":1},"2":{"669":1,"687":1}}],["reputable",{"2":{"249":1}}],["reporting",{"2":{"775":2}}],["reports",{"2":{"771":1}}],["reported",{"2":{"250":1}}],["report",{"2":{"250":1,"260":1,"283":1}}],["repository",{"2":{"2":1,"177":1,"183":2,"186":4,"197":1,"207":2,"777":1}}],["repositories",{"2":{"0":1,"5":1,"197":3}}],["repairs",{"2":{"137":1}}],["repetitive",{"2":{"263":1}}],["repetitionerror",{"0":{"558":1},"2":{"459":1}}],["repetition",{"0":{"84":1},"2":{"77":1,"81":1,"459":1}}],["repetitions",{"2":{"5":1,"41":2,"43":1,"84":2,"98":1}}],["repeatedly",{"2":{"123":1}}],["repeated",{"2":{"84":2}}],["repeat",{"2":{"84":1,"98":2,"170":1}}],["repeats",{"0":{"77":1,"557":1},"2":{"75":4,"77":3,"88":1,"93":4,"95":2,"296":2,"297":2}}],["reprc",{"2":{"667":6}}],["represent",{"2":{"775":1}}],["represented",{"2":{"287":1,"665":1,"666":1}}],["represents",{"2":{"251":1,"287":1,"667":1}}],["representation",{"2":{"42":1,"112":1,"120":1,"134":1,"150":1,"174":1,"273":1,"672":1,"696":1}}],["reproducible",{"2":{"234":1,"248":1}}],["reproduced",{"2":{"28":1}}],["reproduce",{"2":{"28":2}}],["repr",{"2":{"103":1,"665":1,"667":1}}],["replacing",{"2":{"18":1,"166":1,"250":1}}],["replaced",{"2":{"63":1,"103":1,"137":1}}],["replace",{"2":{"16":2,"18":1,"20":1,"21":1,"197":1,"218":1}}],["replicate",{"2":{"13":1,"106":1}}],["revolves",{"2":{"665":1}}],["revokebox",{"0":{"559":1},"2":{"457":1}}],["revoked",{"2":{"59":1,"693":1,"697":1}}],["revoke",{"0":{"45":1},"2":{"40":1,"41":3,"45":2,"56":1,"59":1,"457":1,"461":1,"698":1}}],["reviewed",{"2":{"260":2}}],["review",{"2":{"250":1}}],["revisions",{"2":{"5":2}}],["reverse",{"2":{"117":1}}],["rev",{"2":{"5":8}}],["relevant",{"2":{"250":1}}],["released",{"2":{"180":3}}],["releases",{"2":{"99":1,"272":1}}],["release$",{"2":{"22":1,"177":1}}],["releasecargo",{"2":{"14":1,"104":1}}],["release",{"2":{"6":4,"14":4,"16":2,"22":4,"104":1,"105":2,"106":1,"134":2,"170":4,"174":2,"177":1,"180":1,"183":3,"187":1,"197":1,"216":1,"226":1,"238":2,"256":1}}],["rely",{"2":{"187":1}}],["reliable",{"2":{"235":1}}],["reliability",{"2":{"97":1}}],["relies",{"2":{"103":1}}],["relatable",{"2":{"260":1}}],["relationship",{"2":{"29":3}}],["relatively",{"2":{"201":1,"218":1}}],["relative",{"2":{"2":1}}],["related",{"2":{"25":1,"33":1,"86":1,"103":1,"107":1,"108":1,"137":2,"168":16,"174":1,"199":1,"238":1,"249":2,"263":2,"268":1,"669":1,"681":1,"690":1,"692":1,"775":1}}],["reloading",{"2":{"6":1}}],["reload",{"0":{"6":1},"1":{"7":1,"8":1,"9":1},"2":{"6":1,"244":2}}],["reloaded",{"2":{"3":1}}],["mnemonic",{"0":{"269":1},"2":{"269":1}}],["mv",{"2":{"242":2,"243":2}}],["m1",{"2":{"177":1}}],["m",{"2":{"130":3,"283":1}}],["ml",{"2":{"128":8,"164":8}}],["msg",{"2":{"475":1}}],["ms",{"2":{"113":4,"122":4,"143":16,"151":2,"164":20,"170":4,"174":4,"325":2,"608":2,"707":6,"771":6}}],["myself",{"2":{"220":1}}],["mypy",{"2":{"211":1}}],["my",{"2":{"83":1}}],["mirror",{"2":{"715":1}}],["migration",{"2":{"272":1}}],["might",{"2":{"3":1,"6":1,"65":1,"105":1,"120":1,"141":1,"150":1,"166":3,"183":1,"187":1,"213":1,"214":1,"217":2,"218":1,"220":1,"268":1,"272":1,"278":1,"769":2,"775":1}}],["mitnick",{"2":{"253":1}}],["mitigating",{"2":{"260":1}}],["mitigation",{"2":{"247":1}}],["mitigate",{"2":{"250":1}}],["mitigated",{"2":{"216":1,"268":1}}],["mismatch",{"0":{"487":1,"488":1,"489":1},"2":{"272":1,"626":3}}],["mistakes",{"2":{"273":1,"667":1}}],["mistake",{"2":{"220":1}}],["missingsegment",{"2":{"483":1}}],["missing",{"2":{"135":1,"287":1}}],["milliseconds",{"2":{"57":1,"122":1}}],["mix",{"2":{"20":1,"21":1}}],["mind",{"2":{"178":1,"225":1,"235":1,"249":1,"252":2,"699":1}}],["mindful",{"2":{"20":1,"21":1,"82":1,"249":2,"259":1,"716":1}}],["min",{"2":{"143":4,"164":4,"471":1}}],["minimizing",{"2":{"267":1}}],["minimizes",{"2":{"250":1}}],["minimise",{"2":{"186":1,"233":1,"248":1}}],["minimum",{"2":{"135":1,"151":1,"216":1,"252":1}}],["minimalist",{"0":{"231":1},"2":{"231":1,"249":1}}],["minimal",{"0":{"19":1},"1":{"20":1,"21":1},"2":{"216":1,"766":1}}],["minutes",{"2":{"88":1,"177":1}}],["minute",{"2":{"88":1,"177":1}}],["mintunmintable",{"2":{"485":1}}],["mintabilityerror",{"0":{"485":1},"2":{"459":1}}],["mintability",{"2":{"459":1}}],["mintabilitychanged",{"2":{"309":1}}],["mintable=false",{"2":{"211":2}}],["mintable",{"0":{"486":1,"675":1,"676":1},"2":{"24":2,"25":2,"43":1,"128":4,"141":1,"164":4,"190":12,"199":2,"201":11,"211":3,"220":5,"308":2,"494":2,"675":1,"676":1,"734":1}}],["mintasset",{"2":{"205":2}}],["mintassettx",{"2":{"205":4}}],["mintassetinstructioncommitted",{"2":{"205":2}}],["mintbox",{"0":{"484":1},"2":{"92":8,"190":4,"220":7,"457":1}}],["mint",{"0":{"43":1},"2":{"36":1,"40":3,"41":5,"43":1,"44":1,"56":1,"84":1,"98":1,"128":5,"164":4,"170":1,"171":4,"190":8,"201":1,"211":8,"220":12,"457":1,"461":1,"698":2,"701":1,"702":9}}],["minting",{"0":{"171":1,"190":1,"201":1,"211":1,"220":1},"2":{"27":1,"41":1,"43":2,"129":1,"171":1,"172":1,"173":1,"191":1,"201":1,"203":1,"220":2,"221":1}}],["minted",{"2":{"27":1,"43":2,"171":1,"202":1,"211":2,"220":1}}],["mkdir",{"2":{"17":2,"167":2,"183":2}}],["merkle",{"0":{"683":1},"2":{"669":1}}],["merkletree",{"0":{"449":1,"477":1,"509":1},"2":{"325":1,"509":1}}],["mere",{"2":{"253":1}}],["merely",{"2":{"29":1,"665":1}}],["memorize",{"2":{"269":1}}],["memory",{"2":{"101":1,"112":1,"137":1,"143":2,"164":2,"174":1,"264":1,"268":1,"696":1}}],["menu",{"2":{"265":4}}],["mentioned",{"2":{"97":1}}],["medieval",{"2":{"670":1}}],["media",{"2":{"259":1,"270":1}}],["medical",{"2":{"137":1}}],["me",{"2":{"249":2}}],["mechanisms",{"2":{"263":1}}],["mechanism",{"2":{"249":1}}],["measure",{"2":{"250":1}}],["measures",{"0":{"248":1},"2":{"178":1,"245":1,"247":2,"249":3,"260":4,"268":1}}],["meantime",{"2":{"216":1}}],["meant",{"2":{"88":1,"89":1,"150":1,"180":1,"241":1}}],["meanings",{"2":{"716":1}}],["meaningful",{"2":{"125":2}}],["meaning",{"2":{"43":1,"62":1,"116":1,"126":1,"128":1,"188":1,"229":1,"272":1,"716":2}}],["means",{"2":{"16":1,"18":1,"20":1,"21":1,"41":1,"56":1,"84":1,"94":1,"125":1,"126":1,"135":1,"190":2,"211":1,"216":1,"220":1,"230":2,"249":1,"254":1,"256":1,"283":1,"671":1}}],["mean",{"2":{"6":1,"125":1,"753":1}}],["meanwhile",{"2":{"5":1}}],["messaging",{"2":{"249":1}}],["messages",{"2":{"153":3,"154":1,"217":1,"255":1,"259":3,"270":1,"273":1,"286":1,"767":1,"768":1}}],["message",{"2":{"6":1,"154":1,"168":2,"169":1,"170":1,"195":1,"205":2,"273":1,"377":1,"755":2,"767":1}}],["messy",{"2":{"16":1,"19":1}}],["met",{"2":{"138":1,"684":1}}],["metric",{"2":{"110":1}}],["metrics$",{"2":{"11":1}}],["metrics",{"0":{"10":1,"11":1,"12":1,"771":1},"1":{"11":1,"12":1},"2":{"10":1,"11":5,"120":4,"150":4,"232":1,"771":4}}],["meta",{"2":{"167":2}}],["metaphor",{"2":{"39":1}}],["metadatalimits",{"2":{"634":1}}],["metadataerror",{"0":{"483":1},"2":{"459":1}}],["metadatakey",{"2":{"431":1}}],["metadata=",{"2":{"211":2}}],["metadataremoved",{"2":{"51":1,"290":1,"309":1,"315":1,"342":1}}],["metadatainserted",{"2":{"51":1,"290":1,"309":1,"315":1,"342":1}}],["metadatachanged",{"0":{"51":1,"479":1,"480":1,"481":1,"482":1},"2":{"51":1,"290":2,"309":2,"315":2,"342":2}}],["metadata",{"0":{"50":1,"53":1,"80":1,"137":1,"478":1},"1":{"51":1,"52":1,"53":1,"54":1,"55":1},"2":{"25":2,"29":1,"40":1,"41":5,"50":5,"51":1,"53":7,"54":1,"57":1,"60":3,"65":1,"69":4,"75":4,"80":3,"93":4,"95":2,"101":1,"128":16,"137":16,"143":8,"164":24,"167":4,"169":4,"170":8,"188":6,"189":6,"190":6,"193":6,"199":8,"200":6,"201":6,"205":16,"218":9,"289":2,"296":2,"297":2,"308":2,"319":1,"341":2,"459":1,"493":2,"494":2,"495":2,"608":1,"634":1,"698":1,"710":5,"711":5,"728":1,"742":1,"743":1,"762":1,"772":2}}],["metal",{"0":{"13":1,"18":1},"1":{"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1},"2":{"13":2,"14":1}}],["methodname",{"2":{"668":2}}],["methods",{"2":{"65":1,"134":1,"186":1,"187":3,"231":1,"237":1,"244":1,"247":1,"262":1,"667":2}}],["method",{"2":{"17":1,"187":1,"190":1,"200":2,"201":3,"202":3,"203":2,"208":1,"243":1,"249":1,"253":1,"254":1,"263":1,"268":2,"269":1,"665":1,"668":2,"766":1,"770":1,"771":1,"772":1,"775":1,"777":1}}],["mozilla",{"2":{"249":1}}],["moving",{"0":{"243":1},"1":{"244":1},"2":{"248":1}}],["move",{"2":{"21":1,"242":1,"243":1}}],["month",{"2":{"180":1,"253":1}}],["monitoring",{"2":{"212":1,"246":1,"259":1,"260":2}}],["monitor",{"2":{"10":2,"37":1,"120":1,"137":1,"150":1,"168":1,"212":1,"266":1,"272":1}}],["moment",{"2":{"81":1,"725":1}}],["mouse",{"2":{"52":14,"53":32,"60":7,"61":5,"71":5,"191":3,"710":10,"711":10}}],["mount",{"2":{"8":1,"193":4}}],["mostly",{"2":{"238":1,"690":1}}],["most",{"2":{"18":2,"42":1,"56":2,"69":1,"100":1,"103":2,"105":1,"136":1,"137":1,"139":2,"148":1,"152":1,"163":1,"166":4,"182":1,"193":1,"197":1,"204":1,"207":1,"211":1,"216":1,"218":2,"222":2,"242":1,"243":1,"247":1,"248":2,"268":1,"271":1,"286":1,"665":1,"667":1,"722":2,"725":2,"749":1,"764":1}}],["mod",{"0":{"490":1},"2":{"376":2}}],["modular",{"2":{"232":1}}],["module",{"2":{"118":1,"119":1,"147":1,"154":1,"198":1,"212":1,"216":1,"232":1,"668":3,"679":1,"680":1,"715":1}}],["modules",{"0":{"688":1},"2":{"90":1,"186":3,"669":1,"678":1,"693":1}}],["modes",{"2":{"178":1}}],["modern",{"2":{"177":1,"252":1,"253":2}}],["model=",{"2":{"193":2}}],["models",{"2":{"188":1,"249":1}}],["model",{"0":{"29":1,"288":1},"1":{"289":1,"290":1,"291":1,"292":1,"293":1,"294":1,"295":1,"296":1,"297":1,"298":1,"299":1,"300":1,"301":1,"302":1,"303":1,"304":1,"305":1,"306":1,"307":1,"308":1,"309":1,"310":1,"311":1,"312":1,"313":1,"314":1,"315":1,"316":1,"317":1,"318":1,"319":1,"320":1,"321":1,"322":1,"323":1,"324":1,"325":1,"326":1,"327":1,"328":1,"329":1,"330":1,"331":1,"332":1,"333":1,"334":1,"335":1,"336":1,"337":1,"338":1,"339":1,"340":1,"341":1,"342":1,"343":1,"344":1,"345":1,"346":1,"347":1,"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"367":1,"368":1,"369":1,"370":1,"371":1,"372":1,"373":1,"374":1,"375":1,"376":1,"377":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"401":1,"402":1,"403":1,"404":1,"405":1,"406":1,"407":1,"408":1,"409":1,"410":1,"411":1,"412":1,"413":1,"414":1,"415":1,"416":1,"417":1,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":1,"425":1,"426":1,"427":1,"428":1,"429":1,"430":1,"431":1,"432":1,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":1,"441":1,"442":1,"443":1,"444":1,"445":1,"446":1,"447":1,"448":1,"449":1,"450":1,"451":1,"452":1,"453":1,"454":1,"455":1,"456":1,"457":1,"458":1,"459":1,"460":1,"461":1,"462":1,"463":1,"464":1,"465":1,"466":1,"467":1,"468":1,"469":1,"470":1,"471":1,"472":1,"473":1,"474":1,"475":1,"476":1,"477":1,"478":1,"479":1,"480":1,"481":1,"482":1,"483":1,"484":1,"485":1,"486":1,"487":1,"488":1,"489":1,"490":1,"491":1,"492":1,"493":1,"494":1,"495":1,"496":1,"497":1,"498":1,"499":1,"500":1,"501":1,"502":1,"503":1,"504":1,"505":1,"506":1,"507":1,"508":1,"509":1,"510":1,"511":1,"512":1,"513":1,"514":1,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1,"522":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1,"530":1,"531":1,"532":1,"533":1,"534":1,"535":1,"536":1,"537":1,"538":1,"539":1,"540":1,"541":1,"542":1,"543":1,"544":1,"545":1,"546":1,"547":1,"548":1,"549":1,"550":1,"551":1,"552":1,"553":1,"554":1,"555":1,"556":1,"557":1,"558":1,"559":1,"560":1,"561":1,"562":1,"563":1,"564":1,"565":1,"566":1,"567":1,"568":1,"569":1,"570":1,"571":1,"572":1,"573":1,"574":1,"575":1,"576":1,"577":1,"578":1,"579":1,"580":1,"581":1,"582":1,"583":1,"584":1,"585":1,"586":1,"587":1,"588":1,"589":1,"590":1,"591":1,"592":1,"593":1,"594":1,"595":1,"596":1,"597":1,"598":1,"599":1,"600":1,"601":1,"602":1,"603":1,"604":1,"605":1,"606":1,"607":1,"608":1,"609":1,"610":1,"611":1,"612":1,"613":1,"614":1,"615":1,"616":1,"617":1,"618":1,"619":1,"620":1,"621":1,"622":1,"623":1,"624":1,"625":1,"626":1,"627":1,"628":1,"629":1,"630":1,"631":1,"632":1,"633":1,"634":1,"635":1,"636":1,"637":1,"638":1,"639":1,"640":1,"641":1,"642":1,"643":1,"644":1,"645":1,"646":1,"647":1,"648":1,"649":1,"650":1,"651":1,"652":1,"653":1,"654":1,"655":1,"656":1,"657":1,"658":1,"659":1,"660":1,"661":1,"662":1,"663":1},"2":{"53":2,"60":2,"99":1,"103":1,"104":2,"185":1,"186":8,"187":6,"188":3,"189":2,"190":4,"191":2,"192":2,"193":8,"197":2,"209":4,"210":4,"211":4,"216":6,"217":4,"218":2,"219":4,"220":1,"222":2,"665":1}}],["mode",{"2":{"14":7,"22":1,"106":1,"141":1,"143":4,"164":4,"174":2,"199":1,"272":1}}],["modification",{"2":{"759":1}}],["modifications",{"2":{"6":2,"105":1}}],["modified",{"2":{"141":1,"693":1}}],["modifies",{"2":{"53":2}}],["modifying",{"2":{"39":1,"125":1,"693":1,"694":1}}],["modify",{"2":{"9":1,"39":1,"40":1,"170":1}}],["moreover",{"2":{"101":1,"104":1,"220":1,"665":1}}],["more",{"0":{"228":1},"2":{"0":1,"3":1,"13":2,"14":1,"18":1,"21":1,"24":1,"29":2,"36":1,"39":1,"42":1,"57":1,"65":1,"80":1,"81":1,"83":1,"88":1,"89":1,"97":4,"98":1,"99":1,"101":1,"103":1,"104":1,"107":1,"124":1,"132":1,"134":1,"137":1,"139":1,"140":1,"141":1,"151":2,"154":2,"155":1,"160":1,"170":2,"171":1,"187":2,"188":1,"189":2,"190":4,"200":2,"208":2,"211":4,"214":1,"218":1,"219":2,"220":2,"224":2,"228":2,"232":2,"233":1,"238":6,"246":1,"247":1,"252":2,"260":1,"261":1,"263":3,"268":1,"667":1,"675":1,"683":1,"685":1,"689":1,"694":1,"700":1,"716":1,"717":1,"722":1,"775":1,"777":1}}],["mut",{"2":{"666":4,"668":1,"702":2,"703":2,"714":2}}],["mutable",{"2":{"217":2,"668":1}}],["multiply",{"0":{"491":1},"2":{"376":2}}],["multiple",{"2":{"5":1,"62":2,"166":1,"186":1,"209":1,"242":1,"253":1,"287":1,"716":1}}],["multiformats",{"2":{"197":2}}],["multihash",{"0":{"273":1},"2":{"170":2,"197":2,"238":1,"239":10,"256":1}}],["multi",{"2":{"168":2,"217":1,"227":1,"231":1,"253":1,"273":2}}],["mundane",{"2":{"28":1}}],["must",{"2":{"18":1,"88":1,"97":1,"116":1,"121":1,"125":1,"132":1,"135":4,"137":2,"138":1,"159":1,"160":1,"161":1,"166":1,"168":1,"169":1,"186":1,"207":1,"209":1,"211":1,"212":1,"218":1,"238":2,"247":1,"248":1,"256":3,"667":1,"716":1,"749":1}}],["musl",{"2":{"6":6,"241":1}}],["much",{"2":{"14":1,"39":1,"46":1,"65":1,"88":1,"90":1,"97":3,"101":1,"137":1,"141":1,"154":1,"163":1,"168":1,"180":1,"211":1,"248":1,"273":1}}],["md`",{"2":{"5":2}}],["md",{"2":{"2":4,"4":1,"5":3,"764":1}}],["malware",{"2":{"258":1,"266":1}}],["malformed",{"2":{"208":1,"777":1}}],["malicious",{"2":{"163":1,"230":1,"245":1,"247":1,"252":1,"259":2,"264":1,"268":1,"677":2,"687":1}}],["maven",{"2":{"197":2}}],["mavencentral",{"2":{"197":2}}],["maxtransactionsinblock=512",{"2":{"128":2,"164":2}}],["maximumfaultypeersamount",{"2":{"755":2}}],["maximum",{"2":{"121":2,"146":1,"252":2,"699":1,"755":6}}],["max",{"0":{"153":1},"2":{"113":4,"121":4,"143":38,"152":1,"153":2,"164":42,"170":4,"174":4,"471":1,"474":2,"607":2,"699":1}}],["major",{"2":{"99":1,"138":2}}],["majority",{"2":{"86":1}}],["market",{"2":{"268":1}}],["marking",{"2":{"231":1}}],["mark",{"2":{"218":1}}],["markdown",{"0":{"4":1},"2":{"5":1}}],["march",{"2":{"91":1,"92":9}}],["madhatterasset",{"2":{"201":4,"202":4,"203":6}}],["madhatterkeypair",{"2":{"200":4,"202":2,"203":2}}],["madhatter",{"2":{"200":10,"201":10,"202":23,"203":24}}],["mad",{"2":{"91":1,"92":10,"93":7,"97":1,"104":3,"113":2,"117":2,"164":2,"170":13,"171":4,"172":3,"173":2,"256":4}}],["made",{"2":{"6":1,"28":1,"198":1,"208":1}}],["masks",{"2":{"65":1}}],["making",{"0":{"244":1},"2":{"53":2,"137":1,"211":1,"238":1,"243":1,"247":1}}],["makes",{"2":{"14":1,"16":1,"74":1,"88":1,"154":1,"160":1,"163":1,"188":1,"199":1,"216":1,"268":1,"273":1}}],["make",{"2":{"0":1,"6":1,"16":2,"20":1,"21":1,"22":2,"24":1,"97":3,"99":1,"135":1,"137":2,"146":1,"148":1,"166":2,"167":1,"168":4,"182":1,"187":2,"190":1,"199":1,"211":3,"214":1,"216":1,"231":1,"238":1,"242":1,"244":1,"254":1,"256":1,"260":1,"265":1,"268":1,"271":1,"272":1,"279":1,"282":1,"283":1,"667":1,"692":1}}],["matherror",{"0":{"476":1},"2":{"367":1,"459":1}}],["mathematically",{"2":{"230":2}}],["mathematical",{"2":{"36":1}}],["math",{"2":{"367":1,"459":1}}],["matrix",{"0":{"287":1},"2":{"287":5}}],["maturin",{"2":{"207":4}}],["matter",{"2":{"157":1,"190":1,"245":1}}],["matching",{"2":{"72":1,"128":1}}],["match",{"2":{"57":1,"68":1,"94":2,"97":1,"161":1,"197":1,"219":2,"222":2,"286":1,"736":1}}],["matches",{"2":{"38":1,"94":3,"263":1,"748":1}}],["mapof",{"2":{"199":2,"200":2,"201":2}}],["mapnamevalue",{"2":{"188":4,"189":4,"190":4,"193":4}}],["map",{"2":{"37":1,"169":2,"170":4,"188":6,"189":4,"190":4,"192":6,"193":6,"199":4,"200":4,"201":4,"212":1,"478":1,"588":1,"589":1,"590":1,"591":1,"592":1,"593":1,"732":1}}],["magic",{"2":{"28":2}}],["macos",{"2":{"182":1,"183":1}}],["macro",{"2":{"103":1}}],["macros",{"2":{"103":1}}],["machine",{"2":{"97":1,"106":1,"177":1,"260":1,"268":2,"671":1}}],["machines",{"2":{"18":1,"137":2,"260":1,"263":2}}],["mac",{"2":{"16":2,"213":1}}],["mangling",{"0":{"668":1},"2":{"667":1}}],["manipulate",{"2":{"261":1}}],["manipulating",{"2":{"13":1}}],["manufacturers",{"2":{"674":1}}],["manufacturing",{"2":{"137":3}}],["manual",{"2":{"100":1,"109":1,"238":1,"282":1}}],["manually",{"2":{"3":1,"125":1,"159":1,"161":2,"187":1,"665":1}}],["mandatory",{"2":{"110":1}}],["managed",{"2":{"665":1}}],["manage",{"2":{"227":3}}],["managers",{"2":{"186":1,"264":1}}],["manager",{"0":{"264":1},"1":{"265":1,"266":1},"2":{"167":1,"176":1,"252":2,"264":2,"266":1,"282":1}}],["manageable",{"2":{"107":1,"110":1}}],["management",{"0":{"142":1,"156":1},"1":{"157":1,"158":1,"159":1,"160":1,"161":1,"162":1,"163":1},"2":{"13":1,"140":1,"178":1,"247":1,"692":1}}],["manner",{"2":{"103":1,"692":1}}],["many",{"2":{"90":1,"94":1,"97":1,"137":3,"188":2,"209":1,"220":2,"236":1,"249":2,"262":1,"268":2,"269":1,"273":1}}],["maybe",{"2":{"226":1}}],["may",{"2":{"6":1,"97":1,"187":1,"241":1,"249":3,"250":1,"252":3,"253":2,"266":1,"270":1,"272":1,"283":1,"671":1,"674":1,"753":1,"769":1}}],["maintenance",{"2":{"674":1}}],["maintain",{"2":{"160":1,"260":1}}],["maintaining",{"2":{"19":1,"238":1}}],["maintainers",{"2":{"2":4,"5":4}}],["mainly",{"2":{"262":1}}],["main",{"2":{"4":2,"101":1,"104":2,"186":1,"193":3,"197":2,"199":3,"200":1,"201":3,"202":1,"203":1,"665":1,"667":1}}],["tlsv1",{"2":{"176":2}}],["tl",{"2":{"128":2,"164":2}}],["ttl",{"0":{"122":1},"2":{"122":1}}],["tweet",{"2":{"253":1}}],["twice",{"2":{"248":1}}],["twiggy",{"2":{"109":1}}],["two",{"2":{"3":1,"6":1,"18":1,"49":1,"56":1,"57":1,"81":1,"87":1,"97":2,"118":1,"125":1,"127":1,"128":1,"138":2,"141":1,"154":1,"166":1,"178":1,"186":1,"204":1,"207":1,"216":2,"219":2,"222":1,"225":1,"229":2,"233":1,"250":1,"252":1,"262":1,"263":1,"270":1,"273":1,"282":1,"667":1,"674":1,"699":1}}],["txs",{"2":{"771":10,"775":8}}],["tx",{"2":{"53":4,"61":2,"71":2,"218":4,"771":32}}],["typing",{"2":{"97":1,"209":1,"211":1}}],["typically",{"2":{"200":1,"217":1,"253":1,"673":1,"674":1}}],["typical",{"2":{"42":1,"69":1,"74":1,"141":1,"268":1}}],["type2",{"2":{"668":2}}],["type1",{"2":{"668":2}}],["type=",{"2":{"771":6}}],["type=asset",{"2":{"211":2}}],["type=quantity",{"2":{"171":1}}],["type=quantity$",{"2":{"171":1}}],["typed",{"2":{"190":1,"209":1,"211":1,"220":1,"231":1}}],["typeerror",{"0":{"626":1},"2":{"187":1,"458":1}}],["typeof",{"2":{"187":3}}],["types",{"0":{"25":1,"85":1,"124":1},"1":{"86":1,"87":1,"88":1,"89":1,"90":1,"125":1,"126":1,"127":1},"2":{"25":2,"31":1,"33":1,"37":1,"50":1,"57":3,"81":1,"85":1,"87":1,"97":1,"99":1,"103":1,"124":3,"125":1,"126":3,"137":2,"143":3,"186":1,"200":1,"211":1,"212":2,"216":1,"218":1,"665":1,"667":2,"699":1,"716":1}}],["typescript",{"0":{"185":1},"1":{"186":1,"187":1,"188":1,"189":1,"190":1,"191":1,"192":1,"193":1,"194":1,"195":1},"2":{"5":2,"27":1,"29":1,"42":1,"43":1,"178":1,"187":1,"188":1,"225":1}}],["type",{"2":{"11":10,"16":1,"25":1,"38":1,"46":1,"50":2,"52":1,"57":2,"62":1,"67":1,"79":1,"83":1,"85":2,"86":1,"87":1,"94":3,"97":1,"103":4,"125":3,"126":1,"128":4,"137":2,"141":1,"164":4,"170":12,"174":6,"186":2,"187":3,"190":3,"192":4,"201":5,"208":1,"209":1,"211":4,"212":2,"220":6,"253":1,"282":4,"289":1,"290":1,"291":1,"292":1,"293":1,"294":1,"295":1,"296":1,"297":1,"298":1,"299":1,"300":1,"301":1,"302":1,"303":1,"304":1,"305":1,"306":1,"307":1,"308":2,"309":1,"310":1,"311":1,"312":1,"313":1,"314":1,"315":1,"316":1,"317":1,"318":1,"319":1,"320":1,"321":1,"322":1,"323":1,"324":1,"325":1,"326":1,"327":1,"328":1,"329":1,"330":1,"331":1,"332":1,"333":1,"334":1,"335":1,"336":1,"337":1,"338":1,"339":1,"340":1,"341":1,"342":1,"343":1,"344":1,"345":1,"346":1,"347":1,"348":1,"349":1,"350":1,"351":1,"352":1,"353":1,"354":1,"355":1,"356":1,"357":1,"358":1,"359":1,"360":1,"361":1,"362":1,"363":1,"364":1,"365":1,"366":1,"367":1,"368":1,"369":1,"370":1,"371":1,"372":1,"373":1,"374":1,"375":1,"376":1,"377":1,"378":1,"379":1,"380":1,"381":1,"382":1,"383":1,"384":1,"385":1,"386":1,"387":1,"388":1,"389":1,"390":1,"391":1,"392":1,"393":1,"394":1,"395":1,"396":1,"397":1,"398":1,"399":1,"400":1,"401":1,"402":1,"403":1,"404":1,"405":1,"406":4,"407":4,"408":4,"409":4,"410":4,"411":4,"412":4,"413":4,"414":4,"415":4,"416":4,"417":4,"418":1,"419":1,"420":1,"421":1,"422":1,"423":1,"424":1,"425":1,"426":1,"427":1,"428":1,"429":1,"430":1,"431":1,"432":4,"433":1,"434":1,"435":1,"436":1,"437":1,"438":1,"439":1,"440":1,"441":1,"442":1,"443":1,"444":1,"445":1,"446":1,"447":1,"448":1,"449":1,"450":1,"451":1,"452":1,"453":1,"454":1,"455":1,"456":1,"457":1,"458":2,"459":1,"460":1,"461":1,"462":1,"463":1,"464":1,"465":1,"466":1,"467":1,"468":1,"469":1,"470":1,"471":1,"472":1,"473":1,"474":1,"475":1,"476":1,"477":1,"478":1,"479":1,"480":1,"481":1,"482":1,"483":1,"484":1,"485":1,"486":1,"487":1,"488":1,"489":1,"490":1,"491":1,"492":1,"493":1,"494":2,"495":1,"496":1,"497":1,"498":1,"499":1,"500":1,"501":1,"502":1,"503":1,"504":1,"505":1,"506":1,"507":1,"508":1,"509":1,"510":1,"511":1,"512":1,"513":1,"514":1,"515":1,"516":1,"517":1,"518":1,"519":1,"520":1,"521":1,"522":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1,"530":1,"531":1,"532":1,"533":1,"534":1,"535":1,"536":1,"537":1,"538":1,"539":1,"540":1,"541":1,"542":1,"543":1,"544":1,"545":1,"546":1,"547":1,"548":1,"549":1,"550":1,"551":1,"552":1,"553":1,"554":1,"555":1,"556":1,"557":1,"558":2,"559":1,"560":1,"561":1,"562":1,"563":1,"564":1,"565":1,"566":1,"567":1,"568":1,"569":1,"570":1,"571":1,"572":1,"573":1,"574":1,"575":1,"576":1,"577":1,"578":1,"579":1,"580":1,"581":1,"582":1,"583":1,"584":1,"585":1,"586":1,"587":1,"588":1,"589":1,"590":1,"591":1,"592":1,"593":1,"594":1,"595":1,"596":1,"597":1,"598":1,"599":1,"600":1,"601":1,"602":1,"603":1,"604":1,"605":1,"606":1,"607":1,"608":1,"609":1,"610":1,"611":1,"612":1,"613":1,"614":1,"615":1,"616":1,"617":2,"618":1,"619":1,"620":1,"621":1,"622":1,"623":1,"624":1,"625":1,"626":1,"627":1,"628":1,"629":1,"630":1,"631":1,"632":1,"633":1,"634":1,"635":1,"636":1,"637":1,"638":1,"639":1,"640":1,"641":1,"642":1,"643":1,"644":1,"645":1,"646":1,"647":1,"648":1,"649":1,"650":1,"651":1,"652":1,"653":1,"654":1,"655":1,"656":1,"657":1,"658":1,"659":1,"660":1,"661":1,"662":1,"663":1,"667":12,"673":1,"675":1,"685":1,"716":9,"732":1,"733":1,"734":1,"741":1,"771":16,"775":3}}],["tui",{"2":{"166":1}}],["tuning",{"2":{"143":1}}],["tune",{"2":{"131":1,"151":1}}],["tuned",{"2":{"28":1}}],["turing",{"2":{"97":1,"101":1}}],["turned",{"2":{"140":1,"158":1}}],["turn",{"2":{"85":1,"273":1,"686":1}}],["tuple",{"2":{"38":1,"94":1,"346":1}}],["tutorials",{"2":{"178":1,"222":1}}],["tutorial",{"0":{"226":1},"2":{"14":1,"16":1,"19":1,"40":1,"78":1,"170":1,"178":1,"186":2,"187":1,"212":1,"213":1,"222":1,"223":1,"224":1,"225":1,"226":3,"282":1}}],["tbd",{"2":{"23":1,"30":1,"275":1,"276":1,"284":1}}],["tell",{"2":{"273":1}}],["telegram",{"2":{"270":3,"271":1,"274":1,"277":1,"284":1,"285":1}}],["telemetry`",{"2":{"193":2}}],["telemetryurl",{"2":{"187":4,"193":4,"199":4}}],["telemetry",{"0":{"120":1,"150":1},"2":{"18":9,"65":1,"113":2,"118":1,"120":4,"143":7,"147":1,"150":2,"164":8,"170":2,"174":2,"187":2,"198":6,"213":4,"231":1,"232":1}}],["text",{"2":{"256":1}}],["temporarily",{"2":{"264":1}}],["temporary",{"2":{"43":1}}],["template",{"2":{"193":16}}],["tempting",{"2":{"189":1,"210":1,"219":1}}],["tech",{"2":{"180":2,"186":2}}],["techniques",{"2":{"259":1}}],["technical",{"0":{"78":1},"2":{"75":2,"78":1,"93":2,"95":2,"97":1}}],["technologies",{"2":{"236":1}}],["technology",{"2":{"36":1,"223":1,"224":1,"237":1,"670":1}}],["ten",{"2":{"177":1}}],["teacher",{"0":{"682":1},"2":{"669":1}}],["teams",{"2":{"168":2}}],["team",{"2":{"168":2,"206":1,"268":2,"270":2}}],["tea",{"2":{"92":18,"93":6,"94":5,"97":2,"171":9,"172":3,"173":2,"733":1}}],["terminated",{"2":{"266":1}}],["terminal",{"2":{"20":1,"143":4,"164":4,"166":3,"168":2,"174":2,"199":1,"244":1}}],["terminology",{"2":{"199":1,"200":1}}],["term",{"2":{"160":1,"180":1,"183":1}}],["terms",{"2":{"39":1,"41":1,"56":1,"89":1,"186":1,"190":1,"193":1,"201":1,"204":1,"211":1,"220":1,"222":1}}],["test0",{"2":{"714":2}}],["testops",{"2":{"287":2}}],["testconstskt",{"2":{"205":2}}],["testcontainers",{"2":{"198":13}}],["testengine",{"2":{"198":2,"205":8}}],["tested",{"2":{"0":1,"180":1,"235":1,"268":1}}],["tests",{"2":{"62":2,"193":1,"217":2,"235":1}}],["test",{"2":{"53":16,"137":1,"167":10,"168":5,"193":1,"199":2,"205":14,"213":1,"216":1,"704":8,"705":6,"706":8,"708":8,"709":8,"712":8,"713":8}}],["testing",{"0":{"235":1},"2":{"14":1,"166":1,"214":2,"231":1,"235":3}}],["tremendously",{"2":{"249":1}}],["treating",{"2":{"261":1}}],["treated",{"2":{"238":1,"693":1}}],["treatments",{"2":{"137":1}}],["tree",{"0":{"683":2},"2":{"183":1,"669":2,"683":1}}],["troubleshoot",{"2":{"272":1}}],["troubleshooting",{"0":{"271":1,"274":1,"277":1,"278":1,"283":1,"284":1,"285":1},"1":{"272":1,"273":1,"275":1,"276":1,"278":1,"279":2,"280":2,"281":2,"282":2,"283":1,"286":1},"2":{"176":1,"177":1,"217":1,"271":1,"274":1,"277":1,"282":1,"284":1,"285":1}}],["trouble",{"2":{"120":1,"150":1}}],["trytovalue",{"2":{"217":2}}],["try",{"2":{"92":8,"93":2,"104":2,"168":1,"208":1,"217":1,"220":2,"273":2,"283":1,"667":2}}],["trying",{"2":{"5":1,"101":1,"186":1,"230":2}}],["train",{"2":{"260":1}}],["training",{"2":{"247":1,"260":1}}],["traitname",{"2":{"668":2}}],["traits",{"2":{"216":1,"667":1,"668":1,"772":1}}],["trait",{"2":{"62":2,"103":1,"209":1,"664":1,"667":4,"668":1}}],["trade",{"2":{"106":1,"137":3}}],["traditional",{"2":{"100":1}}],["traps",{"2":{"105":2}}],["tracing",{"2":{"216":1}}],["tracking",{"2":{"249":1,"282":1}}],["track",{"2":{"65":1,"89":1,"209":1,"211":1,"212":1,"249":2,"258":1,"260":1,"687":1}}],["tracked",{"2":{"39":1}}],["trace",{"2":{"39":1,"153":1,"473":1}}],["transmitting",{"2":{"254":1}}],["transparent",{"2":{"233":1}}],["transport",{"2":{"187":3}}],["translation",{"2":{"213":8}}],["transcribed",{"2":{"105":2}}],["transferasset",{"2":{"202":6}}],["transferassetinstruction",{"2":{"191":2}}],["transferable",{"2":{"79":1}}],["transferbox",{"0":{"612":1},"2":{"93":2,"191":4,"457":1}}],["transfers",{"2":{"93":1,"191":1}}],["transferring",{"0":{"172":1,"191":1,"202":1},"2":{"41":1,"44":1,"89":1,"202":1}}],["transferred",{"2":{"27":1,"41":1,"50":1,"86":1,"97":1,"202":4,"203":2,"255":1}}],["transfer",{"0":{"44":1},"2":{"40":2,"41":3,"44":3,"57":1,"94":1,"137":1,"172":3,"191":3,"202":3,"457":1,"461":1,"667":1,"698":2,"701":2,"706":7,"707":5}}],["transactionreceipttime",{"2":{"755":2}}],["transactionrejectionreason",{"0":{"519":1,"610":1},"2":{"519":1,"545":1,"611":1}}],["transactionqueryoutput",{"0":{"609":1},"2":{"634":2}}],["transactionlimits",{"0":{"607":1},"2":{"634":2}}],["transactionlimits=4096",{"2":{"128":2,"164":2}}],["transactionlimiterror",{"0":{"606":1},"2":{"610":1}}],["transactionpayload",{"0":{"577":1,"579":1,"598":1,"608":1},"2":{"579":1,"582":2,"598":1}}],["transactionvalue",{"0":{"611":1,"643":1},"2":{"327":1,"609":1,"643":1,"757":3,"758":1}}],["transactionbuilder",{"2":{"187":1,"205":20}}],["transaction",{"0":{"96":1,"121":1,"122":1,"123":1,"756":1,"769":1,"777":1},"1":{"757":1,"758":1},"2":{"28":2,"32":1,"39":2,"50":1,"53":4,"61":2,"71":6,"72":1,"96":1,"97":1,"98":1,"113":8,"121":2,"122":4,"123":1,"138":1,"143":6,"164":14,"170":12,"174":10,"186":1,"188":1,"189":1,"193":4,"204":8,"205":16,"209":1,"212":4,"218":18,"219":1,"220":5,"222":2,"255":1,"256":1,"431":1,"453":1,"542":1,"545":1,"609":1,"670":1,"755":2,"757":6,"758":1,"769":2,"771":2,"777":4}}],["transactions",{"0":{"71":1},"2":{"11":10,"28":1,"32":1,"41":1,"50":1,"71":1,"80":1,"87":1,"89":3,"122":1,"123":1,"128":2,"135":1,"143":6,"164":8,"174":1,"187":4,"212":1,"218":1,"227":1,"237":1,"245":1,"254":1,"256":2,"325":1,"327":1,"553":1,"689":1,"702":2,"703":2,"756":1,"757":1,"771":2,"775":6}}],["trivial",{"2":{"716":2}}],["tries",{"2":{"190":1,"261":1}}],["tried",{"2":{"28":1}}],["trigger$domain",{"2":{"699":1}}],["trigger$my",{"2":{"83":1}}],["triggernumberofexecutionschanged",{"0":{"624":1},"2":{"620":2}}],["triggercompletedoutcome",{"0":{"618":1},"2":{"616":1}}],["triggercompletedoutcometype",{"0":{"520":1,"619":1},"2":{"520":1,"617":1}}],["triggercompletedeventfilter",{"0":{"617":1},"2":{"503":1}}],["triggercompletedevent",{"0":{"616":1},"2":{"502":1}}],["triggercompleted",{"2":{"502":1,"503":1}}],["triggerbox",{"0":{"615":1},"2":{"455":1}}],["triggereventfilter",{"0":{"399":1,"621":1},"2":{"399":1,"622":1}}],["triggerevent",{"0":{"394":1,"529":1,"620":1},"2":{"339":1,"394":1,"622":1}}],["triggered",{"2":{"89":1}}],["triggerfilter",{"0":{"400":1,"622":1},"2":{"338":1,"400":1}}],["triggeringfilterbox",{"0":{"296":1,"297":1,"613":1,"614":1,"625":1},"2":{"296":1,"297":1,"555":1,"613":1,"614":1,"615":2}}],["triggerid",{"0":{"362":1,"521":1,"623":1},"2":{"73":2,"74":1,"93":2,"372":1,"373":1,"374":1,"431":1,"439":1,"440":1,"454":2,"521":1,"529":1,"613":1,"614":1,"616":1,"617":1,"620":2,"624":1,"760":1,"761":1,"762":1}}],["triggers",{"0":{"72":1,"81":1,"83":1,"85":1,"86":1,"87":1,"88":1,"89":1,"90":1,"91":1,"685":1},"1":{"73":1,"74":1,"75":1,"76":1,"77":1,"78":1,"79":1,"80":1,"81":1,"82":2,"83":2,"84":2,"85":1,"86":2,"87":2,"88":3,"89":3,"90":2,"91":1,"92":2,"93":2,"94":2,"95":2,"96":2,"97":2,"98":1,"99":1},"2":{"33":1,"34":1,"40":2,"41":5,"42":1,"43":1,"48":1,"50":1,"72":2,"79":1,"81":1,"82":8,"83":5,"85":8,"86":1,"87":7,"88":6,"89":4,"90":1,"97":5,"98":3,"99":1,"111":1,"137":1,"178":1,"669":1,"685":1,"698":2,"749":1,"760":2,"763":1}}],["trigger",{"0":{"35":1,"73":1,"74":1,"75":1,"93":1,"95":1,"613":1,"614":1,"759":1},"1":{"74":1,"75":1,"76":2,"77":2,"78":2,"79":2,"80":2,"760":1,"761":1,"762":1,"763":1},"2":{"31":1,"35":1,"41":4,"43":1,"72":3,"73":3,"74":4,"75":1,"78":2,"79":2,"80":1,"81":5,"83":2,"84":5,"85":3,"86":1,"88":8,"90":1,"91":1,"93":1,"95":5,"96":5,"98":9,"233":1,"339":1,"372":1,"373":1,"374":1,"431":1,"455":1,"555":2,"615":2,"616":1,"617":1,"624":1,"761":2,"762":2,"763":1}}],["truly",{"2":{"126":1}}],["trueversion",{"2":{"18":1}}],["true",{"2":{"18":7,"72":1,"94":1,"105":2,"123":1,"143":4,"164":4,"193":2,"198":6,"199":2,"211":1}}],["trustworthy",{"2":{"248":1,"256":1}}],["trusts",{"2":{"189":1,"210":1,"219":1}}],["trustedpeers",{"2":{"198":2}}],["trusted",{"0":{"146":1},"2":{"18":8,"22":1,"42":1,"111":1,"127":1,"135":4,"143":4,"146":3,"149":1,"164":2,"248":1,"249":1,"261":1,"286":1}}],["trust",{"2":{"18":1,"146":1,"245":1}}],["tail",{"2":{"755":2}}],["tailor",{"2":{"238":1}}],["tasks",{"2":{"249":1}}],["tally",{"2":{"211":1}}],["talking",{"2":{"163":1}}],["talk",{"2":{"25":1,"99":1,"141":1,"170":1,"193":1,"204":1,"222":1,"722":1}}],["tag",{"2":{"192":2,"193":2,"198":2,"256":1}}],["table",{"2":{"273":1}}],["tab",{"2":{"168":2,"170":1,"174":1,"265":1}}],["tad",{"2":{"101":1}}],["taken",{"2":{"100":1,"107":1,"159":1,"186":1,"190":1,"247":1,"248":1}}],["takes",{"2":{"14":1,"88":1,"97":3,"100":1,"103":1,"177":1,"253":1,"695":1}}],["take",{"2":{"8":1,"9":1,"18":1,"22":1,"28":1,"50":1,"65":1,"72":1,"97":1,"101":1,"105":1,"106":1,"125":1,"177":1,"190":1,"224":1,"228":2,"231":1,"253":2}}],["targets",{"2":{"185":2}}],["target",{"2":{"5":2,"6":5,"16":6,"17":2,"22":2,"41":3,"108":2,"110":1,"111":1,"167":2,"170":4,"177":1,"186":15,"187":2,"193":2,"197":1,"207":2,"241":1,"242":2,"243":2,"248":1,"479":1,"480":1,"481":1,"482":1}}],["t",{"2":{"5":1,"11":1,"16":3,"28":2,"57":1,"69":1,"82":2,"84":1,"88":6,"97":2,"101":1,"102":2,"103":1,"106":1,"125":1,"128":1,"137":2,"141":1,"153":1,"154":1,"160":2,"163":1,"166":5,"168":1,"171":1,"183":1,"187":3,"188":1,"189":4,"190":1,"198":1,"208":2,"210":4,"211":4,"212":4,"216":1,"218":2,"219":4,"220":4,"222":1,"224":1,"232":1,"243":1,"248":1,"253":1,"272":1,"273":3,"278":2,"280":2,"282":1,"283":1,"285":1,"665":2,"667":1,"676":1,"701":1}}],["thwart",{"2":{"253":1}}],["thus",{"2":{"28":1,"90":1,"110":1,"126":1,"161":1,"187":2,"201":1,"207":2,"220":1,"243":1,"267":1,"693":1}}],["throws",{"2":{"205":10}}],["throw",{"2":{"188":4,"202":2}}],["throughout",{"2":{"247":1,"757":1}}],["throughput",{"2":{"88":1}}],["through",{"2":{"13":1,"27":1,"29":1,"42":1,"43":1,"72":1,"88":1,"94":1,"98":1,"127":1,"141":1,"162":1,"176":1,"178":1,"201":1,"216":1,"224":1,"229":1,"253":2,"266":1,"744":1}}],["threats",{"2":{"250":1,"260":2,"261":1}}],["threat",{"2":{"247":1,"249":1}}],["threading",{"2":{"231":1}}],["threshold",{"2":{"97":1,"143":2,"164":2}}],["three",{"2":{"18":2,"20":1,"21":2,"25":1,"127":1,"132":1,"270":1,"272":1}}],["those",{"2":{"62":1,"65":1,"78":1,"125":2,"187":2,"229":1,"238":1,"267":1}}],["thoughtfully",{"2":{"231":1}}],["thought",{"2":{"141":1}}],["though",{"2":{"18":1,"25":1,"103":1,"134":1,"135":1,"256":1}}],["thoroughly",{"2":{"10":1,"260":1}}],["thankfully",{"2":{"103":1}}],["than",{"2":{"14":1,"88":2,"89":1,"97":2,"98":1,"101":2,"154":1,"187":1,"189":1,"190":1,"200":1,"219":1,"232":1,"256":1,"282":2,"716":1,"734":1}}],["that",{"2":{"4":1,"6":1,"10":1,"11":1,"13":1,"14":1,"16":8,"17":1,"18":4,"20":4,"21":2,"22":6,"28":6,"32":1,"36":2,"37":1,"38":1,"39":3,"40":1,"41":3,"42":6,"43":2,"46":1,"50":1,"51":1,"52":1,"55":1,"57":3,"60":1,"62":5,"63":2,"65":3,"66":1,"68":1,"69":2,"72":3,"78":4,"79":1,"80":1,"81":3,"84":2,"85":1,"86":1,"87":1,"88":8,"89":2,"91":2,"93":2,"94":8,"97":11,"98":5,"99":2,"100":3,"101":3,"103":3,"104":3,"105":1,"106":1,"107":1,"108":1,"110":2,"111":1,"116":3,"121":1,"123":1,"125":6,"126":3,"127":1,"128":5,"134":2,"135":5,"137":6,"141":8,"146":2,"153":1,"154":3,"156":1,"159":1,"160":1,"161":1,"163":1,"166":14,"167":2,"168":6,"169":1,"170":5,"171":3,"177":1,"178":3,"180":2,"182":1,"183":1,"186":4,"187":9,"188":6,"189":6,"190":7,"193":4,"197":2,"198":1,"199":3,"200":1,"204":1,"207":3,"208":4,"209":2,"210":4,"211":6,"212":9,"214":1,"216":11,"217":4,"218":7,"219":8,"220":11,"224":1,"229":2,"230":3,"231":2,"232":1,"233":2,"234":1,"235":2,"238":5,"241":1,"242":2,"243":1,"244":1,"245":1,"246":2,"247":2,"248":4,"249":10,"250":5,"252":3,"253":3,"255":3,"256":9,"258":2,"259":1,"260":8,"263":4,"265":3,"266":3,"267":1,"268":3,"270":1,"272":4,"273":4,"278":1,"279":1,"280":3,"281":1,"282":7,"283":3,"285":1,"286":1,"664":2,"665":3,"667":9,"668":1,"670":3,"684":1,"685":1,"687":2,"688":1,"695":1,"697":1,"699":1,"701":1,"704":4,"705":4,"706":4,"707":2,"708":4,"709":4,"710":2,"711":2,"712":4,"713":4,"714":4,"719":1,"720":1,"721":2,"729":1,"730":2,"731":1,"732":1,"733":2,"736":1,"737":1,"738":1,"739":1,"740":1,"741":2,"744":1,"748":2,"757":1,"759":2,"760":2}}],["thin",{"2":{"188":1,"716":1}}],["thinks",{"2":{"220":1}}],["thinking",{"2":{"137":1}}],["think",{"2":{"94":1,"137":2,"166":2,"220":1}}],["thing",{"2":{"28":1,"116":1,"166":1,"168":1}}],["things",{"2":{"6":1,"18":1,"28":1,"31":1,"42":1,"72":1,"82":1,"97":1,"103":1,"170":1,"183":1,"189":1,"197":1,"210":1,"219":1,"254":1,"278":1,"665":1}}],["third",{"2":{"16":1,"230":1,"259":1,"688":1}}],["this",{"2":{"4":1,"5":2,"6":1,"10":1,"11":2,"13":1,"14":1,"16":5,"17":1,"18":1,"19":1,"20":3,"21":3,"28":4,"36":2,"39":1,"40":1,"41":2,"42":1,"44":1,"46":1,"48":1,"50":1,"51":1,"65":1,"66":1,"71":1,"75":1,"76":1,"78":2,"79":1,"80":2,"81":2,"85":1,"86":2,"87":1,"88":1,"89":2,"90":1,"92":1,"94":4,"95":1,"97":3,"99":1,"100":1,"101":2,"103":1,"104":2,"106":2,"108":3,"112":1,"117":1,"119":1,"120":2,"124":1,"125":2,"126":2,"127":1,"128":3,"137":7,"146":1,"149":1,"150":1,"151":1,"154":1,"159":1,"160":1,"163":2,"166":3,"168":6,"169":1,"170":8,"171":1,"174":2,"178":1,"180":4,"183":1,"185":2,"186":4,"187":8,"188":1,"189":1,"190":2,"193":9,"194":1,"195":1,"197":2,"199":9,"200":6,"201":12,"202":7,"203":6,"204":4,"207":1,"208":1,"209":2,"210":4,"211":3,"212":2,"213":2,"216":1,"217":2,"218":1,"219":1,"220":6,"221":1,"222":4,"223":1,"224":2,"225":1,"226":1,"229":1,"230":2,"233":1,"234":1,"236":2,"237":1,"238":3,"241":2,"243":1,"246":1,"247":3,"248":3,"249":5,"250":3,"252":1,"253":3,"254":1,"255":2,"256":4,"257":2,"258":1,"259":1,"262":1,"263":3,"264":1,"265":1,"266":1,"268":5,"269":1,"271":1,"272":4,"273":2,"274":1,"277":1,"281":1,"282":1,"283":4,"284":1,"285":1,"286":2,"287":2,"288":1,"665":2,"667":4,"670":1,"676":1,"690":1,"695":1,"696":1,"697":1,"700":1,"716":2,"725":1,"728":1,"729":1,"730":1,"733":1,"741":2,"750":2,"753":1,"755":1,"764":2,"767":1,"771":4,"776":1}}],["theoretically",{"2":{"137":1,"264":1}}],["theory",{"2":{"78":1,"91":1,"97":1,"101":1,"141":1,"171":1,"207":1}}],["their",{"2":{"21":1,"24":1,"56":1,"57":2,"65":1,"67":1,"112":1,"115":1,"127":1,"132":1,"135":1,"137":3,"139":1,"145":1,"146":2,"148":1,"158":1,"160":1,"170":2,"186":1,"189":1,"190":1,"201":1,"202":1,"210":1,"211":1,"216":1,"219":1,"220":2,"229":2,"246":2,"247":3,"248":3,"249":3,"250":1,"252":1,"255":1,"259":1,"260":1,"268":1,"668":1,"671":1,"673":1,"674":2,"693":1,"696":1,"716":1,"722":1,"754":1}}],["these",{"2":{"6":1,"16":2,"29":1,"33":1,"39":1,"42":1,"45":1,"46":1,"47":1,"57":1,"72":1,"74":1,"83":1,"86":1,"89":1,"90":1,"100":1,"116":1,"117":1,"126":1,"127":3,"128":1,"137":1,"138":2,"166":1,"175":1,"179":1,"186":1,"187":2,"211":1,"216":2,"222":1,"237":1,"249":1,"250":1,"252":1,"258":1,"260":1,"262":1,"269":1,"273":2,"282":1,"287":1,"670":1,"689":1,"691":1,"692":1,"693":1,"716":2,"775":1}}],["then",{"2":{"5":1,"21":1,"39":1,"88":1,"97":1,"104":1,"128":1,"168":1,"170":1,"183":1,"186":2,"187":1,"189":2,"199":1,"200":1,"201":2,"203":1,"204":1,"210":1,"218":2,"219":2,"220":2,"221":1,"222":1,"238":1,"244":1,"248":1,"263":1,"264":1,"265":5,"279":1,"282":3,"331":1,"456":1,"767":1,"768":1,"769":1}}],["thereby",{"2":{"268":1}}],["therefore",{"2":{"180":1,"245":1,"252":1,"253":1,"256":1}}],["there",{"0":{"282":1},"2":{"3":1,"16":1,"18":1,"19":1,"22":1,"25":1,"28":1,"31":1,"33":1,"39":1,"41":2,"42":1,"52":1,"56":2,"57":1,"62":6,"65":1,"81":1,"87":1,"97":1,"100":1,"103":2,"125":2,"135":1,"137":3,"168":1,"170":1,"171":1,"189":1,"195":1,"200":1,"204":1,"207":1,"209":1,"210":1,"211":1,"214":1,"216":1,"218":3,"219":1,"220":3,"222":1,"233":1,"235":1,"242":1,"252":1,"253":1,"256":1,"259":1,"263":1,"268":2,"270":1,"272":2,"273":1,"282":2,"667":1,"699":1,"701":1}}],["they",{"2":{"0":1,"20":1,"21":1,"28":2,"39":3,"43":1,"56":2,"65":1,"82":1,"87":1,"88":1,"90":1,"97":2,"100":1,"105":2,"137":6,"153":1,"166":1,"167":1,"168":1,"170":1,"186":1,"189":1,"195":1,"210":1,"219":1,"220":1,"224":1,"247":1,"249":2,"256":1,"266":1,"667":1,"673":1,"690":1,"715":1,"717":1,"767":1,"769":3,"775":1}}],["themselves",{"2":{"721":1}}],["theme",{"2":{"211":1}}],["them",{"2":{"0":1,"5":1,"8":1,"9":1,"14":1,"18":2,"21":1,"24":1,"28":2,"41":1,"70":1,"85":1,"92":1,"97":1,"105":1,"125":2,"126":1,"129":1,"137":2,"173":1,"178":2,"186":1,"187":1,"189":1,"191":1,"202":1,"203":1,"210":1,"219":1,"220":1,"248":2,"253":1,"256":3,"259":1,"260":2,"261":1,"264":1,"769":1}}],["the",{"0":{"73":1,"137":1,"160":1,"162":1,"176":1,"242":1,"243":1,"244":2,"256":1,"281":1,"286":1},"1":{"74":1,"75":1,"76":1,"77":1,"78":1,"79":1,"80":1,"244":1},"2":{"0":3,"2":5,"3":3,"4":3,"5":8,"6":18,"7":3,"8":3,"9":2,"10":2,"11":7,"12":1,"13":6,"14":8,"16":24,"17":7,"18":10,"19":2,"20":13,"21":21,"22":11,"24":8,"25":4,"27":2,"28":33,"29":9,"31":1,"32":5,"33":1,"34":1,"35":1,"36":6,"38":1,"39":18,"40":5,"41":6,"42":11,"43":6,"44":1,"45":1,"46":2,"49":1,"50":5,"51":4,"52":2,"53":1,"54":1,"56":1,"57":15,"58":2,"59":3,"60":1,"61":1,"62":12,"63":7,"65":14,"67":6,"68":2,"69":4,"70":1,"71":5,"72":5,"73":1,"74":1,"75":2,"76":1,"77":1,"78":7,"79":2,"80":2,"81":3,"82":2,"83":3,"84":6,"85":5,"86":4,"87":7,"88":23,"89":6,"90":2,"91":6,"92":5,"93":1,"94":12,"96":2,"97":31,"98":12,"99":4,"100":5,"101":14,"103":15,"104":12,"105":9,"106":8,"107":2,"108":4,"109":2,"110":8,"111":7,"112":3,"113":1,"114":1,"115":2,"116":6,"117":7,"118":1,"119":6,"120":4,"121":3,"122":3,"124":2,"125":10,"126":11,"127":7,"128":27,"129":2,"130":5,"131":2,"132":1,"134":14,"135":23,"136":5,"137":41,"138":3,"139":2,"141":8,"143":6,"144":1,"145":2,"146":9,"147":2,"148":7,"149":4,"150":6,"151":9,"152":1,"153":2,"154":4,"155":4,"156":1,"157":1,"158":1,"159":2,"160":8,"161":8,"162":4,"163":5,"166":21,"167":9,"168":41,"169":5,"170":37,"171":16,"172":1,"174":8,"175":2,"176":8,"177":7,"178":12,"179":1,"180":8,"182":1,"183":13,"186":31,"187":22,"188":11,"189":16,"190":14,"191":2,"193":9,"194":1,"195":10,"197":20,"198":8,"199":14,"200":8,"201":10,"202":9,"203":5,"204":9,"206":6,"207":10,"208":12,"209":7,"210":13,"211":10,"212":17,"213":6,"214":1,"216":30,"217":12,"218":16,"219":16,"220":30,"221":3,"222":11,"223":1,"224":6,"225":4,"226":5,"228":4,"229":6,"230":7,"231":2,"232":3,"233":9,"234":3,"236":2,"237":4,"238":26,"241":7,"242":4,"243":8,"244":19,"245":2,"246":6,"247":8,"248":7,"249":18,"250":6,"251":2,"252":35,"253":16,"254":2,"255":14,"256":18,"257":1,"258":1,"259":11,"260":8,"261":1,"262":5,"263":27,"264":13,"265":23,"266":10,"267":5,"268":18,"269":1,"270":5,"271":3,"272":36,"273":17,"274":1,"277":1,"279":5,"280":4,"281":2,"282":13,"283":5,"284":1,"285":3,"286":6,"287":11,"664":3,"665":16,"666":2,"667":21,"668":22,"670":2,"671":5,"672":3,"673":3,"674":4,"675":1,"676":3,"677":1,"679":1,"680":2,"681":1,"683":1,"686":2,"687":1,"692":2,"693":2,"694":1,"695":1,"696":2,"697":2,"698":2,"699":3,"700":1,"701":3,"702":1,"703":1,"704":1,"705":1,"706":1,"707":2,"708":1,"709":1,"710":4,"711":4,"712":1,"713":1,"715":4,"716":26,"717":1,"718":1,"719":2,"720":5,"721":3,"722":2,"724":2,"725":2,"726":1,"727":2,"728":5,"729":5,"730":2,"731":1,"733":6,"734":5,"735":3,"736":2,"737":1,"738":2,"739":1,"740":2,"741":3,"742":5,"743":5,"744":5,"746":1,"747":1,"748":2,"749":3,"750":3,"751":2,"752":4,"753":5,"754":2,"755":5,"756":1,"757":3,"758":1,"759":1,"760":2,"761":2,"762":5,"763":1,"764":1,"766":4,"767":12,"768":2,"769":12,"771":4,"772":1,"775":14,"776":5}}],["tidy",{"2":{"183":1}}],["tirelessly",{"2":{"97":1}}],["tiny",{"2":{"97":1}}],["tinkering",{"2":{"22":1}}],["timeinterval",{"0":{"518":1,"605":1},"2":{"518":1,"603":2}}],["timeeventfilter",{"0":{"604":1},"2":{"378":1,"625":1}}],["timeevent",{"0":{"603":1},"2":{"368":1}}],["timeunit",{"2":{"205":30}}],["timeoutexception",{"2":{"205":4}}],["timeouts",{"2":{"161":1}}],["timeout",{"0":{"122":1},"2":{"113":2,"122":3,"164":2,"170":2,"174":2,"199":4,"200":2,"201":4,"202":2,"203":2}}],["timed",{"2":{"89":1}}],["timestamp",{"2":{"87":1,"325":1,"636":1}}],["times",{"2":{"57":1,"84":2,"98":4,"166":1,"252":1,"253":1,"670":1,"707":1}}],["time",{"0":{"34":1,"87":1},"1":{"88":1,"89":1},"2":{"3":1,"28":2,"31":1,"34":2,"43":1,"57":2,"84":1,"85":1,"87":3,"88":8,"94":2,"97":3,"105":4,"107":1,"113":2,"122":3,"137":1,"143":8,"148":1,"153":1,"160":3,"164":10,"168":1,"169":1,"170":3,"174":2,"180":1,"189":1,"190":17,"191":3,"198":2,"201":8,"202":16,"203":24,"210":1,"211":8,"219":1,"220":15,"232":1,"233":1,"252":2,"253":2,"263":1,"270":2,"272":1,"368":1,"378":1,"608":2,"625":1,"674":1,"680":1,"685":1,"707":1,"755":6,"760":1,"766":1,"771":4}}],["tips",{"2":{"271":1,"274":1,"277":1,"284":1}}],["tip",{"2":{"3":1,"5":1,"16":2,"17":1,"18":1,"20":1,"162":1,"167":1,"168":2,"170":2,"176":1,"183":1,"187":1,"193":1,"207":1,"216":1,"228":1,"238":2,"252":1,"264":1,"734":1}}],["tscomponents",{"2":{"193":1}}],["tsconst",{"2":{"5":1,"189":3,"192":3}}],["tsclient",{"2":{"193":1}}],["tsawait",{"2":{"188":2}}],["tsasync",{"2":{"188":2}}],["ts$",{"2":{"186":1}}],["tsx",{"2":{"186":5}}],["tsimport",{"2":{"5":1,"186":1,"187":6,"188":1,"189":1,"190":2,"191":1,"192":1,"193":4,"195":1}}],["tsexport",{"2":{"2":1,"5":2}}],["ts",{"2":{"2":2,"3":1,"5":9,"186":7,"193":16}}],["tolocalestring",{"2":{"764":1}}],["tolerance",{"0":{"230":1,"677":1},"2":{"669":1}}],["tolerant",{"2":{"146":1,"230":3}}],["tolerate",{"2":{"18":3,"230":1}}],["torelance",{"2":{"697":1}}],["toriipre",{"2":{"193":14}}],["toriirequirements",{"2":{"187":8,"188":6,"192":8}}],["toriirequirementsfortelemetry",{"2":{"187":8}}],["toriirequirementsforapiwebsocket",{"2":{"187":8,"195":4}}],["toriirequirementsforapihttpimport",{"2":{"188":1,"192":1}}],["toriirequirementsforapihttp",{"2":{"187":12,"188":3,"192":3}}],["torii",{"0":{"119":1,"120":1,"680":1,"765":1},"1":{"766":1,"767":1,"768":1,"769":1,"770":1,"771":1,"772":1,"773":1,"774":1,"775":1,"776":1,"777":1},"2":{"18":24,"20":6,"21":3,"113":4,"118":3,"119":4,"120":4,"127":1,"143":2,"147":2,"150":1,"164":6,"170":4,"174":4,"187":24,"193":14,"195":4,"217":4,"669":1}}],["towards",{"2":{"247":1}}],["tokio",{"2":{"216":1,"231":1}}],["token",{"2":{"41":1,"57":7,"62":1,"111":1,"141":1,"171":1,"538":1,"540":1,"701":1,"702":2,"703":3,"704":7,"705":7,"706":7,"707":5,"708":3,"709":3,"710":3,"711":3,"712":3,"713":3,"714":7,"723":1}}],["tokens",{"0":{"57":1,"58":1,"701":1},"1":{"58":1,"702":1,"703":1,"704":1,"705":1,"706":1,"707":1,"708":1,"709":1,"710":1,"711":1,"712":1,"713":1,"714":1},"2":{"33":1,"40":1,"41":3,"42":1,"43":2,"55":1,"56":4,"57":1,"58":1,"59":1,"64":2,"111":1,"128":1,"129":1,"137":1,"141":1,"170":4,"171":2,"698":1,"700":2,"701":1,"719":1,"720":1,"724":1}}],["toirohapublickey",{"2":{"198":4,"200":3,"202":2}}],["tostring",{"2":{"193":2}}],["toggle",{"2":{"193":1}}],["together",{"2":{"39":1,"210":1,"257":1}}],["todo",{"2":{"192":1,"288":1}}],["today",{"2":{"166":1,"665":1}}],["toaccount",{"2":{"191":4}}],["touch",{"2":{"183":1,"270":1}}],["toml",{"2":{"103":2,"105":2,"216":2}}],["toocomplex",{"2":{"630":1}}],["tooling",{"2":{"231":1}}],["toolchains",{"2":{"280":2,"282":4}}],["toolchain",{"0":{"176":1,"278":1,"283":1},"1":{"279":1,"280":1,"281":1,"282":1},"2":{"175":1,"176":3,"279":2,"280":3,"281":1,"282":8}}],["tool",{"2":{"109":1,"134":1,"170":1,"237":1,"241":1,"246":1,"263":1,"272":1}}],["toolsets",{"2":{"180":1}}],["tools",{"0":{"109":1},"2":{"101":1,"231":2,"241":2,"265":1,"692":1}}],["too",{"2":{"36":1,"57":1,"88":1,"146":1,"154":1,"166":1,"248":1,"253":1}}],["top",{"2":{"120":1,"150":1,"265":1}}],["topics",{"2":{"178":1,"246":1,"247":1,"263":1}}],["topic",{"2":{"28":1,"46":1,"247":2,"257":1,"263":2}}],["topology",{"2":{"20":2,"21":1,"325":1}}],["totalquantitychanged",{"2":{"309":1}}],["total",{"2":{"11":14,"97":1,"247":1,"314":1,"341":1,"744":1,"771":6}}],["to",{"0":{"6":1,"11":1,"109":1,"195":1,"243":1,"244":1},"1":{"7":1,"8":1,"9":1,"244":1},"2":{"0":2,"3":2,"4":2,"5":5,"6":5,"7":1,"8":2,"9":1,"10":1,"11":5,"12":1,"13":4,"14":9,"16":22,"18":14,"19":1,"20":7,"21":9,"22":7,"24":1,"25":1,"27":2,"28":9,"29":2,"32":1,"33":1,"34":1,"36":2,"37":1,"39":6,"40":3,"41":5,"42":9,"43":6,"44":7,"45":1,"48":1,"49":1,"50":1,"52":3,"53":10,"55":1,"56":7,"57":5,"59":2,"60":3,"61":1,"62":5,"63":3,"65":11,"66":2,"67":4,"68":1,"69":5,"72":3,"74":1,"76":1,"78":3,"79":2,"80":1,"81":1,"82":2,"83":4,"84":2,"85":2,"86":1,"87":2,"88":14,"89":4,"90":2,"91":1,"93":2,"94":6,"95":1,"96":1,"97":16,"98":2,"99":4,"100":2,"101":7,"102":2,"103":6,"104":7,"105":3,"108":3,"109":2,"110":5,"113":2,"114":1,"116":2,"117":2,"119":1,"120":5,"121":1,"122":5,"123":1,"125":1,"126":1,"127":1,"128":2,"129":2,"130":2,"131":1,"132":1,"134":5,"135":10,"136":2,"137":20,"138":3,"139":3,"140":1,"141":12,"143":5,"144":1,"146":8,"148":6,"150":7,"151":6,"153":2,"154":5,"155":1,"156":1,"158":1,"159":3,"160":6,"161":3,"163":4,"164":7,"166":9,"167":5,"168":29,"169":3,"170":23,"171":8,"172":3,"173":1,"174":8,"175":1,"176":3,"177":2,"178":8,"179":2,"180":3,"181":1,"182":1,"183":5,"186":16,"187":24,"188":9,"189":11,"190":10,"191":2,"193":5,"195":8,"197":6,"198":1,"199":9,"200":9,"201":10,"202":13,"203":6,"204":4,"206":1,"207":2,"208":2,"209":3,"210":10,"211":8,"212":7,"213":13,"214":1,"216":10,"217":13,"218":8,"219":13,"220":15,"221":4,"222":8,"223":2,"224":2,"225":3,"226":3,"228":2,"229":4,"230":6,"231":5,"232":3,"233":3,"234":2,"235":2,"237":2,"238":8,"241":4,"242":3,"243":5,"244":4,"245":2,"246":4,"247":5,"248":7,"249":18,"250":5,"251":2,"252":15,"253":15,"254":2,"255":1,"256":11,"257":2,"258":2,"259":9,"260":20,"261":2,"262":4,"263":14,"264":4,"265":6,"266":1,"267":2,"268":16,"270":4,"272":13,"273":4,"279":2,"280":2,"281":2,"282":4,"283":3,"285":1,"286":2,"287":3,"326":1,"329":1,"369":1,"370":1,"442":1,"448":1,"449":1,"450":1,"451":1,"452":1,"465":1,"466":1,"467":1,"468":1,"469":1,"492":1,"498":1,"499":1,"500":1,"523":1,"524":1,"525":1,"526":1,"527":1,"528":1,"529":1,"575":1,"576":1,"577":1,"599":1,"601":1,"604":1,"608":1,"655":1,"657":1,"664":3,"665":10,"667":7,"668":3,"670":1,"671":2,"673":1,"674":2,"677":3,"680":1,"683":1,"686":2,"688":1,"692":3,"694":1,"698":2,"699":1,"700":1,"701":1,"702":4,"703":4,"704":6,"705":6,"706":8,"707":2,"708":8,"709":8,"710":8,"711":6,"712":8,"713":8,"714":6,"715":1,"716":3,"718":1,"719":1,"721":1,"722":2,"724":1,"725":1,"727":1,"728":1,"730":1,"732":1,"733":1,"734":3,"737":1,"742":1,"743":1,"751":1,"752":1,"755":6,"756":1,"762":1,"764":1,"767":4,"769":4,"771":1,"772":4,"775":4,"776":2,"777":1}}]],"serializationVersion":2}';export{e as default}; diff --git a/assets/chunks/CompatibilityMatrixTable.b72f55f3.js b/assets/chunks/CompatibilityMatrixTable.b72f55f3.js new file mode 100644 index 000000000..d0a25f9b8 --- /dev/null +++ b/assets/chunks/CompatibilityMatrixTable.b72f55f3.js @@ -0,0 +1 @@ +import{u as y}from"./vue-kakuyaku.a530ead7.js";import{_ as b}from"./CompatibilityMatrixTableIcon.vue_vue_type_style_index_0_lang.db34105e.js";import{d as k,g as v,o as t,c as e,k as i,F as d,D as u,t as _,H as x,l as p,e as g,p as C,m as S,_ as T}from"./framework.1293becd.js";import"./IconCheck.ce061fbd.js";const I=l=>(C("data-v-ba4f590a"),l=l(),S(),l),M={key:0},j=["title"],B={key:1,class:"border rounded p-2 my-4"},F={key:0,class:"flex space-x-2 items-center"},L=I(()=>i("span",null,"Loading data...",-1)),N=[L],V={key:1},w=k({__name:"CompatibilityMatrixTable",setup(l){const f="https://docs-compat.iroha2.tachi.soramitsu.co.jp/compat-matrix",o=y(()=>fetch(f,{}).then(r=>r.json()),{immediate:!0}),c=v(()=>{if(!o.state.fulfilled)return null;const r=o.state.fulfilled.value,m=["Story",...r.included_sdks.map(s=>s.name)],a=r.stories.map(s=>({story:s.name,results:s.results.map(n=>n.status)}));return{headers:m,rows:a}});return(r,m)=>c.value?(t(),e("table",M,[i("thead",null,[(t(!0),e(d,null,u(c.value.headers,a=>(t(),e("th",{key:a},_(a),1))),128))]),i("tbody",null,[(t(!0),e(d,null,u(c.value.rows,(a,s)=>(t(),e("tr",{key:s},[i("td",null,_(a.story),1),(t(!0),e(d,null,u(a.results,(n,h)=>(t(),e("td",{key:h,class:"status-cell",title:`Status: ${n}`},[x(b,{status:n},null,8,["status"])],8,j))),128))]))),128))])])):(t(),e("div",B,[p(o).state.pending?(t(),e("div",F,N)):p(o).state.rejected?(t(),e("div",V," Failed to load compatibility matrix data: "+_(p(o).state.rejected.reason),1)):g("",!0)]))}});const E=T(w,[["__scopeId","data-v-ba4f590a"]]);export{E as default}; diff --git a/assets/chunks/CompatibilityMatrixTableIcon.e79f5bb9.js b/assets/chunks/CompatibilityMatrixTableIcon.e79f5bb9.js new file mode 100644 index 000000000..92bf5cb0d --- /dev/null +++ b/assets/chunks/CompatibilityMatrixTableIcon.e79f5bb9.js @@ -0,0 +1 @@ +import{_ as o}from"./CompatibilityMatrixTableIcon.vue_vue_type_style_index_0_lang.db34105e.js";import"./IconCheck.ce061fbd.js";import"./framework.1293becd.js";export{o as default}; diff --git a/assets/chunks/CompatibilityMatrixTableIcon.vue_vue_type_style_index_0_lang.db34105e.js b/assets/chunks/CompatibilityMatrixTableIcon.vue_vue_type_style_index_0_lang.db34105e.js new file mode 100644 index 000000000..a4df2e538 --- /dev/null +++ b/assets/chunks/CompatibilityMatrixTableIcon.vue_vue_type_style_index_0_lang.db34105e.js @@ -0,0 +1 @@ +import{I as _}from"./IconCheck.ce061fbd.js";import{_ as c,o as n,c as r,k as i,d as q,g as u,b as d,I as m}from"./framework.1293becd.js";const p={name:"MaterialSymbolsCancelOutlineRounded"},h={xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 24 24"},T=i("path",{fill:"currentColor",d:"m12 13.4l2.9 2.9q.275.275.7.275t.7-.275q.275-.275.275-.7t-.275-.7L13.4 12l2.9-2.9q.275-.275.275-.7t-.275-.7q-.275-.275-.7-.275t-.7.275L12 10.6L9.1 7.7q-.275-.275-.7-.275t-.7.275q-.275.275-.275.7t.275.7l2.9 2.9l-2.9 2.9q-.275.275-.275.7t.275.7q.275.275.7.275t.7-.275l2.9-2.9Zm0 8.6q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22Zm0-2q3.35 0 5.675-2.325T20 12q0-3.35-2.325-5.675T12 4Q8.65 4 6.325 6.325T4 12q0 3.35 2.325 5.675T12 20Zm0-8Z"},null,-1),f=[T];function $(t,e,o,s,a,l){return n(),r("svg",h,f)}const w=c(p,[["render",$]]),x={name:"MaterialSymbolsQuestionMarkRounded"},g={xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 24 24"},v=i("path",{fill:"currentColor",d:"M12.025 16q-.6 0-1.012-.425t-.363-1q.075-1.05.5-1.825t1.35-1.6q1.025-.9 1.563-1.563t.537-1.512q0-1.025-.687-1.7T12 5.7q-.8 0-1.363.338t-.912.837q-.35.5-.862.675t-.988-.025q-.575-.25-.787-.825t.087-1.075Q7.9 4.5 9.125 3.75T12 3q2.625 0 4.038 1.462t1.412 3.513q0 1.25-.537 2.138t-1.688 2.012q-.85.8-1.2 1.3t-.475 1.15q-.1.625-.525 1.025t-1 .4ZM12 22q-.825 0-1.413-.588T10 20q0-.825.588-1.413T12 18q.825 0 1.413.588T14 20q0 .825-.588 1.413T12 22Z"},null,-1),C=[v];function b(t,e,o,s,a,l){return n(),r("svg",g,C)}const k=c(x,[["render",b]]),I=q({__name:"CompatibilityMatrixTableIcon",props:{status:{}},setup(t){const e=t,o=u(()=>{switch(e.status){case"ok":return _;case"failed":return w;case"no-data":return k}});return(s,a)=>(n(),d(m(o.value),{"data-status":s.status,class:"compat-matrix-table-icon"},null,8,["data-status"]))}});export{I as _}; diff --git a/assets/chunks/IconCheck.ce061fbd.js b/assets/chunks/IconCheck.ce061fbd.js new file mode 100644 index 000000000..fd670afb0 --- /dev/null +++ b/assets/chunks/IconCheck.ce061fbd.js @@ -0,0 +1 @@ +import{_ as t,o as e,c as o,k as s}from"./framework.1293becd.js";const c={name:"MaterialSymbolsCheckCircleOutlineRounded"},n={xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 24 24"},r=s("path",{fill:"currentColor",d:"m10.6 13.8l-2.15-2.15q-.275-.275-.7-.275t-.7.275q-.275.275-.275.7t.275.7L9.9 15.9q.3.3.7.3t.7-.3l5.65-5.65q.275-.275.275-.7t-.275-.7q-.275-.275-.7-.275t-.7.275L10.6 13.8ZM12 22q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22Zm0-2q3.35 0 5.675-2.325T20 12q0-3.35-2.325-5.675T12 4Q8.65 4 6.325 6.325T4 12q0 3.35 2.325 5.675T12 20Zm0-8Z"},null,-1),a=[r];function q(l,_,i,m,d,h){return e(),o("svg",n,a)}const T=t(c,[["render",q]]);export{T as I}; diff --git a/assets/chunks/MermaidRender.7316e2cd.js b/assets/chunks/MermaidRender.7316e2cd.js new file mode 100644 index 000000000..b98b34586 --- /dev/null +++ b/assets/chunks/MermaidRender.7316e2cd.js @@ -0,0 +1 @@ +import{a as g,u as k}from"./vue-kakuyaku.a530ead7.js";import{d as y,g as u,h as x,ao as D,ap as R,u as S,aq as b,o as n,c as r,F as m,k as s,t as p}from"./framework.1293becd.js";import v from"https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";async function j(d,t,l){v.initialize({startOnLoad:!0,theme:l.theme});const{svg:a}=await v.render(d,t);return{svg:a}}const B={ref:"root"},L=["innerHTML"],M={class:"custom-block danger"},T=s("p",{class:"custom-block-title"}," Unable to render the diagram ",-1),$={class:"language-mermaid"},C={key:1,class:"flex justify-center p-8"},I=y({__name:"MermaidRender",props:{id:{},text:{}},setup(d){const t=d,l=u(()=>decodeURIComponent(t.text)),a=x(!1);D(R("root"),([{isIntersecting:e}])=>{a.value||(a.value=e)});const{isDark:f}=S(),c=b(()=>f.value?"dark":"light"),_=g(()=>a.value&&{key:`${t.id}-${t.text}-${c.value}`,payload:{id:t.id,text:l.value,theme:c.value}},({payload:{id:e,text:i,theme:h}})=>k(()=>j(e,i,{theme:h}),{immediate:!0})),o=u(()=>{var e;return(e=_.value)==null?void 0:e.expose.state});return(e,i)=>(n(),r("div",B,[o.value&&!o.value.pending?(n(),r(m,{key:0},[o.value.fulfilled?(n(),r("pre",{key:0,"data-mermaid":"",class:"flex justify-center",innerHTML:o.value.fulfilled.value.svg},null,8,L)):(n(),r(m,{key:1},[s("div",M,[T,s("p",null,p(o.value.rejected.reason),1)]),s("div",$,[s("pre",null,[s("code",null,p(l.value),1)])])],64))],64)):(n(),r("div",C," Rendering the diagram... "))],512))}});export{I as default}; diff --git a/assets/chunks/ShareFeedback.9f629070.js b/assets/chunks/ShareFeedback.9f629070.js new file mode 100644 index 000000000..33081993e --- /dev/null +++ b/assets/chunks/ShareFeedback.9f629070.js @@ -0,0 +1,8 @@ +import{ar as lt,ah as _,F as ee,M as O,y as U,h as g,g as h,d as $,j as A,z as C,x as se,X as G,as as ot,at as rt,l as M,E as at,O as st,_ as te,o as L,c as F,k as y,r as Ae,au as it,av as ut,H as S,w as P,a as q,D as dt,aw as ce,ax as ct,t as ft,ay as Te,e as pt,p as vt,m as mt}from"./framework.1293becd.js";import{b as ht,w as gt,c as bt}from"./vue-kakuyaku.a530ead7.js";import{I as yt}from"./IconCheck.ce061fbd.js";function B(e,t,...n){if(e in t){let o=t[e];return typeof o=="function"?o(...n):o}let l=new Error(`Tried to handle "${e}" but there is no handler defined. Only defined handlers are: ${Object.keys(t).map(o=>`"${o}"`).join(", ")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(l,B),l}var he=(e=>(e[e.None=0]="None",e[e.RenderStrategy=1]="RenderStrategy",e[e.Static=2]="Static",e))(he||{}),wt=(e=>(e[e.Unmount=0]="Unmount",e[e.Hidden=1]="Hidden",e))(wt||{});function j({visible:e=!0,features:t=0,ourProps:n,theirProps:l,...o}){var r;let a=je(l,n),s=Object.assign(o,{props:a});if(e||t&2&&a.static)return fe(s);if(t&1){let u=(r=a.unmount)==null||r?0:1;return B(u,{0(){return null},1(){return fe({...o,props:{...a,hidden:!0,style:{display:"none"}}})}})}return fe(s)}function fe({props:e,attrs:t,slots:n,slot:l,name:o}){var r,a;let{as:s,...u}=Et(e,["unmount","static"]),i=(r=n.default)==null?void 0:r.call(n,l),c={};if(l){let m=!1,v=[];for(let[d,f]of Object.entries(l))typeof f=="boolean"&&(m=!0),f===!0&&v.push(d);m&&(c["data-headlessui-state"]=v.join(" "))}if(s==="template"){if(i=Ce(i??[]),Object.keys(u).length>0||Object.keys(t).length>0){let[m,...v]=i??[];if(!_t(m)||v.length>0)throw new Error(['Passing props on "template"!',"",`The current component <${o} /> is rendering a "template".`,"However we need to passthrough the following props:",Object.keys(u).concat(Object.keys(t)).map(p=>p.trim()).filter((p,T,D)=>D.indexOf(p)===T).sort((p,T)=>p.localeCompare(T)).map(p=>` - ${p}`).join(` +`),"","You can apply a few solutions:",['Add an `as="..."` prop, to ensure that we render an actual element instead of a "template".',"Render a single element as the child so that we can forward the props onto that element."].map(p=>` - ${p}`).join(` +`)].join(` +`));let d=je((a=m.props)!=null?a:{},u),f=lt(m,d);for(let p in d)p.startsWith("on")&&(f.props||(f.props={}),f.props[p]=d[p]);return f}return Array.isArray(i)&&i.length===1?i[0]:i}return _(s,Object.assign({},u,c),{default:()=>i})}function Ce(e){return e.flatMap(t=>t.type===ee?Ce(t.children):[t])}function je(...e){if(e.length===0)return{};if(e.length===1)return e[0];let t={},n={};for(let l of e)for(let o in l)o.startsWith("on")&&typeof l[o]=="function"?(n[o]!=null||(n[o]=[]),n[o].push(l[o])):t[o]=l[o];if(t.disabled||t["aria-disabled"])return Object.assign(t,Object.fromEntries(Object.keys(n).map(l=>[l,void 0])));for(let l in n)Object.assign(t,{[l](o,...r){let a=n[l];for(let s of a){if(o instanceof Event&&o.defaultPrevented)return;s(o,...r)}}});return t}function Et(e,t=[]){let n=Object.assign({},e);for(let l of t)l in n&&delete n[l];return n}function _t(e){return e==null?!1:typeof e.type=="string"||typeof e.type=="object"||typeof e.type=="function"}let St=0;function $t(){return++St}function ie(){return $t()}var De=(e=>(e.Space=" ",e.Enter="Enter",e.Escape="Escape",e.Backspace="Backspace",e.Delete="Delete",e.ArrowLeft="ArrowLeft",e.ArrowUp="ArrowUp",e.ArrowRight="ArrowRight",e.ArrowDown="ArrowDown",e.Home="Home",e.End="End",e.PageUp="PageUp",e.PageDown="PageDown",e.Tab="Tab",e))(De||{});function E(e){var t;return e==null||e.value==null?null:(t=e.value.$el)!=null?t:e.value}let xt=Symbol("Context");var Q=(e=>(e[e.Open=1]="Open",e[e.Closed=2]="Closed",e[e.Closing=4]="Closing",e[e.Opening=8]="Opening",e))(Q||{});function kt(){return O(xt,null)}var Tt=Object.defineProperty,Pt=(e,t,n)=>t in e?Tt(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,Pe=(e,t,n)=>(Pt(e,typeof t!="symbol"?t+"":t,n),n);class Lt{constructor(){Pe(this,"current",this.detect()),Pe(this,"currentId",0)}set(t){this.current!==t&&(this.currentId=0,this.current=t)}reset(){this.set(this.detect())}nextId(){return++this.currentId}get isServer(){return this.current==="server"}get isClient(){return this.current==="client"}detect(){return typeof window>"u"||typeof document>"u"?"server":"client"}}let ue=new Lt;function K(e){if(ue.isServer)return null;if(e instanceof Node)return e.ownerDocument;if(e!=null&&e.hasOwnProperty("value")){let t=E(e);if(t)return t.ownerDocument}return document}let ge=["[contentEditable=true]","[tabindex]","a[href]","area[href]","button:not([disabled])","iframe","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].map(e=>`${e}:not([tabindex='-1'])`).join(",");var N=(e=>(e[e.First=1]="First",e[e.Previous=2]="Previous",e[e.Next=4]="Next",e[e.Last=8]="Last",e[e.WrapAround=16]="WrapAround",e[e.NoScroll=32]="NoScroll",e))(N||{}),Ne=(e=>(e[e.Error=0]="Error",e[e.Overflow=1]="Overflow",e[e.Success=2]="Success",e[e.Underflow=3]="Underflow",e))(Ne||{}),Ft=(e=>(e[e.Previous=-1]="Previous",e[e.Next=1]="Next",e))(Ft||{});function Ot(e=document.body){return e==null?[]:Array.from(e.querySelectorAll(ge)).sort((t,n)=>Math.sign((t.tabIndex||Number.MAX_SAFE_INTEGER)-(n.tabIndex||Number.MAX_SAFE_INTEGER)))}var Me=(e=>(e[e.Strict=0]="Strict",e[e.Loose=1]="Loose",e))(Me||{});function At(e,t=0){var n;return e===((n=K(e))==null?void 0:n.body)?!1:B(t,{0(){return e.matches(ge)},1(){let l=e;for(;l!==null;){if(l.matches(ge))return!0;l=l.parentElement}return!1}})}var Ct=(e=>(e[e.Keyboard=0]="Keyboard",e[e.Mouse=1]="Mouse",e))(Ct||{});typeof window<"u"&&typeof document<"u"&&(document.addEventListener("keydown",e=>{e.metaKey||e.altKey||e.ctrlKey||(document.documentElement.dataset.headlessuiFocusVisible="")},!0),document.addEventListener("click",e=>{e.detail===1?delete document.documentElement.dataset.headlessuiFocusVisible:e.detail===0&&(document.documentElement.dataset.headlessuiFocusVisible="")},!0));function H(e){e==null||e.focus({preventScroll:!0})}let jt=["textarea","input"].join(",");function Dt(e){var t,n;return(n=(t=e==null?void 0:e.matches)==null?void 0:t.call(e,jt))!=null?n:!1}function Nt(e,t=n=>n){return e.slice().sort((n,l)=>{let o=t(n),r=t(l);if(o===null||r===null)return 0;let a=o.compareDocumentPosition(r);return a&Node.DOCUMENT_POSITION_FOLLOWING?-1:a&Node.DOCUMENT_POSITION_PRECEDING?1:0})}function re(e,t,{sorted:n=!0,relativeTo:l=null,skipElements:o=[]}={}){var r;let a=(r=Array.isArray(e)?e.length>0?e[0].ownerDocument:document:e==null?void 0:e.ownerDocument)!=null?r:document,s=Array.isArray(e)?n?Nt(e):e:Ot(e);o.length>0&&s.length>1&&(s=s.filter(f=>!o.includes(f))),l=l??a.activeElement;let u=(()=>{if(t&5)return 1;if(t&10)return-1;throw new Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),i=(()=>{if(t&1)return 0;if(t&2)return Math.max(0,s.indexOf(l))-1;if(t&4)return Math.max(0,s.indexOf(l))+1;if(t&8)return s.length-1;throw new Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),c=t&32?{preventScroll:!0}:{},m=0,v=s.length,d;do{if(m>=v||m+v<=0)return 0;let f=i+m;if(t&16)f=(f+v)%v;else{if(f<0)return 3;if(f>=v)return 1}d=s[f],d==null||d.focus(c),m+=u}while(d!==a.activeElement);return t&6&&Dt(d)&&d.select(),2}function le(e,t,n){ue.isServer||U(l=>{document.addEventListener(e,t,n),l(()=>document.removeEventListener(e,t,n))})}function Re(e,t,n){ue.isServer||U(l=>{window.addEventListener(e,t,n),l(()=>window.removeEventListener(e,t,n))})}function Mt(e,t,n=h(()=>!0)){function l(r,a){if(!n.value||r.defaultPrevented)return;let s=a(r);if(s===null||!s.getRootNode().contains(s))return;let u=function i(c){return typeof c=="function"?i(c()):Array.isArray(c)||c instanceof Set?c:[c]}(e);for(let i of u){if(i===null)continue;let c=i instanceof HTMLElement?i:E(i);if(c!=null&&c.contains(s)||r.composed&&r.composedPath().includes(c))return}return!At(s,Me.Loose)&&s.tabIndex!==-1&&r.preventDefault(),t(r,s)}let o=g(null);le("pointerdown",r=>{var a,s;n.value&&(o.value=((s=(a=r.composedPath)==null?void 0:a.call(r))==null?void 0:s[0])||r.target)},!0),le("mousedown",r=>{var a,s;n.value&&(o.value=((s=(a=r.composedPath)==null?void 0:a.call(r))==null?void 0:s[0])||r.target)},!0),le("click",r=>{o.value&&(l(r,()=>o.value),o.value=null)},!0),le("touchend",r=>l(r,()=>r.target instanceof HTMLElement?r.target:null),!0),Re("blur",r=>l(r,()=>window.document.activeElement instanceof HTMLIFrameElement?window.document.activeElement:null),!0)}var ae=(e=>(e[e.None=1]="None",e[e.Focusable=2]="Focusable",e[e.Hidden=4]="Hidden",e))(ae||{});let be=$({name:"Hidden",props:{as:{type:[Object,String],default:"div"},features:{type:Number,default:1}},setup(e,{slots:t,attrs:n}){return()=>{let{features:l,...o}=e,r={"aria-hidden":(l&2)===2?!0:void 0,style:{position:"fixed",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",...(l&4)===4&&(l&2)!==2&&{display:"none"}}};return j({ourProps:r,theirProps:o,slot:{},attrs:n,slots:t,name:"Hidden"})}}});function Rt(){return/iPhone/gi.test(window.navigator.platform)||/Mac/gi.test(window.navigator.platform)&&window.navigator.maxTouchPoints>0}function _e(e){typeof queueMicrotask=="function"?queueMicrotask(e):Promise.resolve().then(e).catch(t=>setTimeout(()=>{throw t}))}function Se(){let e=[],t={addEventListener(n,l,o,r){return n.addEventListener(l,o,r),t.add(()=>n.removeEventListener(l,o,r))},requestAnimationFrame(...n){let l=requestAnimationFrame(...n);t.add(()=>cancelAnimationFrame(l))},nextFrame(...n){t.requestAnimationFrame(()=>{t.requestAnimationFrame(...n)})},setTimeout(...n){let l=setTimeout(...n);t.add(()=>clearTimeout(l))},microTask(...n){let l={current:!0};return _e(()=>{l.current&&n[0]()}),t.add(()=>{l.current=!1})},style(n,l,o){let r=n.style.getPropertyValue(l);return Object.assign(n.style,{[l]:o}),this.add(()=>{Object.assign(n.style,{[l]:r})})},group(n){let l=Se();return n(l),this.add(()=>l.dispose())},add(n){return e.push(n),()=>{let l=e.indexOf(n);if(l>=0)for(let o of e.splice(l,1))o()}},dispose(){for(let n of e.splice(0))n()}};return t}var Z=(e=>(e[e.Forwards=0]="Forwards",e[e.Backwards=1]="Backwards",e))(Z||{});function It(){let e=g(0);return Re("keydown",t=>{t.key==="Tab"&&(e.value=t.shiftKey?1:0)}),e}function Ie(e,t,n,l){ue.isServer||U(o=>{e=e??window,e.addEventListener(t,n,l),o(()=>e.removeEventListener(t,n,l))})}function Ht(e){function t(){document.readyState!=="loading"&&(e(),document.removeEventListener("DOMContentLoaded",t))}typeof window<"u"&&typeof document<"u"&&(document.addEventListener("DOMContentLoaded",t),t())}function He(e){if(!e)return new Set;if(typeof e=="function")return new Set(e());let t=new Set;for(let n of e.value){let l=E(n);l instanceof HTMLElement&&t.add(l)}return t}var Be=(e=>(e[e.None=1]="None",e[e.InitialFocus=2]="InitialFocus",e[e.TabLock=4]="TabLock",e[e.FocusLock=8]="FocusLock",e[e.RestoreFocus=16]="RestoreFocus",e[e.All=30]="All",e))(Be||{});let Y=Object.assign($({name:"FocusTrap",props:{as:{type:[Object,String],default:"div"},initialFocus:{type:Object,default:null},features:{type:Number,default:30},containers:{type:[Object,Function],default:g(new Set)}},inheritAttrs:!1,setup(e,{attrs:t,slots:n,expose:l}){let o=g(null);l({el:o,$el:o});let r=h(()=>K(o)),a=g(!1);A(()=>a.value=!0),C(()=>a.value=!1),Ut({ownerDocument:r},h(()=>a.value&&!!(e.features&16)));let s=Vt({ownerDocument:r,container:o,initialFocus:h(()=>e.initialFocus)},h(()=>a.value&&!!(e.features&2)));Wt({ownerDocument:r,container:o,containers:e.containers,previousActiveElement:s},h(()=>a.value&&!!(e.features&8)));let u=It();function i(d){let f=E(o);f&&(p=>p())(()=>{B(u.value,{[Z.Forwards]:()=>{re(f,N.First,{skipElements:[d.relatedTarget]})},[Z.Backwards]:()=>{re(f,N.Last,{skipElements:[d.relatedTarget]})}})})}let c=g(!1);function m(d){d.key==="Tab"&&(c.value=!0,requestAnimationFrame(()=>{c.value=!1}))}function v(d){if(!a.value)return;let f=He(e.containers);E(o)instanceof HTMLElement&&f.add(E(o));let p=d.relatedTarget;p instanceof HTMLElement&&p.dataset.headlessuiFocusGuard!=="true"&&(Ue(f,p)||(c.value?re(E(o),B(u.value,{[Z.Forwards]:()=>N.Next,[Z.Backwards]:()=>N.Previous})|N.WrapAround,{relativeTo:d.target}):d.target instanceof HTMLElement&&H(d.target)))}return()=>{let d={},f={ref:o,onKeydown:m,onFocusout:v},{features:p,initialFocus:T,containers:D,...z}=e;return _(ee,[!!(p&4)&&_(be,{as:"button",type:"button","data-headlessui-focus-guard":!0,onFocus:i,features:ae.Focusable}),j({ourProps:f,theirProps:{...t,...z},slot:d,attrs:t,slots:n,name:"FocusTrap"}),!!(p&4)&&_(be,{as:"button",type:"button","data-headlessui-focus-guard":!0,onFocus:i,features:ae.Focusable})])}}}),{features:Be}),R=[];Ht(()=>{function e(t){t.target instanceof HTMLElement&&t.target!==document.body&&R[0]!==t.target&&(R.unshift(t.target),R=R.filter(n=>n!=null&&n.isConnected),R.splice(10))}window.addEventListener("click",e,{capture:!0}),window.addEventListener("mousedown",e,{capture:!0}),window.addEventListener("focus",e,{capture:!0}),document.body.addEventListener("click",e,{capture:!0}),document.body.addEventListener("mousedown",e,{capture:!0}),document.body.addEventListener("focus",e,{capture:!0})});function Bt(e){let t=g(R.slice());return se([e],([n],[l])=>{l===!0&&n===!1?_e(()=>{t.value.splice(0)}):l===!1&&n===!0&&(t.value=R.slice())},{flush:"post"}),()=>{var n;return(n=t.value.find(l=>l!=null&&l.isConnected))!=null?n:null}}function Ut({ownerDocument:e},t){let n=Bt(t);A(()=>{U(()=>{var l,o;t.value||((l=e.value)==null?void 0:l.activeElement)===((o=e.value)==null?void 0:o.body)&&H(n())},{flush:"post"})}),C(()=>{t.value&&H(n())})}function Vt({ownerDocument:e,container:t,initialFocus:n},l){let o=g(null),r=g(!1);return A(()=>r.value=!0),C(()=>r.value=!1),A(()=>{se([t,n,l],(a,s)=>{if(a.every((i,c)=>(s==null?void 0:s[c])===i)||!l.value)return;let u=E(t);u&&_e(()=>{var i,c;if(!r.value)return;let m=E(n),v=(i=e.value)==null?void 0:i.activeElement;if(m){if(m===v){o.value=v;return}}else if(u.contains(v)){o.value=v;return}m?H(m):re(u,N.First|N.NoScroll)===Ne.Error&&console.warn("There are no focusable elements inside the "),o.value=(c=e.value)==null?void 0:c.activeElement})},{immediate:!0,flush:"post"})}),o}function Wt({ownerDocument:e,container:t,containers:n,previousActiveElement:l},o){var r;Ie((r=e.value)==null?void 0:r.defaultView,"focus",a=>{if(!o.value)return;let s=He(n);E(t)instanceof HTMLElement&&s.add(E(t));let u=l.value;if(!u)return;let i=a.target;i&&i instanceof HTMLElement?Ue(s,i)?(l.value=i,H(i)):(a.preventDefault(),a.stopPropagation(),H(u)):H(l.value)},!0)}function Ue(e,t){for(let n of e)if(n.contains(t))return!0;return!1}let pe=new Map,J=new Map;function Le(e,t=g(!0)){U(n=>{var l;if(!t.value)return;let o=E(e);if(!o)return;n(function(){var a;if(!o)return;let s=(a=J.get(o))!=null?a:1;if(s===1?J.delete(o):J.set(o,s-1),s!==1)return;let u=pe.get(o);u&&(u["aria-hidden"]===null?o.removeAttribute("aria-hidden"):o.setAttribute("aria-hidden",u["aria-hidden"]),o.inert=u.inert,pe.delete(o))});let r=(l=J.get(o))!=null?l:0;J.set(o,r+1),r===0&&(pe.set(o,{"aria-hidden":o.getAttribute("aria-hidden"),inert:o.inert}),o.setAttribute("aria-hidden","true"),o.inert=!0)})}let Ve=Symbol("ForcePortalRootContext");function qt(){return O(Ve,!1)}let Fe=$({name:"ForcePortalRoot",props:{as:{type:[Object,String],default:"template"},force:{type:Boolean,default:!1}},setup(e,{slots:t,attrs:n}){return G(Ve,e.force),()=>{let{force:l,...o}=e;return j({theirProps:o,ourProps:{},slot:{},slots:t,attrs:n,name:"ForcePortalRoot"})}}});function Gt(e){let t=K(e);if(!t){if(e===null)return null;throw new Error(`[Headless UI]: Cannot find ownerDocument for contextElement: ${e}`)}let n=t.getElementById("headlessui-portal-root");if(n)return n;let l=t.createElement("div");return l.setAttribute("id","headlessui-portal-root"),t.body.appendChild(l)}let Kt=$({name:"Portal",props:{as:{type:[Object,String],default:"div"}},setup(e,{slots:t,attrs:n}){let l=g(null),o=h(()=>K(l)),r=qt(),a=O(We,null),s=g(r===!0||a==null?Gt(l.value):a.resolveTarget());U(()=>{r||a!=null&&(s.value=a.resolveTarget())});let u=O(ye,null);return A(()=>{let i=E(l);i&&u&&C(u.register(i))}),C(()=>{var i,c;let m=(i=o.value)==null?void 0:i.getElementById("headlessui-portal-root");m&&s.value===m&&s.value.children.length<=0&&((c=s.value.parentElement)==null||c.removeChild(s.value))}),()=>{if(s.value===null)return null;let i={ref:l,"data-headlessui-portal":""};return _(ot,{to:s.value},j({ourProps:i,theirProps:e,slot:{},attrs:n,slots:t,name:"Portal"}))}}}),ye=Symbol("PortalParentContext");function zt(){let e=O(ye,null),t=g([]);function n(r){return t.value.push(r),e&&e.register(r),()=>l(r)}function l(r){let a=t.value.indexOf(r);a!==-1&&t.value.splice(a,1),e&&e.unregister(r)}let o={register:n,unregister:l,portals:t};return[t,$({name:"PortalWrapper",setup(r,{slots:a}){return G(ye,o),()=>{var s;return(s=a.default)==null?void 0:s.call(a)}}})]}let We=Symbol("PortalGroupContext"),Xt=$({name:"PortalGroup",props:{as:{type:[Object,String],default:"template"},target:{type:Object,default:null}},setup(e,{attrs:t,slots:n}){let l=rt({resolveTarget(){return e.target}});return G(We,l),()=>{let{target:o,...r}=e;return j({theirProps:r,ourProps:{},slot:{},attrs:t,slots:n,name:"PortalGroup"})}}}),qe=Symbol("StackContext");var we=(e=>(e[e.Add=0]="Add",e[e.Remove=1]="Remove",e))(we||{});function Yt(){return O(qe,()=>{})}function Jt({type:e,enabled:t,element:n,onUpdate:l}){let o=Yt();function r(...a){l==null||l(...a),o(...a)}A(()=>{se(t,(a,s)=>{a?r(0,e,n):s===!0&&r(1,e,n)},{immediate:!0,flush:"sync"})}),C(()=>{t.value&&r(1,e,n)}),G(qe,r)}let Ge=Symbol("DescriptionContext");function Qt(){let e=O(Ge,null);if(e===null)throw new Error("Missing parent");return e}function Zt({slot:e=g({}),name:t="Description",props:n={}}={}){let l=g([]);function o(r){return l.value.push(r),()=>{let a=l.value.indexOf(r);a!==-1&&l.value.splice(a,1)}}return G(Ge,{register:o,slot:e,name:t,props:n}),h(()=>l.value.length>0?l.value.join(" "):void 0)}let en=$({name:"Description",props:{as:{type:[Object,String],default:"p"},id:{type:String,default:()=>`headlessui-description-${ie()}`}},setup(e,{attrs:t,slots:n}){let l=Qt();return A(()=>C(l.register(e.id))),()=>{let{name:o="Description",slot:r=g({}),props:a={}}=l,{id:s,...u}=e,i={...Object.entries(a).reduce((c,[m,v])=>Object.assign(c,{[m]:M(v)}),{}),id:s};return j({ourProps:i,theirProps:u,slot:r.value,attrs:t,slots:n,name:o})}}});function tn(e){let t=at(e.getSnapshot());return C(e.subscribe(()=>{t.value=e.getSnapshot()})),t}function nn(e,t){let n=e(),l=new Set;return{getSnapshot(){return n},subscribe(o){return l.add(o),()=>l.delete(o)},dispatch(o,...r){let a=t[o].call(n,...r);a&&(n=a,l.forEach(s=>s()))}}}function ln(){let e;return{before({doc:t}){var n;let l=t.documentElement;e=((n=t.defaultView)!=null?n:window).innerWidth-l.clientWidth},after({doc:t,d:n}){let l=t.documentElement,o=l.clientWidth-l.offsetWidth,r=e-o;n.style(l,"paddingRight",`${r}px`)}}}function on(){if(!Rt())return{};let e;return{before(){e=window.pageYOffset},after({doc:t,d:n,meta:l}){function o(a){return l.containers.flatMap(s=>s()).some(s=>s.contains(a))}if(window.getComputedStyle(t.documentElement).scrollBehavior!=="auto"){let a=Se();a.style(t.documentElement,"scroll-behavior","auto"),n.add(()=>n.microTask(()=>a.dispose()))}n.style(t.body,"marginTop",`-${e}px`),window.scrollTo(0,0);let r=null;n.addEventListener(t,"click",a=>{if(a.target instanceof HTMLElement)try{let s=a.target.closest("a");if(!s)return;let{hash:u}=new URL(s.href),i=t.querySelector(u);i&&!o(i)&&(r=i)}catch{}},!0),n.addEventListener(t,"touchmove",a=>{a.target instanceof HTMLElement&&!o(a.target)&&a.preventDefault()},{passive:!1}),n.add(()=>{window.scrollTo(0,window.pageYOffset+e),r&&r.isConnected&&(r.scrollIntoView({block:"nearest"}),r=null)})}}}function rn(){return{before({doc:e,d:t}){t.style(e.documentElement,"overflow","hidden")}}}function an(e){let t={};for(let n of e)Object.assign(t,n(t));return t}let I=nn(()=>new Map,{PUSH(e,t){var n;let l=(n=this.get(e))!=null?n:{doc:e,count:0,d:Se(),meta:new Set};return l.count++,l.meta.add(t),this.set(e,l),this},POP(e,t){let n=this.get(e);return n&&(n.count--,n.meta.delete(t)),this},SCROLL_PREVENT({doc:e,d:t,meta:n}){let l={doc:e,d:t,meta:an(n)},o=[on(),ln(),rn()];o.forEach(({before:r})=>r==null?void 0:r(l)),o.forEach(({after:r})=>r==null?void 0:r(l))},SCROLL_ALLOW({d:e}){e.dispose()},TEARDOWN({doc:e}){this.delete(e)}});I.subscribe(()=>{let e=I.getSnapshot(),t=new Map;for(let[n]of e)t.set(n,n.documentElement.style.overflow);for(let n of e.values()){let l=t.get(n.doc)==="hidden",o=n.count!==0;(o&&!l||!o&&l)&&I.dispatch(n.count>0?"SCROLL_PREVENT":"SCROLL_ALLOW",n),n.count===0&&I.dispatch("TEARDOWN",n)}});function sn(e,t,n){let l=tn(I),o=h(()=>{let r=e.value?l.value.get(e.value):void 0;return r?r.count>0:!1});return se([e,t],([r,a],[s],u)=>{if(!r||!a)return;I.dispatch("PUSH",r,n);let i=!1;u(()=>{i||(I.dispatch("POP",s??r,n),i=!0)})},{immediate:!0}),o}function un({defaultContainers:e=[],portals:t,mainTreeNodeRef:n}={}){let l=g(null),o=K(l);function r(){var a;let s=[];for(let u of e)u!==null&&(u instanceof HTMLElement?s.push(u):"value"in u&&u.value instanceof HTMLElement&&s.push(u.value));if(t!=null&&t.value)for(let u of t.value)s.push(u);for(let u of(a=o==null?void 0:o.querySelectorAll("html > *, body > *"))!=null?a:[])u!==document.body&&u!==document.head&&u instanceof HTMLElement&&u.id!=="headlessui-portal-root"&&(u.contains(E(l))||s.some(i=>u.contains(i))||s.push(u));return s}return{resolveContainers:r,contains(a){return r().some(s=>s.contains(a))},mainTreeNodeRef:l,MainTreeNode(){return n!=null?null:_(be,{features:ae.Hidden,ref:l})}}}var dn=(e=>(e[e.Open=0]="Open",e[e.Closed=1]="Closed",e))(dn||{});let Ee=Symbol("DialogContext");function $e(e){let t=O(Ee,null);if(t===null){let n=new Error(`<${e} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(n,$e),n}return t}let oe="DC8F892D-2EBD-447C-A4C8-A03058436FF4",cn=$({name:"Dialog",inheritAttrs:!1,props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},open:{type:[Boolean,String],default:oe},initialFocus:{type:Object,default:null},id:{type:String,default:()=>`headlessui-dialog-${ie()}`}},emits:{close:e=>!0},setup(e,{emit:t,attrs:n,slots:l,expose:o}){var r;let a=g(!1);A(()=>{a.value=!0});let s=g(0),u=kt(),i=h(()=>e.open===oe&&u!==null?(u.value&Q.Open)===Q.Open:e.open),c=g(null),m=h(()=>K(c));if(o({el:c,$el:c}),!(e.open!==oe||u!==null))throw new Error("You forgot to provide an `open` prop to the `Dialog`.");if(typeof i.value!="boolean")throw new Error(`You provided an \`open\` prop to the \`Dialog\`, but the value is not a boolean. Received: ${i.value===oe?void 0:e.open}`);let v=h(()=>a.value&&i.value?0:1),d=h(()=>v.value===0),f=h(()=>s.value>1),p=O(Ee,null)!==null,[T,D]=zt(),{resolveContainers:z,mainTreeNodeRef:xe,MainTreeNode:Ke}=un({portals:T,defaultContainers:[h(()=>{var b;return(b=X.panelRef.value)!=null?b:c.value})]}),ze=h(()=>f.value?"parent":"leaf"),ke=h(()=>u!==null?(u.value&Q.Closing)===Q.Closing:!1),Xe=h(()=>p||ke.value?!1:d.value),Ye=h(()=>{var b,w,x;return(x=Array.from((w=(b=m.value)==null?void 0:b.querySelectorAll("body > *"))!=null?w:[]).find(k=>k.id==="headlessui-portal-root"?!1:k.contains(E(xe))&&k instanceof HTMLElement))!=null?x:null});Le(Ye,Xe);let Je=h(()=>f.value?!0:d.value),Qe=h(()=>{var b,w,x;return(x=Array.from((w=(b=m.value)==null?void 0:b.querySelectorAll("[data-headlessui-portal]"))!=null?w:[]).find(k=>k.contains(E(xe))&&k instanceof HTMLElement))!=null?x:null});Le(Qe,Je),Jt({type:"Dialog",enabled:h(()=>v.value===0),element:c,onUpdate:(b,w)=>{if(w==="Dialog")return B(b,{[we.Add]:()=>s.value+=1,[we.Remove]:()=>s.value-=1})}});let Ze=Zt({name:"DialogDescription",slot:h(()=>({open:i.value}))}),ne=g(null),X={titleId:ne,panelRef:g(null),dialogState:v,setTitleId(b){ne.value!==b&&(ne.value=b)},close(){t("close",!1)}};G(Ee,X);let et=h(()=>!(!d.value||f.value));Mt(z,(b,w)=>{X.close(),st(()=>w==null?void 0:w.focus())},et);let tt=h(()=>!(f.value||v.value!==0));Ie((r=m.value)==null?void 0:r.defaultView,"keydown",b=>{tt.value&&(b.defaultPrevented||b.key===De.Escape&&(b.preventDefault(),b.stopPropagation(),X.close()))});let nt=h(()=>!(ke.value||v.value!==0||p));return sn(m,nt,b=>{var w;return{containers:[...(w=b.containers)!=null?w:[],z]}}),U(b=>{if(v.value!==0)return;let w=E(c);if(!w)return;let x=new ResizeObserver(k=>{for(let de of k){let W=de.target.getBoundingClientRect();W.x===0&&W.y===0&&W.width===0&&W.height===0&&X.close()}});x.observe(w),b(()=>x.disconnect())}),()=>{let{id:b,open:w,initialFocus:x,...k}=e,de={...n,ref:c,id:b,role:"dialog","aria-modal":v.value===0?!0:void 0,"aria-labelledby":ne.value,"aria-describedby":Ze.value},W={open:v.value===0};return _(Fe,{force:!0},()=>[_(Kt,()=>_(Xt,{target:c.value},()=>_(Fe,{force:!1},()=>_(Y,{initialFocus:x,containers:z,features:d.value?B(ze.value,{parent:Y.features.RestoreFocus,leaf:Y.features.All&~Y.features.FocusLock}):Y.features.None},()=>_(D,{},()=>j({ourProps:de,theirProps:{...k,...n},slot:W,attrs:n,slots:l,visible:v.value===0,features:he.RenderStrategy|he.Static,name:"Dialog"})))))),_(Ke)])}}}),fn=$({name:"DialogPanel",props:{as:{type:[Object,String],default:"div"},id:{type:String,default:()=>`headlessui-dialog-panel-${ie()}`}},setup(e,{attrs:t,slots:n,expose:l}){let o=$e("DialogPanel");l({el:o.panelRef,$el:o.panelRef});function r(a){a.stopPropagation()}return()=>{let{id:a,...s}=e,u={id:a,ref:o.panelRef,onClick:r};return j({ourProps:u,theirProps:s,slot:{open:o.dialogState.value===0},attrs:t,slots:n,name:"DialogPanel"})}}}),pn=$({name:"DialogTitle",props:{as:{type:[Object,String],default:"h2"},id:{type:String,default:()=>`headlessui-dialog-title-${ie()}`}},setup(e,{attrs:t,slots:n}){let l=$e("DialogTitle");return A(()=>{l.setTitleId(e.id),C(()=>l.setTitleId(null))}),()=>{let{id:o,...r}=e;return j({ourProps:{id:o},theirProps:r,slot:{open:l.dialogState.value===0},attrs:t,slots:n,name:"DialogTitle"})}}}),vn=en;/*! + * mande v2.0.6 + * (c) 2023 Eduardo San Martin Morote + * @license MIT + */function mn(e){let t=Object.keys(e).map(n=>[n,e[n]].map(encodeURIComponent).join("=")).join("&");return t?"?"+t:""}let hn=/^\/+/;function gn(e,t){return e+(t&&(e.endsWith("/")?t.replace(hn,""):t.startsWith("/")?t:"/"+t))}function bn(e){return Object.keys(e).reduce((t,n)=>(e[n]!=null&&(t[n]=e[n]),t),{})}const ve={responseAs:"json",headers:{Accept:"application/json","Content-Type":"application/json"},stringify:JSON.stringify};function yn(e,t={},n){function l(r,a,s,u={}){let i,c;typeof a=="object"?(i="",u=s||a||{},c=a):(i=a,c=s);let m={...ve,...o,method:r,...u,headers:bn({...ve.headers,...o.headers,...u.headers})},v={...ve.query,...o.query,...u.query},{responseAs:d}=m;i=gn(e,typeof i=="number"?""+i:i||""),i+=mn(v),r[0]==="P"&&c&&(m.body=m.stringify(c));const f=typeof fetch<"u"?fetch:n;if(!f)throw new Error("No fetch function exists. Make sure to include a polyfill on Node.js.");return f(i,m).then(p=>Promise.all([p,d==="response"?p:p[d]().catch(()=>null)])).then(([p,T])=>{if(p.status>=200&&p.status<300)return d!=="response"&&p.status==204?null:T;let D=new Error(p.statusText);throw D.response=p,D.body=T,D})}const o={query:{},headers:{},...t};return{options:o,post:l.bind(null,"POST"),put:l.bind(null,"PUT"),patch:l.bind(null,"PATCH"),get:(r,a)=>l("GET",r,null,a),delete:(r,a)=>l("DELETE",r,null,a)}}const wn={name:"MaterialSymbolsClose"},En={xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 24 24"},_n=y("path",{fill:"currentColor",d:"M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6L6.4 19Z"},null,-1),Sn=[_n];function $n(e,t,n,l,o,r){return L(),F("svg",En,Sn)}const xn=te(wn,[["render",$n]]),kn={name:"IcOutlineFeedback"},Tn={xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 24 24"},Pn=y("path",{fill:"currentColor",d:"M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17l-.59.59l-.58.58V4h16v12zm-9-4h2v2h-2zm0-6h2v4h-2z"},null,-1),Ln=[Pn];function Fn(e,t,n,l,o,r){return L(),F("svg",Tn,Ln)}const On=te(kn,[["render",Fn]]);const An={},Cn={class:"v-btn-primary"};function jn(e,t){return L(),F("button",Cn,[Ae(e.$slots,"default")])}const Oe=te(An,[["render",jn]]),Dn={},Nn={class:"font-medium rounded-lg bg-vp-bg px-3 py-1.5 hover:bg-vp-brand-soft hover:text-vp-brand-2"};function Mn(e,t){return L(),F("button",Nn,[Ae(e.$slots,"default")])}const me=te(Dn,[["render",Mn]]);function Rn(e){return h(()=>!it(e))}const V=e=>(vt("data-v-701191ba"),e=e(),mt(),e),In=V(()=>y("span",null,"Share feedback",-1)),Hn=V(()=>y("div",{class:"fixed inset-0 bg-black/30 z-90","aria-hidden":"true"},null,-1)),Bn={class:"fixed inset-0 flex items-center justify-center p-4 z-90"},Un={class:"feedback-card_header flex items-center"},Vn={class:"p-4 flex items-center space-x-4"},Wn=V(()=>y("div",null,"Thank you for sharing your feedback!",-1)),qn={class:"flex flex-row-reverse p-4"},Gn={key:1,class:"flex-1 overflow-y-scroll"},Kn={class:"p-4 space-y-4"},zn={class:"space-y-1"},Xn=V(()=>y("legend",{class:"field-label"}," Feedback type* ",-1)),Yn=["id","value"],Jn=["for"],Qn=V(()=>y("label",{for:"feedback-input-text",class:"field-label"},"Feedback*",-1)),Zn=["placeholder"],el=V(()=>y("label",{for:"feedback-input-contact",class:"field-label"},[y("i",null,"(optional)"),q(" Contact information ")],-1)),tl={key:0,class:"px-4 text-xs"},nl={class:"flex p-4 items-center space-x-2"},ll=V(()=>y("div",{class:"flex-1"},null,-1)),ol=$({__name:"ShareFeedback",props:{feedbackUrl:{}},setup(e){const t=e,n=g(!1),l=["bug","suggestion","other"],o={suggestion:"Suggestion ✨",bug:"Bug 🐞",other:"Other"},r=g(null),a=g(""),s=g(""),u=h(()=>{switch(r.value){case"bug":return"Report any bugs or issues you found in Iroha 2 documentation";default:return"What can we do to improve the overall documentation browsing experience?"}}),i=ht(),c=g(!1);gt(i.state,()=>{c.value=!0,a.value=s.value=""}),bt(i.state,v=>{console.error("Feedback rejection reason:",v)}),ut(Rn(n),()=>{c.value=!1});function m(){const v={kind:r.value,feedback:a.value,contact:s.value,location:window.location},d=yn(t.feedbackUrl);i.set(d.post(v))}return(v,d)=>(L(),F(ee,null,[S(Oe,{class:"inline-flex items-center space-x-2",onClick:d[0]||(d[0]=f=>n.value=!0)},{default:P(()=>[S(On),In]),_:1}),S(M(cn),{open:n.value,onClose:d[7]||(d[7]=f=>n.value=!1)},{default:P(()=>[Hn,y("div",Bn,[S(M(fn),{class:"feedback-card shadow-lg flex flex-col"},{default:P(()=>[y("div",Un,[S(M(pn),{class:"feedback-card_title flex-1"},{default:P(()=>[q(" Share feedback ")]),_:1}),S(me,{class:"text-base p-2 -m-2",onClick:d[1]||(d[1]=f=>n.value=!1)},{default:P(()=>[S(xn)]),_:1})]),c.value?(L(),F(ee,{key:0},[y("div",Vn,[S(yt,{class:"text-3xl feedback-card_check"}),Wn]),y("div",qn,[S(me,{onClick:d[2]||(d[2]=f=>n.value=!1)},{default:P(()=>[q(" Close ")]),_:1})])],64)):(L(),F("div",Gn,[y("div",Kn,[S(M(vn),{class:"text-sm"},{default:P(()=>[q(" Please take a moment to help us improve the Iroha 2 Documentation. We take your input very seriously. ")]),_:1}),y("div",null,[y("fieldset",zn,[Xn,(L(),F(ee,null,dt(l,f=>y("div",{key:f,class:"flex space-x-2 items-center"},[ce(y("input",{id:`feedback-kind-${f}`,"onUpdate:modelValue":d[3]||(d[3]=p=>r.value=p),class:"max-w-min",value:f,type:"radio",name:"feedback-kind"},null,8,Yn),[[ct,r.value]]),y("label",{for:`feedback-kind-${f}`,class:"flex-1 text-sm"},ft(o[f]),9,Jn)])),64))])]),y("div",null,[Qn,ce(y("textarea",{id:"feedback-input-text","onUpdate:modelValue":d[4]||(d[4]=f=>a.value=f),placeholder:u.value,rows:"5"},null,8,Zn),[[Te,a.value]])]),y("div",null,[el,ce(y("input",{id:"feedback-input-contact","onUpdate:modelValue":d[5]||(d[5]=f=>s.value=f),placeholder:"Email address, Discord, or Telegram"},null,512),[[Te,s.value]])])]),M(i).state.rejected?(L(),F("div",tl," Unable to send feedback ")):pt("",!0),y("div",nl,[ll,S(me,{onClick:d[6]||(d[6]=f=>n.value=!1)},{default:P(()=>[q(" Cancel ")]),_:1}),S(Oe,{disabled:!a.value||!r.value||M(i).state.pending,onClick:m},{default:P(()=>[q(" Submit ")]),_:1},8,["disabled"])])]))]),_:1})])]),_:1},8,["open"])],64))}});const il=te(ol,[["__scopeId","data-v-701191ba"]]);export{il as default}; diff --git a/assets/chunks/VPLocalSearchBox.3ae6ae48.js b/assets/chunks/VPLocalSearchBox.3ae6ae48.js new file mode 100644 index 000000000..1e6db3b06 --- /dev/null +++ b/assets/chunks/VPLocalSearchBox.3ae6ae48.js @@ -0,0 +1,7 @@ +import{V as vt,h as ne,x as ze,az as Nt,aA as Tt,d as kt,E as ge,aB as Xe,g as ke,aC as It,aD as Dt,y as _t,aE as Ot,j as Ie,O as fe,S as be,aF as Rt,aG as Mt,W as Lt,aH as Pt,o as Z,b as zt,k as S,$ as Bt,l as W,aw as Vt,ay as $t,am as Wt,c as ee,n as et,e as we,D as tt,F as rt,a as de,t as he,as as Kt,p as jt,m as Jt,aj as at,aI as Ut,a7 as Ht,ad as Gt,s as qt,_ as Qt}from"./framework.1293becd.js";import{u as Yt,c as Zt}from"./theme.3c30bd29.js";const Xt={root:()=>vt(()=>import("./@localSearchIndexroot.46c613a1.js"),[])};/*! +* tabbable 6.2.0 +* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE +*/var pt=["input:not([inert])","select:not([inert])","textarea:not([inert])","a[href]:not([inert])","button:not([inert])","[tabindex]:not(slot):not([inert])","audio[controls]:not([inert])","video[controls]:not([inert])",'[contenteditable]:not([contenteditable="false"]):not([inert])',"details>summary:first-of-type:not([inert])","details:not([inert])"],Ee=pt.join(","),yt=typeof Element>"u",oe=yt?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,Se=!yt&&Element.prototype.getRootNode?function(o){var e;return o==null||(e=o.getRootNode)===null||e===void 0?void 0:e.call(o)}:function(o){return o==null?void 0:o.ownerDocument},Ae=function o(e,t){var r;t===void 0&&(t=!0);var n=e==null||(r=e.getAttribute)===null||r===void 0?void 0:r.call(e,"inert"),a=n===""||n==="true",i=a||t&&e&&o(e.parentNode);return i},er=function(e){var t,r=e==null||(t=e.getAttribute)===null||t===void 0?void 0:t.call(e,"contenteditable");return r===""||r==="true"},mt=function(e,t,r){if(Ae(e))return[];var n=Array.prototype.slice.apply(e.querySelectorAll(Ee));return t&&oe.call(e,Ee)&&n.unshift(e),n=n.filter(r),n},gt=function o(e,t,r){for(var n=[],a=Array.from(e);a.length;){var i=a.shift();if(!Ae(i,!1))if(i.tagName==="SLOT"){var s=i.assignedElements(),u=s.length?s:i.children,l=o(u,!0,r);r.flatten?n.push.apply(n,l):n.push({scopeParent:i,candidates:l})}else{var h=oe.call(i,Ee);h&&r.filter(i)&&(t||!e.includes(i))&&n.push(i);var d=i.shadowRoot||typeof r.getShadowRoot=="function"&&r.getShadowRoot(i),v=!Ae(d,!1)&&(!r.shadowRootFilter||r.shadowRootFilter(i));if(d&&v){var y=o(d===!0?i.children:d.children,!0,r);r.flatten?n.push.apply(n,y):n.push({scopeParent:i,candidates:y})}else a.unshift.apply(a,i.children)}}return n},bt=function(e){return!isNaN(parseInt(e.getAttribute("tabindex"),10))},ie=function(e){if(!e)throw new Error("No node provided");return e.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(e.tagName)||er(e))&&!bt(e)?0:e.tabIndex},tr=function(e,t){var r=ie(e);return r<0&&t&&!bt(e)?0:r},rr=function(e,t){return e.tabIndex===t.tabIndex?e.documentOrder-t.documentOrder:e.tabIndex-t.tabIndex},wt=function(e){return e.tagName==="INPUT"},ar=function(e){return wt(e)&&e.type==="hidden"},nr=function(e){var t=e.tagName==="DETAILS"&&Array.prototype.slice.apply(e.children).some(function(r){return r.tagName==="SUMMARY"});return t},ir=function(e,t){for(var r=0;rsummary:first-of-type"),i=a?e.parentElement:e;if(oe.call(i,"details:not([open]) *"))return!0;if(!r||r==="full"||r==="legacy-full"){if(typeof n=="function"){for(var s=e;e;){var u=e.parentElement,l=Se(e);if(u&&!u.shadowRoot&&n(u)===!0)return nt(e);e.assignedSlot?e=e.assignedSlot:!u&&l!==e.ownerDocument?e=l.host:e=u}e=s}if(lr(e))return!e.getClientRects().length;if(r!=="legacy-full")return!0}else if(r==="non-zero-area")return nt(e);return!1},fr=function(e){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(e.tagName))for(var t=e.parentElement;t;){if(t.tagName==="FIELDSET"&&t.disabled){for(var r=0;r=0)},hr=function o(e){var t=[],r=[];return e.forEach(function(n,a){var i=!!n.scopeParent,s=i?n.scopeParent:n,u=tr(s,i),l=i?o(n.candidates):s;u===0?i?t.push.apply(t,l):t.push(s):r.push({documentOrder:a,tabIndex:u,item:n,isScope:i,content:l})}),r.sort(rr).reduce(function(n,a){return a.isScope?n.push.apply(n,a.content):n.push(a.content),n},[]).concat(t)},vr=function(e,t){t=t||{};var r;return t.getShadowRoot?r=gt([e],t.includeContainer,{filter:Be.bind(null,t),flatten:!1,getShadowRoot:t.getShadowRoot,shadowRootFilter:dr}):r=mt(e,t.includeContainer,Be.bind(null,t)),hr(r)},pr=function(e,t){t=t||{};var r;return t.getShadowRoot?r=gt([e],t.includeContainer,{filter:Ce.bind(null,t),flatten:!0,getShadowRoot:t.getShadowRoot}):r=mt(e,t.includeContainer,Ce.bind(null,t)),r},se=function(e,t){if(t=t||{},!e)throw new Error("No node provided");return oe.call(e,Ee)===!1?!1:Be(t,e)},yr=pt.concat("iframe").join(","),De=function(e,t){if(t=t||{},!e)throw new Error("No node provided");return oe.call(e,yr)===!1?!1:Ce(t,e)};/*! +* focus-trap 7.5.2 +* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE +*/function it(o,e){var t=Object.keys(o);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(o);e&&(r=r.filter(function(n){return Object.getOwnPropertyDescriptor(o,n).enumerable})),t.push.apply(t,r)}return t}function ot(o){for(var e=1;e0){var r=e[e.length-1];r!==t&&r.pause()}var n=e.indexOf(t);n===-1||e.splice(n,1),e.push(t)},deactivateTrap:function(e,t){var r=e.indexOf(t);r!==-1&&e.splice(r,1),e.length>0&&e[e.length-1].unpause()}},wr=function(e){return e.tagName&&e.tagName.toLowerCase()==="input"&&typeof e.select=="function"},xr=function(e){return(e==null?void 0:e.key)==="Escape"||(e==null?void 0:e.key)==="Esc"||(e==null?void 0:e.keyCode)===27},ye=function(e){return(e==null?void 0:e.key)==="Tab"||(e==null?void 0:e.keyCode)===9},Fr=function(e){return ye(e)&&!e.shiftKey},Er=function(e){return ye(e)&&e.shiftKey},ut=function(e){return setTimeout(e,0)},lt=function(e,t){var r=-1;return e.every(function(n,a){return t(n)?(r=a,!1):!0}),r},ve=function(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),n=1;n1?p-1:0),k=1;k=0)c=r.activeElement;else{var f=i.tabbableGroups[0],p=f&&f.firstTabbableNode;c=p||h("fallbackFocus")}if(!c)throw new Error("Your focus-trap needs to have at least one focusable element");return c},v=function(){if(i.containerGroups=i.containers.map(function(c){var f=vr(c,a.tabbableOptions),p=pr(c,a.tabbableOptions),w=f.length>0?f[0]:void 0,k=f.length>0?f[f.length-1]:void 0,O=p.find(function(b){return se(b)}),P=p.slice().reverse().find(function(b){return se(b)}),m=!!f.find(function(b){return ie(b)>0});return{container:c,tabbableNodes:f,focusableNodes:p,posTabIndexesFound:m,firstTabbableNode:w,lastTabbableNode:k,firstDomTabbableNode:O,lastDomTabbableNode:P,nextTabbableNode:function(z){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,j=f.indexOf(z);return j<0?G?p.slice(p.indexOf(z)+1).find(function(J){return se(J)}):p.slice(0,p.indexOf(z)).reverse().find(function(J){return se(J)}):f[j+(G?1:-1)]}}}),i.tabbableGroups=i.containerGroups.filter(function(c){return c.tabbableNodes.length>0}),i.tabbableGroups.length<=0&&!h("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times");if(i.containerGroups.find(function(c){return c.posTabIndexesFound})&&i.containerGroups.length>1)throw new Error("At least one node with a positive tabindex was found in one of your focus-trap's multiple containers. Positive tabindexes are only supported in single-container focus-traps.")},y=function F(c){if(c!==!1&&c!==r.activeElement){if(!c||!c.focus){F(d());return}c.focus({preventScroll:!!a.preventScroll}),i.mostRecentlyFocusedNode=c,wr(c)&&c.select()}},g=function(c){var f=h("setReturnFocus",c);return f||(f===!1?!1:c)},E=function(c){var f=c.target,p=c.event,w=c.isBackward,k=w===void 0?!1:w;f=f||xe(p),v();var O=null;if(i.tabbableGroups.length>0){var P=l(f,p),m=P>=0?i.containerGroups[P]:void 0;if(P<0)k?O=i.tabbableGroups[i.tabbableGroups.length-1].lastTabbableNode:O=i.tabbableGroups[0].firstTabbableNode;else if(k){var b=lt(i.tabbableGroups,function(U){var H=U.firstTabbableNode;return f===H});if(b<0&&(m.container===f||De(f,a.tabbableOptions)&&!se(f,a.tabbableOptions)&&!m.nextTabbableNode(f,!1))&&(b=P),b>=0){var z=b===0?i.tabbableGroups.length-1:b-1,G=i.tabbableGroups[z];O=ie(f)>=0?G.lastTabbableNode:G.lastDomTabbableNode}else ye(p)||(O=m.nextTabbableNode(f,!1))}else{var j=lt(i.tabbableGroups,function(U){var H=U.lastTabbableNode;return f===H});if(j<0&&(m.container===f||De(f,a.tabbableOptions)&&!se(f,a.tabbableOptions)&&!m.nextTabbableNode(f))&&(j=P),j>=0){var J=j===i.tabbableGroups.length-1?0:j+1,B=i.tabbableGroups[J];O=ie(f)>=0?B.firstTabbableNode:B.firstDomTabbableNode}else ye(p)||(O=m.nextTabbableNode(f))}}else O=h("fallbackFocus");return O},x=function(c){var f=xe(c);if(!(l(f,c)>=0)){if(ve(a.clickOutsideDeactivates,c)){s.deactivate({returnFocus:a.returnFocusOnDeactivate});return}ve(a.allowOutsideClick,c)||c.preventDefault()}},C=function(c){var f=xe(c),p=l(f,c)>=0;if(p||f instanceof Document)p&&(i.mostRecentlyFocusedNode=f);else{c.stopImmediatePropagation();var w,k=!0;if(i.mostRecentlyFocusedNode)if(ie(i.mostRecentlyFocusedNode)>0){var O=l(i.mostRecentlyFocusedNode),P=i.containerGroups[O].tabbableNodes;if(P.length>0){var m=P.findIndex(function(b){return b===i.mostRecentlyFocusedNode});m>=0&&(a.isKeyForward(i.recentNavEvent)?m+1=0&&(w=P[m-1],k=!1))}}else i.containerGroups.some(function(b){return b.tabbableNodes.some(function(z){return ie(z)>0})})||(k=!1);else k=!1;k&&(w=E({target:i.mostRecentlyFocusedNode,isBackward:a.isKeyBackward(i.recentNavEvent)})),y(w||i.mostRecentlyFocusedNode||d())}i.recentNavEvent=void 0},N=function(c){var f=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1;i.recentNavEvent=c;var p=E({event:c,isBackward:f});p&&(ye(c)&&c.preventDefault(),y(p))},A=function(c){if(xr(c)&&ve(a.escapeDeactivates,c)!==!1){c.preventDefault(),s.deactivate();return}(a.isKeyForward(c)||a.isKeyBackward(c))&&N(c,a.isKeyBackward(c))},M=function(c){var f=xe(c);l(f,c)>=0||ve(a.clickOutsideDeactivates,c)||ve(a.allowOutsideClick,c)||(c.preventDefault(),c.stopImmediatePropagation())},L=function(){if(i.active)return st.activateTrap(n,s),i.delayInitialFocusTimer=a.delayInitialFocus?ut(function(){y(d())}):y(d()),r.addEventListener("focusin",C,!0),r.addEventListener("mousedown",x,{capture:!0,passive:!1}),r.addEventListener("touchstart",x,{capture:!0,passive:!1}),r.addEventListener("click",M,{capture:!0,passive:!1}),r.addEventListener("keydown",A,{capture:!0,passive:!1}),s},D=function(){if(i.active)return r.removeEventListener("focusin",C,!0),r.removeEventListener("mousedown",x,!0),r.removeEventListener("touchstart",x,!0),r.removeEventListener("click",M,!0),r.removeEventListener("keydown",A,!0),s},T=function(c){var f=c.some(function(p){var w=Array.from(p.removedNodes);return w.some(function(k){return k===i.mostRecentlyFocusedNode})});f&&y(d())},I=typeof window<"u"&&"MutationObserver"in window?new MutationObserver(T):void 0,R=function(){I&&(I.disconnect(),i.active&&!i.paused&&i.containers.map(function(c){I.observe(c,{subtree:!0,childList:!0})}))};return s={get active(){return i.active},get paused(){return i.paused},activate:function(c){if(i.active)return this;var f=u(c,"onActivate"),p=u(c,"onPostActivate"),w=u(c,"checkCanFocusTrap");w||v(),i.active=!0,i.paused=!1,i.nodeFocusedBeforeActivation=r.activeElement,f==null||f();var k=function(){w&&v(),L(),R(),p==null||p()};return w?(w(i.containers.concat()).then(k,k),this):(k(),this)},deactivate:function(c){if(!i.active)return this;var f=ot({onDeactivate:a.onDeactivate,onPostDeactivate:a.onPostDeactivate,checkCanReturnFocus:a.checkCanReturnFocus},c);clearTimeout(i.delayInitialFocusTimer),i.delayInitialFocusTimer=void 0,D(),i.active=!1,i.paused=!1,R(),st.deactivateTrap(n,s);var p=u(f,"onDeactivate"),w=u(f,"onPostDeactivate"),k=u(f,"checkCanReturnFocus"),O=u(f,"returnFocus","returnFocusOnDeactivate");p==null||p();var P=function(){ut(function(){O&&y(g(i.nodeFocusedBeforeActivation)),w==null||w()})};return O&&k?(k(g(i.nodeFocusedBeforeActivation)).then(P,P),this):(P(),this)},pause:function(c){if(i.paused||!i.active)return this;var f=u(c,"onPause"),p=u(c,"onPostPause");return i.paused=!0,f==null||f(),D(),R(),p==null||p(),this},unpause:function(c){if(!i.paused||!i.active)return this;var f=u(c,"onUnpause"),p=u(c,"onPostUnpause");return i.paused=!1,f==null||f(),v(),L(),R(),p==null||p(),this},updateContainerElements:function(c){var f=[].concat(c).filter(Boolean);return i.containers=f.map(function(p){return typeof p=="string"?r.querySelector(p):p}),i.active&&v(),R(),this}},s.updateContainerElements(e),s};function Cr(o,e={}){let t;const{immediate:r,...n}=e,a=ne(!1),i=ne(!1),s=d=>t&&t.activate(d),u=d=>t&&t.deactivate(d),l=()=>{t&&(t.pause(),i.value=!0)},h=()=>{t&&(t.unpause(),i.value=!1)};return ze(()=>Nt(o),d=>{d&&(t=Ar(d,{...n,onActivate(){a.value=!0,e.onActivate&&e.onActivate()},onDeactivate(){a.value=!1,e.onDeactivate&&e.onDeactivate()}}),r&&s())},{flush:"post"}),Tt(()=>u()),{hasFocus:a,isPaused:i,activate:s,deactivate:u,pause:l,unpause:h}}class le{constructor(e,t=!0,r=[],n=5e3){this.ctx=e,this.iframes=t,this.exclude=r,this.iframesTimeout=n}static matches(e,t){const r=typeof t=="string"?[t]:t,n=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(n){let a=!1;return r.every(i=>n.call(e,i)?(a=!0,!1):!0),a}else return!1}getContexts(){let e,t=[];return typeof this.ctx>"u"||!this.ctx?e=[]:NodeList.prototype.isPrototypeOf(this.ctx)?e=Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?e=this.ctx:typeof this.ctx=="string"?e=Array.prototype.slice.call(document.querySelectorAll(this.ctx)):e=[this.ctx],e.forEach(r=>{const n=t.filter(a=>a.contains(r)).length>0;t.indexOf(r)===-1&&!n&&t.push(r)}),t}getIframeContents(e,t,r=()=>{}){let n;try{const a=e.contentWindow;if(n=a.document,!a||!n)throw new Error("iframe inaccessible")}catch{r()}n&&t(n)}isIframeBlank(e){const t="about:blank",r=e.getAttribute("src").trim();return e.contentWindow.location.href===t&&r!==t&&r}observeIframeLoad(e,t,r){let n=!1,a=null;const i=()=>{if(!n){n=!0,clearTimeout(a);try{this.isIframeBlank(e)||(e.removeEventListener("load",i),this.getIframeContents(e,t,r))}catch{r()}}};e.addEventListener("load",i),a=setTimeout(i,this.iframesTimeout)}onIframeReady(e,t,r){try{e.contentWindow.document.readyState==="complete"?this.isIframeBlank(e)?this.observeIframeLoad(e,t,r):this.getIframeContents(e,t,r):this.observeIframeLoad(e,t,r)}catch{r()}}waitForIframes(e,t){let r=0;this.forEachIframe(e,()=>!0,n=>{r++,this.waitForIframes(n.querySelector("html"),()=>{--r||t()})},n=>{n||t()})}forEachIframe(e,t,r,n=()=>{}){let a=e.querySelectorAll("iframe"),i=a.length,s=0;a=Array.prototype.slice.call(a);const u=()=>{--i<=0&&n(s)};i||u(),a.forEach(l=>{le.matches(l,this.exclude)?u():this.onIframeReady(l,h=>{t(l)&&(s++,r(h)),u()},u)})}createIterator(e,t,r){return document.createNodeIterator(e,t,r,!1)}createInstanceOnIframe(e){return new le(e.querySelector("html"),this.iframes)}compareNodeIframe(e,t,r){const n=e.compareDocumentPosition(r),a=Node.DOCUMENT_POSITION_PRECEDING;if(n&a)if(t!==null){const i=t.compareDocumentPosition(r),s=Node.DOCUMENT_POSITION_FOLLOWING;if(i&s)return!0}else return!0;return!1}getIteratorNode(e){const t=e.previousNode();let r;return t===null?r=e.nextNode():r=e.nextNode()&&e.nextNode(),{prevNode:t,node:r}}checkIframeFilter(e,t,r,n){let a=!1,i=!1;return n.forEach((s,u)=>{s.val===r&&(a=u,i=s.handled)}),this.compareNodeIframe(e,t,r)?(a===!1&&!i?n.push({val:r,handled:!0}):a!==!1&&!i&&(n[a].handled=!0),!0):(a===!1&&n.push({val:r,handled:!1}),!1)}handleOpenIframes(e,t,r,n){e.forEach(a=>{a.handled||this.getIframeContents(a.val,i=>{this.createInstanceOnIframe(i).forEachNode(t,r,n)})})}iterateThroughNodes(e,t,r,n,a){const i=this.createIterator(t,e,n);let s=[],u=[],l,h,d=()=>({prevNode:h,node:l}=this.getIteratorNode(i),l);for(;d();)this.iframes&&this.forEachIframe(t,v=>this.checkIframeFilter(l,h,v,s),v=>{this.createInstanceOnIframe(v).forEachNode(e,y=>u.push(y),n)}),u.push(l);u.forEach(v=>{r(v)}),this.iframes&&this.handleOpenIframes(s,e,r,n),a()}forEachNode(e,t,r,n=()=>{}){const a=this.getContexts();let i=a.length;i||n(),a.forEach(s=>{const u=()=>{this.iterateThroughNodes(e,s,t,r,()=>{--i<=0&&n()})};this.iframes?this.waitForIframes(s,u):u()})}}let Nr=class{constructor(e){this.ctx=e,this.ie=!1;const t=window.navigator.userAgent;(t.indexOf("MSIE")>-1||t.indexOf("Trident")>-1)&&(this.ie=!0)}set opt(e){this._opt=Object.assign({},{element:"",className:"",exclude:[],iframes:!1,iframesTimeout:5e3,separateWordSearch:!0,diacritics:!0,synonyms:{},accuracy:"partially",acrossElements:!1,caseSensitive:!1,ignoreJoiners:!1,ignoreGroups:0,ignorePunctuation:[],wildcards:"disabled",each:()=>{},noMatch:()=>{},filter:()=>!0,done:()=>{},debug:!1,log:window.console},e)}get opt(){return this._opt}get iterator(){return new le(this.ctx,this.opt.iframes,this.opt.exclude,this.opt.iframesTimeout)}log(e,t="debug"){const r=this.opt.log;this.opt.debug&&typeof r=="object"&&typeof r[t]=="function"&&r[t](`mark.js: ${e}`)}escapeStr(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}createRegExp(e){return this.opt.wildcards!=="disabled"&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),this.opt.wildcards!=="disabled"&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e),e}createSynonymsRegExp(e){const t=this.opt.synonyms,r=this.opt.caseSensitive?"":"i",n=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(let a in t)if(t.hasOwnProperty(a)){const i=t[a],s=this.opt.wildcards!=="disabled"?this.setupWildcardsRegExp(a):this.escapeStr(a),u=this.opt.wildcards!=="disabled"?this.setupWildcardsRegExp(i):this.escapeStr(i);s!==""&&u!==""&&(e=e.replace(new RegExp(`(${this.escapeStr(s)}|${this.escapeStr(u)})`,`gm${r}`),n+`(${this.processSynomyms(s)}|${this.processSynomyms(u)})`+n))}return e}processSynomyms(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}setupWildcardsRegExp(e){return e=e.replace(/(?:\\)*\?/g,t=>t.charAt(0)==="\\"?"?":""),e.replace(/(?:\\)*\*/g,t=>t.charAt(0)==="\\"?"*":"")}createWildcardsRegExp(e){let t=this.opt.wildcards==="withSpaces";return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}setupIgnoreJoinersRegExp(e){return e.replace(/[^(|)\\]/g,(t,r,n)=>{let a=n.charAt(r+1);return/[(|)\\]/.test(a)||a===""?t:t+"\0"})}createJoinersRegExp(e){let t=[];const r=this.opt.ignorePunctuation;return Array.isArray(r)&&r.length&&t.push(this.escapeStr(r.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join(`[${t.join("")}]*`):e}createDiacriticsRegExp(e){const t=this.opt.caseSensitive?"":"i",r=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"];let n=[];return e.split("").forEach(a=>{r.every(i=>{if(i.indexOf(a)!==-1){if(n.indexOf(i)>-1)return!1;e=e.replace(new RegExp(`[${i}]`,`gm${t}`),`[${i}]`),n.push(i)}return!0})}),e}createMergedBlanksRegExp(e){return e.replace(/[\s]+/gmi,"[\\s]+")}createAccuracyRegExp(e){const t="!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿";let r=this.opt.accuracy,n=typeof r=="string"?r:r.value,a=typeof r=="string"?[]:r.limiters,i="";switch(a.forEach(s=>{i+=`|${this.escapeStr(s)}`}),n){case"partially":default:return`()(${e})`;case"complementary":return i="\\s"+(i||this.escapeStr(t)),`()([^${i}]*${e}[^${i}]*)`;case"exactly":return`(^|\\s${i})(${e})(?=$|\\s${i})`}}getSeparatedKeywords(e){let t=[];return e.forEach(r=>{this.opt.separateWordSearch?r.split(" ").forEach(n=>{n.trim()&&t.indexOf(n)===-1&&t.push(n)}):r.trim()&&t.indexOf(r)===-1&&t.push(r)}),{keywords:t.sort((r,n)=>n.length-r.length),length:t.length}}isNumeric(e){return Number(parseFloat(e))==e}checkRanges(e){if(!Array.isArray(e)||Object.prototype.toString.call(e[0])!=="[object Object]")return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];const t=[];let r=0;return e.sort((n,a)=>n.start-a.start).forEach(n=>{let{start:a,end:i,valid:s}=this.callNoMatchOnInvalidRanges(n,r);s&&(n.start=a,n.length=i-a,t.push(n),r=i)}),t}callNoMatchOnInvalidRanges(e,t){let r,n,a=!1;return e&&typeof e.start<"u"?(r=parseInt(e.start,10),n=r+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&n-t>0&&n-r>0?a=!0:(this.log(`Ignoring invalid or overlapping range: ${JSON.stringify(e)}`),this.opt.noMatch(e))):(this.log(`Ignoring invalid range: ${JSON.stringify(e)}`),this.opt.noMatch(e)),{start:r,end:n,valid:a}}checkWhitespaceRanges(e,t,r){let n,a=!0,i=r.length,s=t-i,u=parseInt(e.start,10)-s;return u=u>i?i:u,n=u+parseInt(e.length,10),n>i&&(n=i,this.log(`End range automatically set to the max value of ${i}`)),u<0||n-u<0||u>i||n>i?(a=!1,this.log(`Invalid range: ${JSON.stringify(e)}`),this.opt.noMatch(e)):r.substring(u,n).replace(/\s+/g,"")===""&&(a=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:u,end:n,valid:a}}getTextNodes(e){let t="",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,n=>{r.push({start:t.length,end:(t+=n.textContent).length,node:n})},n=>this.matchesExclude(n.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT,()=>{e({value:t,nodes:r})})}matchesExclude(e){return le.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}wrapRangeInTextNode(e,t,r){const n=this.opt.element?this.opt.element:"mark",a=e.splitText(t),i=a.splitText(r-t);let s=document.createElement(n);return s.setAttribute("data-markjs","true"),this.opt.className&&s.setAttribute("class",this.opt.className),s.textContent=a.textContent,a.parentNode.replaceChild(s,a),i}wrapRangeInMappedTextNode(e,t,r,n,a){e.nodes.every((i,s)=>{const u=e.nodes[s+1];if(typeof u>"u"||u.start>t){if(!n(i.node))return!1;const l=t-i.start,h=(r>i.end?i.end:r)-i.start,d=e.value.substr(0,i.start),v=e.value.substr(h+i.start);if(i.node=this.wrapRangeInTextNode(i.node,l,h),e.value=d+v,e.nodes.forEach((y,g)=>{g>=s&&(e.nodes[g].start>0&&g!==s&&(e.nodes[g].start-=h),e.nodes[g].end-=h)}),r-=h,a(i.node.previousSibling,i.start),r>i.end)t=i.end;else return!1}return!0})}wrapMatches(e,t,r,n,a){const i=t===0?0:t+1;this.getTextNodes(s=>{s.nodes.forEach(u=>{u=u.node;let l;for(;(l=e.exec(u.textContent))!==null&&l[i]!=="";){if(!r(l[i],u))continue;let h=l.index;if(i!==0)for(let d=1;d{let u;for(;(u=e.exec(s.value))!==null&&u[i]!=="";){let l=u.index;if(i!==0)for(let d=1;dr(u[i],d),(d,v)=>{e.lastIndex=v,n(d)})}a()})}wrapRangeFromIndex(e,t,r,n){this.getTextNodes(a=>{const i=a.value.length;e.forEach((s,u)=>{let{start:l,end:h,valid:d}=this.checkWhitespaceRanges(s,i,a.value);d&&this.wrapRangeInMappedTextNode(a,l,h,v=>t(v,s,a.value.substring(l,h),u),v=>{r(v,s)})}),n()})}unwrapMatches(e){const t=e.parentNode;let r=document.createDocumentFragment();for(;e.firstChild;)r.appendChild(e.removeChild(e.firstChild));t.replaceChild(r,e),this.ie?this.normalizeTextNode(t):t.normalize()}normalizeTextNode(e){if(e){if(e.nodeType===3)for(;e.nextSibling&&e.nextSibling.nodeType===3;)e.nodeValue+=e.nextSibling.nodeValue,e.parentNode.removeChild(e.nextSibling);else this.normalizeTextNode(e.firstChild);this.normalizeTextNode(e.nextSibling)}}markRegExp(e,t){this.opt=t,this.log(`Searching with expression "${e}"`);let r=0,n="wrapMatches";const a=i=>{r++,this.opt.each(i)};this.opt.acrossElements&&(n="wrapMatchesAcrossElements"),this[n](e,this.opt.ignoreGroups,(i,s)=>this.opt.filter(s,i,r),a,()=>{r===0&&this.opt.noMatch(e),this.opt.done(r)})}mark(e,t){this.opt=t;let r=0,n="wrapMatches";const{keywords:a,length:i}=this.getSeparatedKeywords(typeof e=="string"?[e]:e),s=this.opt.caseSensitive?"":"i",u=l=>{let h=new RegExp(this.createRegExp(l),`gm${s}`),d=0;this.log(`Searching with expression "${h}"`),this[n](h,1,(v,y)=>this.opt.filter(y,l,r,d),v=>{d++,r++,this.opt.each(v)},()=>{d===0&&this.opt.noMatch(l),a[i-1]===l?this.opt.done(r):u(a[a.indexOf(l)+1])})};this.opt.acrossElements&&(n="wrapMatchesAcrossElements"),i===0?this.opt.done(r):u(a[0])}markRanges(e,t){this.opt=t;let r=0,n=this.checkRanges(e);n&&n.length?(this.log("Starting to mark with the following ranges: "+JSON.stringify(n)),this.wrapRangeFromIndex(n,(a,i,s,u)=>this.opt.filter(a,i,s,u),(a,i)=>{r++,this.opt.each(a,i)},()=>{this.opt.done(r)})):this.opt.done(r)}unmark(e){this.opt=e;let t=this.opt.element?this.opt.element:"*";t+="[data-markjs]",this.opt.className&&(t+=`.${this.opt.className}`),this.log(`Removal selector "${t}"`),this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT,r=>{this.unwrapMatches(r)},r=>{const n=le.matches(r,t),a=this.matchesExclude(r);return!n||a?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},this.opt.done)}};function Tr(o){const e=new Nr(o);return this.mark=(t,r)=>(e.mark(t,r),this),this.markRegExp=(t,r)=>(e.markRegExp(t,r),this),this.markRanges=(t,r)=>(e.markRanges(t,r),this),this.unmark=t=>(e.unmark(t),this),this}var V=function(){return V=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0&&a[a.length-1])&&(l[0]===6||l[0]===2)){t=0;continue}if(l[0]===3&&(!a||l[1]>a[0]&&l[1]=o.length&&(o=void 0),{value:o&&o[r++],done:!o}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function $(o,e){var t=typeof Symbol=="function"&&o[Symbol.iterator];if(!t)return o;var r=t.call(o),n,a=[],i;try{for(;(e===void 0||e-- >0)&&!(n=r.next()).done;)a.push(n.value)}catch(s){i={error:s}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(i)throw i.error}}return a}var Dr="ENTRIES",xt="KEYS",Ft="VALUES",K="",_e=function(){function o(e,t){var r=e._tree,n=Array.from(r.keys());this.set=e,this._type=t,this._path=n.length>0?[{node:r,keys:n}]:[]}return o.prototype.next=function(){var e=this.dive();return this.backtrack(),e},o.prototype.dive=function(){if(this._path.length===0)return{done:!0,value:void 0};var e=ue(this._path),t=e.node,r=e.keys;if(ue(r)===K)return{done:!1,value:this.result()};var n=t.get(ue(r));return this._path.push({node:n,keys:Array.from(n.keys())}),this.dive()},o.prototype.backtrack=function(){if(this._path.length!==0){var e=ue(this._path).keys;e.pop(),!(e.length>0)&&(this._path.pop(),this.backtrack())}},o.prototype.key=function(){return this.set._prefix+this._path.map(function(e){var t=e.keys;return ue(t)}).filter(function(e){return e!==K}).join("")},o.prototype.value=function(){return ue(this._path).node.get(K)},o.prototype.result=function(){switch(this._type){case Ft:return this.value();case xt:return this.key();default:return[this.key(),this.value()]}},o.prototype[Symbol.iterator]=function(){return this},o}(),ue=function(o){return o[o.length-1]},_r=function(o,e,t){var r=new Map;if(e===void 0)return r;for(var n=e.length+1,a=n+t,i=new Uint8Array(a*n).fill(t+1),s=0;st)continue e}Et(o.get(y),e,t,r,n,E,i,s+y)}}}catch(p){u={error:p}}finally{try{v&&!v.done&&(l=d.return)&&l.call(d)}finally{if(u)throw u.error}}},Oe=function(){function o(e,t){e===void 0&&(e=new Map),t===void 0&&(t=""),this._size=void 0,this._tree=e,this._prefix=t}return o.prototype.atPrefix=function(e){var t,r;if(!e.startsWith(this._prefix))throw new Error("Mismatched prefix");var n=$(Ne(this._tree,e.slice(this._prefix.length)),2),a=n[0],i=n[1];if(a===void 0){var s=$(Ke(i),2),u=s[0],l=s[1];try{for(var h=_(u.keys()),d=h.next();!d.done;d=h.next()){var v=d.value;if(v!==K&&v.startsWith(l)){var y=new Map;return y.set(v.slice(l.length),u.get(v)),new o(y,e)}}}catch(g){t={error:g}}finally{try{d&&!d.done&&(r=h.return)&&r.call(h)}finally{if(t)throw t.error}}}return new o(a,e)},o.prototype.clear=function(){this._size=void 0,this._tree.clear()},o.prototype.delete=function(e){return this._size=void 0,Or(this._tree,e)},o.prototype.entries=function(){return new _e(this,Dr)},o.prototype.forEach=function(e){var t,r;try{for(var n=_(this),a=n.next();!a.done;a=n.next()){var i=$(a.value,2),s=i[0],u=i[1];e(s,u,this)}}catch(l){t={error:l}}finally{try{a&&!a.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}},o.prototype.fuzzyGet=function(e,t){return _r(this._tree,e,t)},o.prototype.get=function(e){var t=Ve(this._tree,e);return t!==void 0?t.get(K):void 0},o.prototype.has=function(e){var t=Ve(this._tree,e);return t!==void 0&&t.has(K)},o.prototype.keys=function(){return new _e(this,xt)},o.prototype.set=function(e,t){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;var r=Re(this._tree,e);return r.set(K,t),this},Object.defineProperty(o.prototype,"size",{get:function(){if(this._size)return this._size;this._size=0;for(var e=this.entries();!e.next().done;)this._size+=1;return this._size},enumerable:!1,configurable:!0}),o.prototype.update=function(e,t){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;var r=Re(this._tree,e);return r.set(K,t(r.get(K))),this},o.prototype.fetch=function(e,t){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;var r=Re(this._tree,e),n=r.get(K);return n===void 0&&r.set(K,n=t()),n},o.prototype.values=function(){return new _e(this,Ft)},o.prototype[Symbol.iterator]=function(){return this.entries()},o.from=function(e){var t,r,n=new o;try{for(var a=_(e),i=a.next();!i.done;i=a.next()){var s=$(i.value,2),u=s[0],l=s[1];n.set(u,l)}}catch(h){t={error:h}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}return n},o.fromObject=function(e){return o.from(Object.entries(e))},o}(),Ne=function(o,e,t){var r,n;if(t===void 0&&(t=[]),e.length===0||o==null)return[o,t];try{for(var a=_(o.keys()),i=a.next();!i.done;i=a.next()){var s=i.value;if(s!==K&&e.startsWith(s))return t.push([o,s]),Ne(o.get(s),e.slice(s.length),t)}}catch(u){r={error:u}}finally{try{i&&!i.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}return t.push([o,e]),Ne(void 0,"",t)},Ve=function(o,e){var t,r;if(e.length===0||o==null)return o;try{for(var n=_(o.keys()),a=n.next();!a.done;a=n.next()){var i=a.value;if(i!==K&&e.startsWith(i))return Ve(o.get(i),e.slice(i.length))}}catch(s){t={error:s}}finally{try{a&&!a.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}},Re=function(o,e){var t,r,n=e.length;e:for(var a=0;o&&a0)throw new Error("Expected documents to be present. Omit the argument to remove all documents.");this._index=new Oe,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldLength=new Map,this._avgFieldLength=[],this._storedFields=new Map,this._nextId=0}},o.prototype.discard=function(e){var t=this,r=this._idToShortId.get(e);if(r==null)throw new Error("MiniSearch: cannot discard document with ID ".concat(e,": it is not in the index"));this._idToShortId.delete(e),this._documentIds.delete(r),this._storedFields.delete(r),(this._fieldLength.get(r)||[]).forEach(function(n,a){t.removeFieldLength(r,a,t._documentCount,n)}),this._fieldLength.delete(r),this._documentCount-=1,this._dirtCount+=1,this.maybeAutoVacuum()},o.prototype.maybeAutoVacuum=function(){if(this._options.autoVacuum!==!1){var e=this._options.autoVacuum,t=e.minDirtFactor,r=e.minDirtCount,n=e.batchSize,a=e.batchWait;this.conditionalVacuum({batchSize:n,batchWait:a},{minDirtCount:r,minDirtFactor:t})}},o.prototype.discardAll=function(e){var t,r,n=this._options.autoVacuum;try{this._options.autoVacuum=!1;try{for(var a=_(e),i=a.next();!i.done;i=a.next()){var s=i.value;this.discard(s)}}catch(u){t={error:u}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}}finally{this._options.autoVacuum=n}this.maybeAutoVacuum()},o.prototype.replace=function(e){var t=this._options,r=t.idField,n=t.extractField,a=n(e,r);this.discard(a),this.add(e)},o.prototype.vacuum=function(e){return e===void 0&&(e={}),this.conditionalVacuum(e)},o.prototype.conditionalVacuum=function(e,t){var r=this;return this._currentVacuum?(this._enqueuedVacuumConditions=this._enqueuedVacuumConditions&&t,this._enqueuedVacuum!=null?this._enqueuedVacuum:(this._enqueuedVacuum=this._currentVacuum.then(function(){var n=r._enqueuedVacuumConditions;return r._enqueuedVacuumConditions=We,r.performVacuuming(e,n)}),this._enqueuedVacuum)):this.vacuumConditionsMet(t)===!1?Promise.resolve():(this._currentVacuum=this.performVacuuming(e),this._currentVacuum)},o.prototype.performVacuuming=function(e,t){return kr(this,void 0,void 0,function(){var r,n,a,i,s,u,l,h,d,v,y,g,E,x,C,N,A,M,L,D,T,I,R,F,c;return Ir(this,function(f){switch(f.label){case 0:if(r=this._dirtCount,!this.vacuumConditionsMet(t))return[3,10];n=e.batchSize||$e.batchSize,a=e.batchWait||$e.batchWait,i=1,f.label=1;case 1:f.trys.push([1,7,8,9]),s=_(this._index),u=s.next(),f.label=2;case 2:if(u.done)return[3,6];l=$(u.value,2),h=l[0],d=l[1];try{for(v=(I=void 0,_(d)),y=v.next();!y.done;y=v.next()){g=$(y.value,2),E=g[0],x=g[1];try{for(C=(F=void 0,_(x)),N=C.next();!N.done;N=C.next())A=$(N.value,1),M=A[0],!this._documentIds.has(M)&&(x.size<=1?d.delete(E):x.delete(M))}catch(p){F={error:p}}finally{try{N&&!N.done&&(c=C.return)&&c.call(C)}finally{if(F)throw F.error}}}}catch(p){I={error:p}}finally{try{y&&!y.done&&(R=v.return)&&R.call(v)}finally{if(I)throw I.error}}return this._index.get(h).size===0&&this._index.delete(h),i%n!==0?[3,4]:[4,new Promise(function(p){return setTimeout(p,a)})];case 3:f.sent(),f.label=4;case 4:i+=1,f.label=5;case 5:return u=s.next(),[3,2];case 6:return[3,9];case 7:return L=f.sent(),D={error:L},[3,9];case 8:try{u&&!u.done&&(T=s.return)&&T.call(s)}finally{if(D)throw D.error}return[7];case 9:this._dirtCount-=r,f.label=10;case 10:return[4,null];case 11:return f.sent(),this._currentVacuum=this._enqueuedVacuum,this._enqueuedVacuum=null,[2]}})})},o.prototype.vacuumConditionsMet=function(e){if(e==null)return!0;var t=e.minDirtCount,r=e.minDirtFactor;return t=t||Pe.minDirtCount,r=r||Pe.minDirtFactor,this.dirtCount>=t&&this.dirtFactor>=r},Object.defineProperty(o.prototype,"isVacuuming",{get:function(){return this._currentVacuum!=null},enumerable:!1,configurable:!0}),Object.defineProperty(o.prototype,"dirtCount",{get:function(){return this._dirtCount},enumerable:!1,configurable:!0}),Object.defineProperty(o.prototype,"dirtFactor",{get:function(){return this._dirtCount/(1+this._documentCount+this._dirtCount)},enumerable:!1,configurable:!0}),o.prototype.has=function(e){return this._idToShortId.has(e)},o.prototype.getStoredFields=function(e){var t=this._idToShortId.get(e);if(t!=null)return this._storedFields.get(t)},o.prototype.search=function(e,t){var r,n;t===void 0&&(t={});var a=this.executeQuery(e,t),i=[];try{for(var s=_(a),u=s.next();!u.done;u=s.next()){var l=$(u.value,2),h=l[0],d=l[1],v=d.score,y=d.terms,g=d.match,E=y.length,x={id:this._documentIds.get(h),score:v*E,terms:Object.keys(g),match:g};Object.assign(x,this._storedFields.get(h)),(t.filter==null||t.filter(x))&&i.push(x)}}catch(C){r={error:C}}finally{try{u&&!u.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}return i.sort(dt),i},o.prototype.autoSuggest=function(e,t){var r,n,a,i;t===void 0&&(t={}),t=V(V({},this._options.autoSuggestOptions),t);var s=new Map;try{for(var u=_(this.search(e,t)),l=u.next();!l.done;l=u.next()){var h=l.value,d=h.score,v=h.terms,y=v.join(" "),g=s.get(y);g!=null?(g.score+=d,g.count+=1):s.set(y,{score:d,terms:v,count:1})}}catch(L){r={error:L}}finally{try{l&&!l.done&&(n=u.return)&&n.call(u)}finally{if(r)throw r.error}}var E=[];try{for(var x=_(s),C=x.next();!C.done;C=x.next()){var N=$(C.value,2),g=N[0],A=N[1],d=A.score,v=A.terms,M=A.count;E.push({suggestion:g,terms:v,score:d/M})}}catch(L){a={error:L}}finally{try{C&&!C.done&&(i=x.return)&&i.call(x)}finally{if(a)throw a.error}}return E.sort(dt),E},Object.defineProperty(o.prototype,"documentCount",{get:function(){return this._documentCount},enumerable:!1,configurable:!0}),Object.defineProperty(o.prototype,"termCount",{get:function(){return this._index.size},enumerable:!1,configurable:!0}),o.loadJSON=function(e,t){if(t==null)throw new Error("MiniSearch: loadJSON should be given the same options used when serializing the index");return this.loadJS(JSON.parse(e),t)},o.getDefault=function(e){if(Le.hasOwnProperty(e))return Me(Le,e);throw new Error('MiniSearch: unknown option "'.concat(e,'"'))},o.loadJS=function(e,t){var r,n,a,i,s,u,l=e.index,h=e.documentCount,d=e.nextId,v=e.documentIds,y=e.fieldIds,g=e.fieldLength,E=e.averageFieldLength,x=e.storedFields,C=e.dirtCount,N=e.serializationVersion;if(N!==1&&N!==2)throw new Error("MiniSearch: cannot deserialize an index created with an incompatible version");var A=new o(t);A._documentCount=h,A._nextId=d,A._documentIds=Fe(v),A._idToShortId=new Map,A._fieldIds=y,A._fieldLength=Fe(g),A._avgFieldLength=E,A._storedFields=Fe(x),A._dirtCount=C||0,A._index=new Oe;try{for(var M=_(A._documentIds),L=M.next();!L.done;L=M.next()){var D=$(L.value,2),T=D[0],I=D[1];A._idToShortId.set(I,T)}}catch(b){r={error:b}}finally{try{L&&!L.done&&(n=M.return)&&n.call(M)}finally{if(r)throw r.error}}try{for(var R=_(l),F=R.next();!F.done;F=R.next()){var c=$(F.value,2),f=c[0],p=c[1],w=new Map;try{for(var k=(s=void 0,_(Object.keys(p))),O=k.next();!O.done;O=k.next()){var P=O.value,m=p[P];N===1&&(m=m.ds),w.set(parseInt(P,10),Fe(m))}}catch(b){s={error:b}}finally{try{O&&!O.done&&(u=k.return)&&u.call(k)}finally{if(s)throw s.error}}A._index.set(f,w)}}catch(b){a={error:b}}finally{try{F&&!F.done&&(i=R.return)&&i.call(R)}finally{if(a)throw a.error}}return A},o.prototype.executeQuery=function(e,t){var r=this;if(t===void 0&&(t={}),typeof e!="string"){var n=V(V(V({},t),e),{queries:void 0}),a=e.queries.map(function(x){return r.executeQuery(x,n)});return this.combineResults(a,n.combineWith)}var i=this._options,s=i.tokenize,u=i.processTerm,l=i.searchOptions,h=V(V({tokenize:s,processTerm:u},l),t),d=h.tokenize,v=h.processTerm,y=d(e).flatMap(function(x){return v(x)}).filter(function(x){return!!x}),g=y.map(Br(h)),E=g.map(function(x){return r.executeQuerySpec(x,h)});return this.combineResults(E,h.combineWith)},o.prototype.executeQuerySpec=function(e,t){var r,n,a,i,s=V(V({},this._options.searchOptions),t),u=(s.fields||this._options.fields).reduce(function(P,m){var b;return V(V({},P),(b={},b[m]=Me(s.boost,m)||1,b))},{}),l=s.boostDocument,h=s.weights,d=s.maxFuzzy,v=s.bm25,y=V(V({},ct.weights),h),g=y.fuzzy,E=y.prefix,x=this._index.get(e.term),C=this.termResults(e.term,e.term,1,x,u,l,v),N,A;if(e.prefix&&(N=this._index.atPrefix(e.term)),e.fuzzy){var M=e.fuzzy===!0?.2:e.fuzzy,L=M<1?Math.min(d,Math.round(e.term.length*M)):M;L&&(A=this._index.fuzzyGet(e.term,L))}if(N)try{for(var D=_(N),T=D.next();!T.done;T=D.next()){var I=$(T.value,2),R=I[0],F=I[1],c=R.length-e.term.length;if(c){A==null||A.delete(R);var f=E*R.length/(R.length+.3*c);this.termResults(e.term,R,f,F,u,l,v,C)}}}catch(P){r={error:P}}finally{try{T&&!T.done&&(n=D.return)&&n.call(D)}finally{if(r)throw r.error}}if(A)try{for(var p=_(A.keys()),w=p.next();!w.done;w=p.next()){var R=w.value,k=$(A.get(R),2),O=k[0],c=k[1];if(c){var f=g*R.length/(R.length+c);this.termResults(e.term,R,f,O,u,l,v,C)}}}catch(P){a={error:P}}finally{try{w&&!w.done&&(i=p.return)&&i.call(p)}finally{if(a)throw a.error}}return C},o.prototype.combineResults=function(e,t){if(t===void 0&&(t=je),e.length===0)return new Map;var r=t.toLowerCase();return e.reduce(Lr[r])||new Map},o.prototype.toJSON=function(){var e,t,r,n,a=[];try{for(var i=_(this._index),s=i.next();!s.done;s=i.next()){var u=$(s.value,2),l=u[0],h=u[1],d={};try{for(var v=(r=void 0,_(h)),y=v.next();!y.done;y=v.next()){var g=$(y.value,2),E=g[0],x=g[1];d[E]=Object.fromEntries(x)}}catch(C){r={error:C}}finally{try{y&&!y.done&&(n=v.return)&&n.call(v)}finally{if(r)throw r.error}}a.push([l,d])}}catch(C){e={error:C}}finally{try{s&&!s.done&&(t=i.return)&&t.call(i)}finally{if(e)throw e.error}}return{documentCount:this._documentCount,nextId:this._nextId,documentIds:Object.fromEntries(this._documentIds),fieldIds:this._fieldIds,fieldLength:Object.fromEntries(this._fieldLength),averageFieldLength:this._avgFieldLength,storedFields:Object.fromEntries(this._storedFields),dirtCount:this._dirtCount,index:a,serializationVersion:2}},o.prototype.termResults=function(e,t,r,n,a,i,s,u){var l,h,d,v,y;if(u===void 0&&(u=new Map),n==null)return u;try{for(var g=_(Object.keys(a)),E=g.next();!E.done;E=g.next()){var x=E.value,C=a[x],N=this._fieldIds[x],A=n.get(N);if(A!=null){var M=A.size,L=this._avgFieldLength[N];try{for(var D=(d=void 0,_(A.keys())),T=D.next();!T.done;T=D.next()){var I=T.value;if(!this._documentIds.has(I)){this.removeTerm(N,I,t),M-=1;continue}var R=i?i(this._documentIds.get(I),t,this._storedFields.get(I)):1;if(R){var F=A.get(I),c=this._fieldLength.get(I)[N],f=zr(F,M,this._documentCount,c,L,s),p=r*C*R*f,w=u.get(I);if(w){w.score+=p,$r(w.terms,e);var k=Me(w.match,t);k?k.push(x):w.match[t]=[x]}else u.set(I,{score:p,terms:[e],match:(y={},y[t]=[x],y)})}}}catch(O){d={error:O}}finally{try{T&&!T.done&&(v=D.return)&&v.call(D)}finally{if(d)throw d.error}}}}}catch(O){l={error:O}}finally{try{E&&!E.done&&(h=g.return)&&h.call(g)}finally{if(l)throw l.error}}return u},o.prototype.addTerm=function(e,t,r){var n=this._index.fetch(r,ht),a=n.get(e);if(a==null)a=new Map,a.set(t,1),n.set(e,a);else{var i=a.get(t);a.set(t,(i||0)+1)}},o.prototype.removeTerm=function(e,t,r){if(!this._index.has(r)){this.warnDocumentChanged(t,e,r);return}var n=this._index.fetch(r,ht),a=n.get(e);a==null||a.get(t)==null?this.warnDocumentChanged(t,e,r):a.get(t)<=1?a.size<=1?n.delete(e):a.delete(t):a.set(t,a.get(t)-1),this._index.get(r).size===0&&this._index.delete(r)},o.prototype.warnDocumentChanged=function(e,t,r){var n,a;try{for(var i=_(Object.keys(this._fieldIds)),s=i.next();!s.done;s=i.next()){var u=s.value;if(this._fieldIds[u]===t){this._options.logger("warn","MiniSearch: document with ID ".concat(this._documentIds.get(e),' has changed before removal: term "').concat(r,'" was not present in field "').concat(u,'". Removing a document after it has changed can corrupt the index!'),"version_conflict");return}}}catch(l){n={error:l}}finally{try{s&&!s.done&&(a=i.return)&&a.call(i)}finally{if(n)throw n.error}}},o.prototype.addDocumentId=function(e){var t=this._nextId;return this._idToShortId.set(e,t),this._documentIds.set(t,e),this._documentCount+=1,this._nextId+=1,t},o.prototype.addFields=function(e){for(var t=0;t(jt("data-v-58ff7037"),o=o(),Jt(),o),Kr=["aria-owns"],jr={class:"shell"},Jr=["title"],Ur=Q(()=>S("svg",{class:"search-icon",width:"18",height:"18",viewBox:"0 0 24 24","aria-hidden":"true"},[S("g",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2"},[S("circle",{cx:"11",cy:"11",r:"8"}),S("path",{d:"m21 21l-4.35-4.35"})])],-1)),Hr=[Ur],Gr={class:"search-actions before"},qr=["title"],Qr=Q(()=>S("svg",{width:"18",height:"18",viewBox:"0 0 24 24","aria-hidden":"true"},[S("path",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M19 12H5m7 7l-7-7l7-7"})],-1)),Yr=[Qr],Zr=["placeholder"],Xr={class:"search-actions"},ea=["title"],ta=Q(()=>S("svg",{width:"18",height:"18",viewBox:"0 0 24 24","aria-hidden":"true"},[S("path",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M3 14h7v7H3zM3 3h7v7H3zm11 1h7m-7 5h7m-7 6h7m-7 5h7"})],-1)),ra=[ta],aa=["disabled","title"],na=Q(()=>S("svg",{width:"18",height:"18",viewBox:"0 0 24 24","aria-hidden":"true"},[S("path",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M20 5H9l-7 7l7 7h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2Zm-2 4l-6 6m0-6l6 6"})],-1)),ia=[na],oa=["id","role","aria-labelledby"],sa=["aria-selected"],ua=["href","aria-label","onMouseenter","onFocusin"],la={class:"titles"},ca=Q(()=>S("span",{class:"title-icon"},"#",-1)),fa=["innerHTML"],da=Q(()=>S("svg",{width:"18",height:"18",viewBox:"0 0 24 24"},[S("path",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"m9 18l6-6l-6-6"})],-1)),ha={class:"title main"},va=["innerHTML"],pa={key:0,class:"excerpt-wrapper"},ya={key:0,class:"excerpt",inert:""},ma=["innerHTML"],ga=Q(()=>S("div",{class:"excerpt-gradient-bottom"},null,-1)),ba=Q(()=>S("div",{class:"excerpt-gradient-top"},null,-1)),wa={key:0,class:"no-results"},xa={class:"search-keyboard-shortcuts"},Fa=["aria-label"],Ea=Q(()=>S("svg",{width:"14",height:"14",viewBox:"0 0 24 24"},[S("path",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 19V5m-7 7l7-7l7 7"})],-1)),Sa=[Ea],Aa=["aria-label"],Ca=Q(()=>S("svg",{width:"14",height:"14",viewBox:"0 0 24 24"},[S("path",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 5v14m7-7l-7 7l-7-7"})],-1)),Na=[Ca],Ta=["aria-label"],ka=Q(()=>S("svg",{width:"14",height:"14",viewBox:"0 0 24 24"},[S("g",{fill:"none",stroke:"currentcolor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2"},[S("path",{d:"m9 10l-5 5l5 5"}),S("path",{d:"M20 4v7a4 4 0 0 1-4 4H4"})])],-1)),Ia=[ka],Da=["aria-label"],_a=kt({__name:"VPLocalSearchBox",props:{placeholder:{}},emits:["close"],setup(o,{emit:e}){var k,O,P;const t=ge(),r=ge(),n=ge(Xt),a=Yt(),{activate:i}=Cr(t,{immediate:!0,allowOutsideClick:!0,clickOutsideDeactivates:!0,escapeDeactivates:!0}),{localeIndex:s,theme:u}=a,l=Xe(async()=>{var m,b,z,G,j,J,B,U,H;return at(Mr.loadJSON((z=await((b=(m=n.value)[s.value])==null?void 0:b.call(m)))==null?void 0:z.default,{fields:["title","titles","text"],storeFields:["title","titles"],searchOptions:{fuzzy:.2,prefix:!0,boost:{title:4,text:2,titles:1},...((G=u.value.search)==null?void 0:G.provider)==="local"&&((J=(j=u.value.search.options)==null?void 0:j.miniSearch)==null?void 0:J.searchOptions)},...((B=u.value.search)==null?void 0:B.provider)==="local"&&((H=(U=u.value.search.options)==null?void 0:U.miniSearch)==null?void 0:H.options)}))}),d=ke(()=>{var m,b;return((m=u.value.search)==null?void 0:m.provider)==="local"&&((b=u.value.search.options)==null?void 0:b.disableQueryPersistence)===!0}).value?ne(""):It("vitepress:local-search-filter",""),v=Dt("vitepress:local-search-detailed-list",((k=u.value.search)==null?void 0:k.provider)==="local"&&((O=u.value.search.options)==null?void 0:O.detailedView)===!0),y=ke(()=>{var m,b,z;return((m=u.value.search)==null?void 0:m.provider)==="local"&&(((b=u.value.search.options)==null?void 0:b.disableDetailedView)===!0||((z=u.value.search.options)==null?void 0:z.detailedView)===!1)});_t(()=>{y.value&&(v.value=!1)});const g=ge([]),E=ne(!1);ze(d,()=>{E.value=!1});const x=Xe(async()=>{if(r.value)return at(new Tr(r.value))},null);Ot(()=>[l.value,d.value,v.value],async([m,b,z],G,j)=>{var Je,Ue,He,Ge;let J=!1;if(j(()=>{J=!0}),!m)return;g.value=m.search(b).slice(0,16),E.value=!0;const B=z?await Promise.all(g.value.map(q=>C(q.id))):[];if(J)return;const U=new Map;for(const{id:q,mod:te}of B){const re=q.slice(0,q.indexOf("#"));let X=U.get(re);if(X)continue;X=new Map,U.set(re,X);const Y=te.default??te;if(Y!=null&&Y.render||Y!=null&&Y.setup){const ae=Ut(Y);ae.config.warnHandler=()=>{},ae.provide(Ht,a),Object.defineProperties(ae.config.globalProperties,{$frontmatter:{get(){return a.frontmatter.value}},$params:{get(){return a.page.value.params}}});const qe=document.createElement("div");ae.mount(qe),qe.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach(ce=>{var Ze;const me=(Ze=ce.querySelector("a"))==null?void 0:Ze.getAttribute("href"),Qe=(me==null?void 0:me.startsWith("#"))&&me.slice(1);if(!Qe)return;let Ye="";for(;(ce=ce.nextElementSibling)&&!/^h[1-6]$/i.test(ce.tagName);)Ye+=ce.outerHTML;X.set(Qe,Ye)}),ae.unmount()}if(J)return}const H=new Set;if(g.value=g.value.map(q=>{const[te,re]=q.id.split("#"),X=U.get(te),Y=(X==null?void 0:X.get(re))??"";for(const ae in q.match)H.add(ae);return{...q,text:Y}}),await fe(),J)return;await new Promise(q=>{var te;(te=x.value)==null||te.unmark({done:()=>{var re;(re=x.value)==null||re.markRegExp(w(H),{done:q})}})});const Te=((Je=t.value)==null?void 0:Je.querySelectorAll(".result .excerpt"))??[];for(const q of Te)(Ue=q.querySelector('mark[data-markjs="true"]'))==null||Ue.scrollIntoView({block:"center"});(Ge=(He=r.value)==null?void 0:He.firstElementChild)==null||Ge.scrollIntoView({block:"start"})},{debounce:200,immediate:!0});async function C(m){const b=Gt(m.slice(0,m.indexOf("#")));try{if(!b)throw new Error(`Cannot find file for id: ${m}`);return{id:m,mod:await vt(()=>import(b),[])}}catch(z){return console.error(z),{id:m,mod:{}}}}const N=ne(),A=ke(()=>{var m;return((m=d.value)==null?void 0:m.length)<=0});function M(m=!0){var b,z;(b=N.value)==null||b.focus(),m&&((z=N.value)==null||z.select())}Ie(()=>{M()});function L(m){m.pointerType==="mouse"&&M()}const D=ne(-1),T=ne(!1);ze(g,m=>{D.value=m.length?0:-1,I()});function I(){fe(()=>{const m=document.querySelector(".result.selected");m&&m.scrollIntoView({block:"nearest"})})}be("ArrowUp",m=>{m.preventDefault(),D.value--,D.value<0&&(D.value=g.value.length-1),T.value=!0,I()}),be("ArrowDown",m=>{m.preventDefault(),D.value++,D.value>=g.value.length&&(D.value=0),T.value=!0,I()});const R=Rt();be("Enter",m=>{if(m.target instanceof HTMLButtonElement&&m.target.type!=="submit")return;const b=g.value[D.value];if(m.target instanceof HTMLInputElement&&!b){m.preventDefault();return}b&&(R.go(b.id),e("close"))}),be("Escape",()=>{e("close")});const F={modal:{displayDetails:"Display detailed list",resetButtonTitle:"Reset search",backButtonTitle:"Close search",noResultsText:"No results for",footer:{selectText:"to select",selectKeyAriaLabel:"enter",navigateText:"to navigate",navigateUpKeyAriaLabel:"up arrow",navigateDownKeyAriaLabel:"down arrow",closeText:"to close",closeKeyAriaLabel:"escape"}}},c=Zt((P=u.value.search)==null?void 0:P.options,F);Ie(()=>{window.history.pushState(null,"",null)}),Mt("popstate",m=>{m.preventDefault(),e("close")});const f=Lt(qt?document.body:null);Ie(()=>{fe(()=>{f.value=!0,fe().then(()=>i())})}),Pt(()=>{f.value=!1});function p(){d.value="",fe().then(()=>M(!1))}function w(m){return new RegExp([...m].sort((b,z)=>z.length-b.length).map(b=>`(${b.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&").replace(/-/g,"\\x2d")})`).join("|"),"gi")}return(m,b)=>{var z,G,j,J;return Z(),zt(Kt,{to:"body"},[S("div",{ref_key:"el",ref:t,role:"button","aria-owns":(z=g.value)!=null&&z.length?"localsearch-list":void 0,"aria-expanded":"true","aria-haspopup":"listbox","aria-labelledby":"localsearch-label",class:"VPLocalSearchBox"},[S("div",{class:"backdrop",onClick:b[0]||(b[0]=B=>m.$emit("close"))}),S("div",jr,[S("form",{class:"search-bar",onPointerup:b[4]||(b[4]=B=>L(B)),onSubmit:b[5]||(b[5]=Bt(()=>{},["prevent"]))},[S("label",{title:m.placeholder,id:"localsearch-label",for:"localsearch-input"},Hr,8,Jr),S("div",Gr,[S("button",{class:"back-button",title:W(c)("modal.backButtonTitle"),onClick:b[1]||(b[1]=B=>m.$emit("close"))},Yr,8,qr)]),Vt(S("input",{ref_key:"searchInput",ref:N,"onUpdate:modelValue":b[2]||(b[2]=B=>Wt(d)?d.value=B:null),placeholder:m.placeholder,id:"localsearch-input","aria-labelledby":"localsearch-label",class:"search-input"},null,8,Zr),[[$t,W(d)]]),S("div",Xr,[y.value?we("",!0):(Z(),ee("button",{key:0,class:et(["toggle-layout-button",{"detailed-list":W(v)}]),type:"button",title:W(c)("modal.displayDetails"),onClick:b[3]||(b[3]=B=>D.value>-1&&(v.value=!W(v)))},ra,10,ea)),S("button",{class:"clear-button",type:"reset",disabled:A.value,title:W(c)("modal.resetButtonTitle"),onClick:p},ia,8,aa)])],32),S("ul",{ref_key:"resultsEl",ref:r,id:(G=g.value)!=null&&G.length?"localsearch-list":void 0,role:(j=g.value)!=null&&j.length?"listbox":void 0,"aria-labelledby":(J=g.value)!=null&&J.length?"localsearch-label":void 0,class:"results",onMousemove:b[7]||(b[7]=B=>T.value=!1)},[(Z(!0),ee(rt,null,tt(g.value,(B,U)=>(Z(),ee("li",{key:B.id,role:"option","aria-selected":D.value===U?"true":"false"},[S("a",{href:B.id,class:et(["result",{selected:D.value===U}]),"aria-label":[...B.titles,B.title].join(" > "),onMouseenter:H=>!T.value&&(D.value=U),onFocusin:H=>D.value=U,onClick:b[6]||(b[6]=H=>m.$emit("close"))},[S("div",null,[S("div",la,[ca,(Z(!0),ee(rt,null,tt(B.titles,(H,Te)=>(Z(),ee("span",{key:Te,class:"title"},[S("span",{class:"text",innerHTML:H},null,8,fa),da]))),128)),S("span",ha,[S("span",{class:"text",innerHTML:B.title},null,8,va)])]),W(v)?(Z(),ee("div",pa,[B.text?(Z(),ee("div",ya,[S("div",{class:"vp-doc",innerHTML:B.text},null,8,ma)])):we("",!0),ga,ba])):we("",!0)])],42,ua)],8,sa))),128)),W(d)&&!g.value.length&&E.value?(Z(),ee("li",wa,[de(he(W(c)("modal.noResultsText"))+' "',1),S("strong",null,he(W(d)),1),de('" ')])):we("",!0)],40,oa),S("div",xa,[S("span",null,[S("kbd",{"aria-label":W(c)("modal.footer.navigateUpKeyAriaLabel")},Sa,8,Fa),S("kbd",{"aria-label":W(c)("modal.footer.navigateDownKeyAriaLabel")},Na,8,Aa),de(" "+he(W(c)("modal.footer.navigateText")),1)]),S("span",null,[S("kbd",{"aria-label":W(c)("modal.footer.selectKeyAriaLabel")},Ia,8,Ta),de(" "+he(W(c)("modal.footer.selectText")),1)]),S("span",null,[S("kbd",{"aria-label":W(c)("modal.footer.closeKeyAriaLabel")},"esc",8,Da),de(" "+he(W(c)("modal.footer.closeText")),1)])])])],8,Kr)])}}});const Pa=Qt(_a,[["__scopeId","data-v-58ff7037"]]);export{Pa as default}; diff --git a/assets/chunks/framework.1293becd.js b/assets/chunks/framework.1293becd.js new file mode 100644 index 000000000..af5b6d631 --- /dev/null +++ b/assets/chunks/framework.1293becd.js @@ -0,0 +1,2 @@ +function Cr(e,t){const n=Object.create(null),r=e.split(",");for(let s=0;s!!n[s.toLowerCase()]:s=>!!n[s]}const te={},gt=[],Re=()=>{},gi=()=>!1,mi=/^on[^a-z]/,zt=e=>mi.test(e),Er=e=>e.startsWith("onUpdate:"),oe=Object.assign,xr=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},yi=Object.prototype.hasOwnProperty,Y=(e,t)=>yi.call(e,t),D=Array.isArray,mt=e=>Yt(e)==="[object Map]",Ks=e=>Yt(e)==="[object Set]",qr=e=>Yt(e)==="[object Date]",K=e=>typeof e=="function",ne=e=>typeof e=="string",kt=e=>typeof e=="symbol",Z=e=>e!==null&&typeof e=="object",Ws=e=>Z(e)&&K(e.then)&&K(e.catch),Vs=Object.prototype.toString,Yt=e=>Vs.call(e),_i=e=>Yt(e).slice(8,-1),qs=e=>Yt(e)==="[object Object]",Tr=e=>ne(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Ft=Cr(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Rn=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},vi=/-(\w)/g,He=Rn(e=>e.replace(vi,(t,n)=>n?n.toUpperCase():"")),bi=/\B([A-Z])/g,ft=Rn(e=>e.replace(bi,"-$1").toLowerCase()),Pn=Rn(e=>e.charAt(0).toUpperCase()+e.slice(1)),pn=Rn(e=>e?`on${Pn(e)}`:""),Bt=(e,t)=>!Object.is(e,t),gn=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},lr=e=>{const t=parseFloat(e);return isNaN(t)?e:t},wi=e=>{const t=ne(e)?Number(e):NaN;return isNaN(t)?e:t};let zr;const cr=()=>zr||(zr=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Mn(e){if(D(e)){const t={};for(let n=0;n{if(n){const r=n.split(Ei);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function Fn(e){let t="";if(ne(e))t=e;else if(D(e))for(let n=0;nne(e)?e:e==null?"":D(e)||Z(e)&&(e.toString===Vs||!K(e.toString))?JSON.stringify(e,Ys,2):String(e),Ys=(e,t)=>t&&t.__v_isRef?Ys(e,t.value):mt(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,s])=>(n[`${r} =>`]=s,n),{})}:Ks(t)?{[`Set(${t.size})`]:[...t.values()]}:Z(t)&&!D(t)&&!qs(t)?String(t):t;let ve;class Js{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=ve,!t&&ve&&(this.index=(ve.scopes||(ve.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=ve;try{return ve=this,t()}finally{ve=n}}}on(){ve=this}off(){ve=this.parent}stop(t){if(this._active){let n,r;for(n=0,r=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},Qs=e=>(e.w&Xe)>0,Zs=e=>(e.n&Xe)>0,Mi=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r{(f==="length"||f>=c)&&l.push(a)})}else switch(n!==void 0&&l.push(i.get(n)),t){case"add":D(e)?Tr(n)&&l.push(i.get("length")):(l.push(i.get(at)),mt(e)&&l.push(i.get(ur)));break;case"delete":D(e)||(l.push(i.get(at)),mt(e)&&l.push(i.get(ur)));break;case"set":mt(e)&&l.push(i.get(at));break}if(l.length===1)l[0]&&fr(l[0]);else{const c=[];for(const a of l)a&&c.push(...a);fr(Ar(c))}}function fr(e,t){const n=D(e)?e:[...e];for(const r of n)r.computed&&Jr(r);for(const r of n)r.computed||Jr(r)}function Jr(e,t){(e!==Se||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function Ii(e,t){var n;return(n=bn.get(e))==null?void 0:n.get(t)}const Li=Cr("__proto__,__v_isRef,__isVue"),to=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(kt)),Ni=In(),Hi=In(!1,!0),$i=In(!0),ji=In(!0,!0),Xr=Di();function Di(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=J(this);for(let o=0,i=this.length;o{e[t]=function(...n){Tt();const r=J(this)[t].apply(this,n);return At(),r}}),e}function ki(e){const t=J(this);return me(t,"has",e),t.hasOwnProperty(e)}function In(e=!1,t=!1){return function(r,s,o){if(s==="__v_isReactive")return!e;if(s==="__v_isReadonly")return e;if(s==="__v_isShallow")return t;if(s==="__v_raw"&&o===(e?t?co:lo:t?io:oo).get(r))return r;const i=D(r);if(!e){if(i&&Y(Xr,s))return Reflect.get(Xr,s,o);if(s==="hasOwnProperty")return ki}const l=Reflect.get(r,s,o);return(kt(s)?to.has(s):Li(s))||(e||me(r,"get",s),t)?l:ue(l)?i&&Tr(s)?l:l.value:Z(l)?e?Jt(l):Hn(l):l}}const Bi=no(),Ui=no(!0);function no(e=!1){return function(n,r,s,o){let i=n[r];if(wt(i)&&ue(i)&&!ue(s))return!1;if(!e&&(!wn(s)&&!wt(s)&&(i=J(i),s=J(s)),!D(n)&&ue(i)&&!ue(s)))return i.value=s,!0;const l=D(n)&&Tr(r)?Number(r)e,Ln=e=>Reflect.getPrototypeOf(e);function tn(e,t,n=!1,r=!1){e=e.__v_raw;const s=J(e),o=J(t);n||(t!==o&&me(s,"get",t),me(s,"get",o));const{has:i}=Ln(s),l=r?Or:n?Rr:Ut;if(i.call(s,t))return l(e.get(t));if(i.call(s,o))return l(e.get(o));e!==s&&e.get(t)}function nn(e,t=!1){const n=this.__v_raw,r=J(n),s=J(e);return t||(e!==s&&me(r,"has",e),me(r,"has",s)),e===s?n.has(e):n.has(e)||n.has(s)}function rn(e,t=!1){return e=e.__v_raw,!t&&me(J(e),"iterate",at),Reflect.get(e,"size",e)}function Qr(e){e=J(e);const t=J(this);return Ln(t).has.call(t,e)||(t.add(e),ke(t,"add",e,e)),this}function Zr(e,t){t=J(t);const n=J(this),{has:r,get:s}=Ln(n);let o=r.call(n,e);o||(e=J(e),o=r.call(n,e));const i=s.call(n,e);return n.set(e,t),o?Bt(t,i)&&ke(n,"set",e,t):ke(n,"add",e,t),this}function Gr(e){const t=J(this),{has:n,get:r}=Ln(t);let s=n.call(t,e);s||(e=J(e),s=n.call(t,e)),r&&r.call(t,e);const o=t.delete(e);return s&&ke(t,"delete",e,void 0),o}function es(){const e=J(this),t=e.size!==0,n=e.clear();return t&&ke(e,"clear",void 0,void 0),n}function sn(e,t){return function(r,s){const o=this,i=o.__v_raw,l=J(i),c=t?Or:e?Rr:Ut;return!e&&me(l,"iterate",at),i.forEach((a,f)=>r.call(s,c(a),c(f),o))}}function on(e,t,n){return function(...r){const s=this.__v_raw,o=J(s),i=mt(o),l=e==="entries"||e===Symbol.iterator&&i,c=e==="keys"&&i,a=s[e](...r),f=n?Or:t?Rr:Ut;return!t&&me(o,"iterate",c?ur:at),{next(){const{value:h,done:p}=a.next();return p?{value:h,done:p}:{value:l?[f(h[0]),f(h[1])]:f(h),done:p}},[Symbol.iterator](){return this}}}}function Ue(e){return function(...t){return e==="delete"?!1:this}}function Yi(){const e={get(o){return tn(this,o)},get size(){return rn(this)},has:nn,add:Qr,set:Zr,delete:Gr,clear:es,forEach:sn(!1,!1)},t={get(o){return tn(this,o,!1,!0)},get size(){return rn(this)},has:nn,add:Qr,set:Zr,delete:Gr,clear:es,forEach:sn(!1,!0)},n={get(o){return tn(this,o,!0)},get size(){return rn(this,!0)},has(o){return nn.call(this,o,!0)},add:Ue("add"),set:Ue("set"),delete:Ue("delete"),clear:Ue("clear"),forEach:sn(!0,!1)},r={get(o){return tn(this,o,!0,!0)},get size(){return rn(this,!0)},has(o){return nn.call(this,o,!0)},add:Ue("add"),set:Ue("set"),delete:Ue("delete"),clear:Ue("clear"),forEach:sn(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(o=>{e[o]=on(o,!1,!1),n[o]=on(o,!0,!1),t[o]=on(o,!1,!0),r[o]=on(o,!0,!0)}),[e,n,t,r]}const[Ji,Xi,Qi,Zi]=Yi();function Nn(e,t){const n=t?e?Zi:Qi:e?Xi:Ji;return(r,s,o)=>s==="__v_isReactive"?!e:s==="__v_isReadonly"?e:s==="__v_raw"?r:Reflect.get(Y(n,s)&&s in r?n:r,s,o)}const Gi={get:Nn(!1,!1)},el={get:Nn(!1,!0)},tl={get:Nn(!0,!1)},nl={get:Nn(!0,!0)},oo=new WeakMap,io=new WeakMap,lo=new WeakMap,co=new WeakMap;function rl(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function sl(e){return e.__v_skip||!Object.isExtensible(e)?0:rl(_i(e))}function Hn(e){return wt(e)?e:$n(e,!1,ro,Gi,oo)}function ol(e){return $n(e,!1,qi,el,io)}function Jt(e){return $n(e,!0,so,tl,lo)}function Wa(e){return $n(e,!0,zi,nl,co)}function $n(e,t,n,r,s){if(!Z(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const o=s.get(e);if(o)return o;const i=sl(e);if(i===0)return e;const l=new Proxy(e,i===2?r:n);return s.set(e,l),l}function yt(e){return wt(e)?yt(e.__v_raw):!!(e&&e.__v_isReactive)}function wt(e){return!!(e&&e.__v_isReadonly)}function wn(e){return!!(e&&e.__v_isShallow)}function ao(e){return yt(e)||wt(e)}function J(e){const t=e&&e.__v_raw;return t?J(t):e}function It(e){return _n(e,"__v_skip",!0),e}const Ut=e=>Z(e)?Hn(e):e,Rr=e=>Z(e)?Jt(e):e;function Pr(e){ze&&Se&&(e=J(e),eo(e.dep||(e.dep=Ar())))}function Mr(e,t){e=J(e);const n=e.dep;n&&fr(n)}function ue(e){return!!(e&&e.__v_isRef===!0)}function le(e){return uo(e,!1)}function jn(e){return uo(e,!0)}function uo(e,t){return ue(e)?e:new il(e,t)}class il{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:J(t),this._value=n?t:Ut(t)}get value(){return Pr(this),this._value}set value(t){const n=this.__v_isShallow||wn(t)||wt(t);t=n?t:J(t),Bt(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Ut(t),Mr(this))}}function fo(e){return ue(e)?e.value:e}const ll={get:(e,t,n)=>fo(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const s=e[t];return ue(s)&&!ue(n)?(s.value=n,!0):Reflect.set(e,t,n,r)}};function ho(e){return yt(e)?e:new Proxy(e,ll)}class cl{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:r}=t(()=>Pr(this),()=>Mr(this));this._get=n,this._set=r}get value(){return this._get()}set value(t){this._set(t)}}function po(e){return new cl(e)}class al{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return Ii(J(this._object),this._key)}}class ul{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function fl(e,t,n){return ue(e)?e:K(e)?new ul(e):Z(e)&&arguments.length>1?dl(e,t,n):le(e)}function dl(e,t,n){const r=e[t];return ue(r)?r:new al(e,t,n)}class hl{constructor(t,n,r,s){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new Sr(t,()=>{this._dirty||(this._dirty=!0,Mr(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!s,this.__v_isReadonly=r}get value(){const t=J(this);return Pr(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function pl(e,t,n=!1){let r,s;const o=K(e);return o?(r=e,s=Re):(r=e.get,s=e.set),new hl(r,s,o||!s,n)}function Ye(e,t,n,r){let s;try{s=r?e(...r):e()}catch(o){Xt(o,t,n)}return s}function xe(e,t,n,r){if(K(e)){const o=Ye(e,t,n,r);return o&&Ws(o)&&o.catch(i=>{Xt(i,t,n)}),o}const s=[];for(let o=0;o>>1;Wt(de[r])Le&&de.splice(t,1)}function _l(e){D(e)?_t.push(...e):(!De||!De.includes(e,e.allowRecurse?rt+1:rt))&&_t.push(e),mo()}function ts(e,t=Kt?Le+1:0){for(;tWt(n)-Wt(r)),rt=0;rte.id==null?1/0:e.id,vl=(e,t)=>{const n=Wt(e)-Wt(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function yo(e){dr=!1,Kt=!0,de.sort(vl);const t=Re;try{for(Le=0;Lene(v)?v.trim():v)),h&&(s=n.map(lr))}let l,c=r[l=pn(t)]||r[l=pn(He(t))];!c&&o&&(c=r[l=pn(ft(t))]),c&&xe(c,e,6,s);const a=r[l+"Once"];if(a){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,xe(a,e,6,s)}}function _o(e,t,n=!1){const r=t.emitsCache,s=r.get(e);if(s!==void 0)return s;const o=e.emits;let i={},l=!1;if(!K(e)){const c=a=>{const f=_o(a,t,!0);f&&(l=!0,oe(i,f))};!n&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!o&&!l?(Z(e)&&r.set(e,null),null):(D(o)?o.forEach(c=>i[c]=null):oe(i,o),Z(e)&&r.set(e,i),i)}function Bn(e,t){return!e||!zt(t)?!1:(t=t.slice(2).replace(/Once$/,""),Y(e,t[0].toLowerCase()+t.slice(1))||Y(e,ft(t))||Y(e,t))}let fe=null,Un=null;function En(e){const t=fe;return fe=e,Un=e&&e.type.__scopeId||null,t}function Va(e){Un=e}function qa(){Un=null}function wl(e,t=fe,n){if(!t||e._n)return e;const r=(...s)=>{r._d&&ps(-1);const o=En(t);let i;try{i=e(...s)}finally{En(o),r._d&&ps(1)}return i};return r._n=!0,r._c=!0,r._d=!0,r}function Zn(e){const{type:t,vnode:n,proxy:r,withProxy:s,props:o,propsOptions:[i],slots:l,attrs:c,emit:a,render:f,renderCache:h,data:p,setupState:v,ctx:C,inheritAttrs:T}=e;let L,m;const _=En(e);try{if(n.shapeFlag&4){const S=s||r;L=Ae(f.call(S,S,h,o,v,p,C)),m=c}else{const S=t;L=Ae(S.length>1?S(o,{attrs:c,slots:l,emit:a}):S(o,null)),m=t.props?c:Cl(c)}}catch(S){$t.length=0,Xt(S,e,1),L=se(be)}let H=L;if(m&&T!==!1){const S=Object.keys(m),{shapeFlag:B}=H;S.length&&B&7&&(i&&S.some(Er)&&(m=El(m,i)),H=Qe(H,m))}return n.dirs&&(H=Qe(H),H.dirs=H.dirs?H.dirs.concat(n.dirs):n.dirs),n.transition&&(H.transition=n.transition),L=H,En(_),L}const Cl=e=>{let t;for(const n in e)(n==="class"||n==="style"||zt(n))&&((t||(t={}))[n]=e[n]);return t},El=(e,t)=>{const n={};for(const r in e)(!Er(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function xl(e,t,n){const{props:r,children:s,component:o}=e,{props:i,children:l,patchFlag:c}=t,a=o.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return r?ns(r,i,a):!!i;if(c&8){const f=t.dynamicProps;for(let h=0;he.__isSuspense;function vo(e,t){t&&t.pendingBranch?D(e)?t.effects.push(...e):t.effects.push(e):_l(e)}function Kn(e,t){return Wn(e,null,t)}function za(e,t){return Wn(e,null,{flush:"post"})}const ln={};function Ne(e,t,n){return Wn(e,t,n)}function Wn(e,t,{immediate:n,deep:r,flush:s,onTrack:o,onTrigger:i}=te){var l;const c=Xs()===((l=ae)==null?void 0:l.scope)?ae:null;let a,f=!1,h=!1;if(ue(e)?(a=()=>e.value,f=wn(e)):yt(e)?(a=()=>e,r=!0):D(e)?(h=!0,f=e.some(S=>yt(S)||wn(S)),a=()=>e.map(S=>{if(ue(S))return S.value;if(yt(S))return lt(S);if(K(S))return Ye(S,c,2)})):K(e)?t?a=()=>Ye(e,c,2):a=()=>{if(!(c&&c.isUnmounted))return p&&p(),xe(e,c,3,[v])}:a=Re,t&&r){const S=a;a=()=>lt(S())}let p,v=S=>{p=_.onStop=()=>{Ye(S,c,4)}},C;if(xt)if(v=Re,t?n&&xe(t,c,3,[a(),h?[]:void 0,v]):a(),s==="sync"){const S=wc();C=S.__watcherHandles||(S.__watcherHandles=[])}else return Re;let T=h?new Array(e.length).fill(ln):ln;const L=()=>{if(_.active)if(t){const S=_.run();(r||f||(h?S.some((B,W)=>Bt(B,T[W])):Bt(S,T)))&&(p&&p(),xe(t,c,3,[S,T===ln?void 0:h&&T[0]===ln?[]:T,v]),T=S)}else _.run()};L.allowRecurse=!!t;let m;s==="sync"?m=L:s==="post"?m=()=>pe(L,c&&c.suspense):(L.pre=!0,c&&(L.id=c.uid),m=()=>kn(L));const _=new Sr(a,m);t?n?L():T=_.run():s==="post"?pe(_.run.bind(_),c&&c.suspense):_.run();const H=()=>{_.stop(),c&&c.scope&&xr(c.scope.effects,_)};return C&&C.push(H),H}function Sl(e,t,n){const r=this.proxy,s=ne(e)?e.includes(".")?bo(r,e):()=>r[e]:e.bind(r,r);let o;K(t)?o=t:(o=t.handler,n=t);const i=ae;Et(this);const l=Wn(s,o.bind(r),n);return i?Et(i):ut(),l}function bo(e,t){const n=t.split(".");return()=>{let r=e;for(let s=0;s{lt(n,t)});else if(qs(e))for(const n in e)lt(e[n],t);return e}function Ya(e,t){const n=fe;if(n===null)return e;const r=Yn(n)||n.proxy,s=e.dirs||(e.dirs=[]);for(let o=0;o{e.isMounted=!0}),Ao(()=>{e.isUnmounting=!0}),e}const we=[Function,Array],wo={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:we,onEnter:we,onAfterEnter:we,onEnterCancelled:we,onBeforeLeave:we,onLeave:we,onAfterLeave:we,onLeaveCancelled:we,onBeforeAppear:we,onAppear:we,onAfterAppear:we,onAppearCancelled:we},Rl={name:"BaseTransition",props:wo,setup(e,{slots:t}){const n=Zt(),r=Ol();let s;return()=>{const o=t.default&&Eo(t.default(),!0);if(!o||!o.length)return;let i=o[0];if(o.length>1){for(const T of o)if(T.type!==be){i=T;break}}const l=J(e),{mode:c}=l;if(r.isLeaving)return Gn(i);const a=rs(i);if(!a)return Gn(i);const f=hr(a,l,r,n);pr(a,f);const h=n.subTree,p=h&&rs(h);let v=!1;const{getTransitionKey:C}=a.type;if(C){const T=C();s===void 0?s=T:T!==s&&(s=T,v=!0)}if(p&&p.type!==be&&(!st(a,p)||v)){const T=hr(p,l,r,n);if(pr(p,T),c==="out-in")return r.isLeaving=!0,T.afterLeave=()=>{r.isLeaving=!1,n.update.active!==!1&&n.update()},Gn(i);c==="in-out"&&a.type!==be&&(T.delayLeave=(L,m,_)=>{const H=Co(r,p);H[String(p.key)]=p,L._leaveCb=()=>{m(),L._leaveCb=void 0,delete f.delayedLeave},f.delayedLeave=_})}return i}}},Pl=Rl;function Co(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function hr(e,t,n,r){const{appear:s,mode:o,persisted:i=!1,onBeforeEnter:l,onEnter:c,onAfterEnter:a,onEnterCancelled:f,onBeforeLeave:h,onLeave:p,onAfterLeave:v,onLeaveCancelled:C,onBeforeAppear:T,onAppear:L,onAfterAppear:m,onAppearCancelled:_}=t,H=String(e.key),S=Co(n,e),B=(y,P)=>{y&&xe(y,r,9,P)},W=(y,P)=>{const I=P[1];B(y,P),D(y)?y.every(V=>V.length<=1)&&I():y.length<=1&&I()},k={mode:o,persisted:i,beforeEnter(y){let P=l;if(!n.isMounted)if(s)P=T||l;else return;y._leaveCb&&y._leaveCb(!0);const I=S[H];I&&st(e,I)&&I.el._leaveCb&&I.el._leaveCb(),B(P,[y])},enter(y){let P=c,I=a,V=f;if(!n.isMounted)if(s)P=L||c,I=m||a,V=_||f;else return;let M=!1;const q=y._enterCb=N=>{M||(M=!0,N?B(V,[y]):B(I,[y]),k.delayedLeave&&k.delayedLeave(),y._enterCb=void 0)};P?W(P,[y,q]):q()},leave(y,P){const I=String(e.key);if(y._enterCb&&y._enterCb(!0),n.isUnmounting)return P();B(h,[y]);let V=!1;const M=y._leaveCb=q=>{V||(V=!0,P(),q?B(C,[y]):B(v,[y]),y._leaveCb=void 0,S[I]===e&&delete S[I])};S[I]=e,p?W(p,[y,M]):M()},clone(y){return hr(y,t,n,r)}};return k}function Gn(e){if(Qt(e))return e=Qe(e),e.children=null,e}function rs(e){return Qt(e)?e.children?e.children[0]:void 0:e}function pr(e,t){e.shapeFlag&6&&e.component?pr(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Eo(e,t=!1,n){let r=[],s=0;for(let o=0;o1)for(let o=0;ooe({name:e.name},t,{setup:e}))():e}const vt=e=>!!e.type.__asyncLoader;function Ja(e){K(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:s=200,timeout:o,suspensible:i=!0,onError:l}=e;let c=null,a,f=0;const h=()=>(f++,c=null,p()),p=()=>{let v;return c||(v=c=t().catch(C=>{if(C=C instanceof Error?C:new Error(String(C)),l)return new Promise((T,L)=>{l(C,()=>T(h()),()=>L(C),f+1)});throw C}).then(C=>v!==c&&c?c:(C&&(C.__esModule||C[Symbol.toStringTag]==="Module")&&(C=C.default),a=C,C)))};return Ir({name:"AsyncComponentWrapper",__asyncLoader:p,get __asyncResolved(){return a},setup(){const v=ae;if(a)return()=>er(a,v);const C=_=>{c=null,Xt(_,v,13,!r)};if(i&&v.suspense||xt)return p().then(_=>()=>er(_,v)).catch(_=>(C(_),()=>r?se(r,{error:_}):null));const T=le(!1),L=le(),m=le(!!s);return s&&setTimeout(()=>{m.value=!1},s),o!=null&&setTimeout(()=>{if(!T.value&&!L.value){const _=new Error(`Async component timed out after ${o}ms.`);C(_),L.value=_}},o),p().then(()=>{T.value=!0,v.parent&&Qt(v.parent.vnode)&&kn(v.parent.update)}).catch(_=>{C(_),L.value=_}),()=>{if(T.value&&a)return er(a,v);if(L.value&&r)return se(r,{error:L.value});if(n&&!m.value)return se(n)}}})}function er(e,t){const{ref:n,props:r,children:s,ce:o}=t.vnode,i=se(e,r,s);return i.ref=n,i.ce=o,delete t.vnode.ce,i}const Qt=e=>e.type.__isKeepAlive;function Ml(e,t){xo(e,"a",t)}function Fl(e,t){xo(e,"da",t)}function xo(e,t,n=ae){const r=e.__wdc||(e.__wdc=()=>{let s=n;for(;s;){if(s.isDeactivated)return;s=s.parent}return e()});if(Vn(t,r,n),n){let s=n.parent;for(;s&&s.parent;)Qt(s.parent.vnode)&&Il(r,t,n,s),s=s.parent}}function Il(e,t,n,r){const s=Vn(t,e,r,!0);qn(()=>{xr(r[t],s)},n)}function Vn(e,t,n=ae,r=!1){if(n){const s=n[e]||(n[e]=[]),o=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;Tt(),Et(n);const l=xe(t,n,e,i);return ut(),At(),l});return r?s.unshift(o):s.push(o),o}}const Be=e=>(t,n=ae)=>(!xt||e==="sp")&&Vn(e,(...r)=>t(...r),n),Ll=Be("bm"),St=Be("m"),Nl=Be("bu"),To=Be("u"),Ao=Be("bum"),qn=Be("um"),Hl=Be("sp"),$l=Be("rtg"),jl=Be("rtc");function Dl(e,t=ae){Vn("ec",e,t)}const Lr="components";function Xa(e,t){return Oo(Lr,e,!0,t)||e}const So=Symbol.for("v-ndc");function Qa(e){return ne(e)?Oo(Lr,e,!1)||e:e||So}function Oo(e,t,n=!0,r=!1){const s=fe||ae;if(s){const o=s.type;if(e===Lr){const l=_c(o,!1);if(l&&(l===t||l===He(t)||l===Pn(He(t))))return o}const i=ss(s[e]||o[e],t)||ss(s.appContext[e],t);return!i&&r?o:i}}function ss(e,t){return e&&(e[t]||e[He(t)]||e[Pn(He(t))])}function Za(e,t,n,r){let s;const o=n&&n[r];if(D(e)||ne(e)){s=new Array(e.length);for(let i=0,l=e.length;it(i,l,void 0,o&&o[l]));else{const i=Object.keys(e);s=new Array(i.length);for(let l=0,c=i.length;l{const o=r.fn(...s);return o&&(o.key=r.key),o}:r.fn)}return e}function eu(e,t,n={},r,s){if(fe.isCE||fe.parent&&vt(fe.parent)&&fe.parent.isCE)return t!=="default"&&(n.name=t),se("slot",n,r&&r());let o=e[t];o&&o._c&&(o._d=!1),Do();const i=o&&Ro(o(n)),l=Bo(ge,{key:n.key||i&&i.key||`_${t}`},i||(r?r():[]),i&&e._===1?64:-2);return!s&&l.scopeId&&(l.slotScopeIds=[l.scopeId+"-s"]),o&&o._c&&(o._d=!0),l}function Ro(e){return e.some(t=>Sn(t)?!(t.type===be||t.type===ge&&!Ro(t.children)):!0)?e:null}function tu(e,t){const n={};for(const r in e)n[t&&/[A-Z]/.test(r)?`on:${r}`:pn(r)]=e[r];return n}const gr=e=>e?Vo(e)?Yn(e)||e.proxy:gr(e.parent):null,Lt=oe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>gr(e.parent),$root:e=>gr(e.root),$emit:e=>e.emit,$options:e=>Nr(e),$forceUpdate:e=>e.f||(e.f=()=>kn(e.update)),$nextTick:e=>e.n||(e.n=Dn.bind(e.proxy)),$watch:e=>Sl.bind(e)}),tr=(e,t)=>e!==te&&!e.__isScriptSetup&&Y(e,t),kl={get({_:e},t){const{ctx:n,setupState:r,data:s,props:o,accessCache:i,type:l,appContext:c}=e;let a;if(t[0]!=="$"){const v=i[t];if(v!==void 0)switch(v){case 1:return r[t];case 2:return s[t];case 4:return n[t];case 3:return o[t]}else{if(tr(r,t))return i[t]=1,r[t];if(s!==te&&Y(s,t))return i[t]=2,s[t];if((a=e.propsOptions[0])&&Y(a,t))return i[t]=3,o[t];if(n!==te&&Y(n,t))return i[t]=4,n[t];mr&&(i[t]=0)}}const f=Lt[t];let h,p;if(f)return t==="$attrs"&&me(e,"get",t),f(e);if((h=l.__cssModules)&&(h=h[t]))return h;if(n!==te&&Y(n,t))return i[t]=4,n[t];if(p=c.config.globalProperties,Y(p,t))return p[t]},set({_:e},t,n){const{data:r,setupState:s,ctx:o}=e;return tr(s,t)?(s[t]=n,!0):r!==te&&Y(r,t)?(r[t]=n,!0):Y(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(o[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:s,propsOptions:o}},i){let l;return!!n[i]||e!==te&&Y(e,i)||tr(t,i)||(l=o[0])&&Y(l,i)||Y(r,i)||Y(Lt,i)||Y(s.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:Y(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function nu(){return Bl().slots}function Bl(){const e=Zt();return e.setupContext||(e.setupContext=zo(e))}function os(e){return D(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let mr=!0;function Ul(e){const t=Nr(e),n=e.proxy,r=e.ctx;mr=!1,t.beforeCreate&&is(t.beforeCreate,e,"bc");const{data:s,computed:o,methods:i,watch:l,provide:c,inject:a,created:f,beforeMount:h,mounted:p,beforeUpdate:v,updated:C,activated:T,deactivated:L,beforeDestroy:m,beforeUnmount:_,destroyed:H,unmounted:S,render:B,renderTracked:W,renderTriggered:k,errorCaptured:y,serverPrefetch:P,expose:I,inheritAttrs:V,components:M,directives:q,filters:N}=t;if(a&&Kl(a,r,null),i)for(const re in i){const G=i[re];K(G)&&(r[re]=G.bind(n))}if(s){const re=s.call(n,n);Z(re)&&(e.data=Hn(re))}if(mr=!0,o)for(const re in o){const G=o[re],Ze=K(G)?G.bind(n,n):K(G.get)?G.get.bind(n,n):Re,Gt=!K(G)&&K(G.set)?G.set.bind(n):Re,Ge=ce({get:Ze,set:Gt});Object.defineProperty(r,re,{enumerable:!0,configurable:!0,get:()=>Ge.value,set:Me=>Ge.value=Me})}if(l)for(const re in l)Po(l[re],r,n,re);if(c){const re=K(c)?c.call(n):c;Reflect.ownKeys(re).forEach(G=>{Jl(G,re[G])})}f&&is(f,e,"c");function X(re,G){D(G)?G.forEach(Ze=>re(Ze.bind(n))):G&&re(G.bind(n))}if(X(Ll,h),X(St,p),X(Nl,v),X(To,C),X(Ml,T),X(Fl,L),X(Dl,y),X(jl,W),X($l,k),X(Ao,_),X(qn,S),X(Hl,P),D(I))if(I.length){const re=e.exposed||(e.exposed={});I.forEach(G=>{Object.defineProperty(re,G,{get:()=>n[G],set:Ze=>n[G]=Ze})})}else e.exposed||(e.exposed={});B&&e.render===Re&&(e.render=B),V!=null&&(e.inheritAttrs=V),M&&(e.components=M),q&&(e.directives=q)}function Kl(e,t,n=Re){D(e)&&(e=yr(e));for(const r in e){const s=e[r];let o;Z(s)?"default"in s?o=bt(s.from||r,s.default,!0):o=bt(s.from||r):o=bt(s),ue(o)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>o.value,set:i=>o.value=i}):t[r]=o}}function is(e,t,n){xe(D(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function Po(e,t,n,r){const s=r.includes(".")?bo(n,r):()=>n[r];if(ne(e)){const o=t[e];K(o)&&Ne(s,o)}else if(K(e))Ne(s,e.bind(n));else if(Z(e))if(D(e))e.forEach(o=>Po(o,t,n,r));else{const o=K(e.handler)?e.handler.bind(n):t[e.handler];K(o)&&Ne(s,o,e)}}function Nr(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:s,optionsCache:o,config:{optionMergeStrategies:i}}=e.appContext,l=o.get(t);let c;return l?c=l:!s.length&&!n&&!r?c=t:(c={},s.length&&s.forEach(a=>xn(c,a,i,!0)),xn(c,t,i)),Z(t)&&o.set(t,c),c}function xn(e,t,n,r=!1){const{mixins:s,extends:o}=t;o&&xn(e,o,n,!0),s&&s.forEach(i=>xn(e,i,n,!0));for(const i in t)if(!(r&&i==="expose")){const l=Wl[i]||n&&n[i];e[i]=l?l(e[i],t[i]):t[i]}return e}const Wl={data:ls,props:cs,emits:cs,methods:Mt,computed:Mt,beforeCreate:he,created:he,beforeMount:he,mounted:he,beforeUpdate:he,updated:he,beforeDestroy:he,beforeUnmount:he,destroyed:he,unmounted:he,activated:he,deactivated:he,errorCaptured:he,serverPrefetch:he,components:Mt,directives:Mt,watch:ql,provide:ls,inject:Vl};function ls(e,t){return t?e?function(){return oe(K(e)?e.call(this,this):e,K(t)?t.call(this,this):t)}:t:e}function Vl(e,t){return Mt(yr(e),yr(t))}function yr(e){if(D(e)){const t={};for(let n=0;n1)return n&&K(t)?t.call(r&&r.proxy):t}}function Xl(e,t,n,r=!1){const s={},o={};_n(o,zn,1),e.propsDefaults=Object.create(null),Fo(e,t,s,o);for(const i in e.propsOptions[0])i in s||(s[i]=void 0);n?e.props=r?s:ol(s):e.type.props?e.props=s:e.props=o,e.attrs=o}function Ql(e,t,n,r){const{props:s,attrs:o,vnode:{patchFlag:i}}=e,l=J(s),[c]=e.propsOptions;let a=!1;if((r||i>0)&&!(i&16)){if(i&8){const f=e.vnode.dynamicProps;for(let h=0;h{c=!0;const[p,v]=Io(h,t,!0);oe(i,p),v&&l.push(...v)};!n&&t.mixins.length&&t.mixins.forEach(f),e.extends&&f(e.extends),e.mixins&&e.mixins.forEach(f)}if(!o&&!c)return Z(e)&&r.set(e,gt),gt;if(D(o))for(let f=0;f-1,v[1]=T<0||C-1||Y(v,"default"))&&l.push(h)}}}const a=[i,l];return Z(e)&&r.set(e,a),a}function as(e){return e[0]!=="$"}function us(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function fs(e,t){return us(e)===us(t)}function ds(e,t){return D(t)?t.findIndex(n=>fs(n,e)):K(t)&&fs(t,e)?0:-1}const Lo=e=>e[0]==="_"||e==="$stable",Hr=e=>D(e)?e.map(Ae):[Ae(e)],Zl=(e,t,n)=>{if(t._n)return t;const r=wl((...s)=>Hr(t(...s)),n);return r._c=!1,r},No=(e,t,n)=>{const r=e._ctx;for(const s in e){if(Lo(s))continue;const o=e[s];if(K(o))t[s]=Zl(s,o,r);else if(o!=null){const i=Hr(o);t[s]=()=>i}}},Ho=(e,t)=>{const n=Hr(t);e.slots.default=()=>n},Gl=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=J(t),_n(t,"_",n)):No(t,e.slots={})}else e.slots={},t&&Ho(e,t);_n(e.slots,zn,1)},ec=(e,t,n)=>{const{vnode:r,slots:s}=e;let o=!0,i=te;if(r.shapeFlag&32){const l=t._;l?n&&l===1?o=!1:(oe(s,t),!n&&l===1&&delete s._):(o=!t.$stable,No(t,s)),i=t}else t&&(Ho(e,t),i={default:1});if(o)for(const l in s)!Lo(l)&&!(l in i)&&delete s[l]};function An(e,t,n,r,s=!1){if(D(e)){e.forEach((p,v)=>An(p,t&&(D(t)?t[v]:t),n,r,s));return}if(vt(r)&&!s)return;const o=r.shapeFlag&4?Yn(r.component)||r.component.proxy:r.el,i=s?null:o,{i:l,r:c}=e,a=t&&t.r,f=l.refs===te?l.refs={}:l.refs,h=l.setupState;if(a!=null&&a!==c&&(ne(a)?(f[a]=null,Y(h,a)&&(h[a]=null)):ue(a)&&(a.value=null)),K(c))Ye(c,l,12,[i,f]);else{const p=ne(c),v=ue(c);if(p||v){const C=()=>{if(e.f){const T=p?Y(h,c)?h[c]:f[c]:c.value;s?D(T)&&xr(T,o):D(T)?T.includes(o)||T.push(o):p?(f[c]=[o],Y(h,c)&&(h[c]=f[c])):(c.value=[o],e.k&&(f[e.k]=c.value))}else p?(f[c]=i,Y(h,c)&&(h[c]=i)):v&&(c.value=i,e.k&&(f[e.k]=i))};i?(C.id=-1,pe(C,n)):C()}}}let Ke=!1;const cn=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",an=e=>e.nodeType===8;function tc(e){const{mt:t,p:n,o:{patchProp:r,createText:s,nextSibling:o,parentNode:i,remove:l,insert:c,createComment:a}}=e,f=(m,_)=>{if(!_.hasChildNodes()){n(null,m,_),Cn(),_._vnode=m;return}Ke=!1,h(_.firstChild,m,null,null,null),Cn(),_._vnode=m,Ke&&console.error("Hydration completed but contains mismatches.")},h=(m,_,H,S,B,W=!1)=>{const k=an(m)&&m.data==="[",y=()=>T(m,_,H,S,B,k),{type:P,ref:I,shapeFlag:V,patchFlag:M}=_;let q=m.nodeType;_.el=m,M===-2&&(W=!1,_.dynamicChildren=null);let N=null;switch(P){case Ct:q!==3?_.children===""?(c(_.el=s(""),i(m),m),N=m):N=y():(m.data!==_.children&&(Ke=!0,m.data=_.children),N=o(m));break;case be:q!==8||k?N=y():N=o(m);break;case Ht:if(k&&(m=o(m),q=m.nodeType),q===1||q===3){N=m;const ye=!_.children.length;for(let X=0;X<_.staticCount;X++)ye&&(_.children+=N.nodeType===1?N.outerHTML:N.data),X===_.staticCount-1&&(_.anchor=N),N=o(N);return k?o(N):N}else y();break;case ge:k?N=C(m,_,H,S,B,W):N=y();break;default:if(V&1)q!==1||_.type.toLowerCase()!==m.tagName.toLowerCase()?N=y():N=p(m,_,H,S,B,W);else if(V&6){_.slotScopeIds=B;const ye=i(m);if(t(_,ye,null,H,S,cn(ye),W),N=k?L(m):o(m),N&&an(N)&&N.data==="teleport end"&&(N=o(N)),vt(_)){let X;k?(X=se(ge),X.anchor=N?N.previousSibling:ye.lastChild):X=m.nodeType===3?Wo(""):se("div"),X.el=m,_.component.subTree=X}}else V&64?q!==8?N=y():N=_.type.hydrate(m,_,H,S,B,W,e,v):V&128&&(N=_.type.hydrate(m,_,H,S,cn(i(m)),B,W,e,h))}return I!=null&&An(I,null,S,_),N},p=(m,_,H,S,B,W)=>{W=W||!!_.dynamicChildren;const{type:k,props:y,patchFlag:P,shapeFlag:I,dirs:V}=_,M=k==="input"&&V||k==="option";if(M||P!==-1){if(V&&Ie(_,null,H,"created"),y)if(M||!W||P&48)for(const N in y)(M&&N.endsWith("value")||zt(N)&&!Ft(N))&&r(m,N,null,y[N],!1,void 0,H);else y.onClick&&r(m,"onClick",null,y.onClick,!1,void 0,H);let q;if((q=y&&y.onVnodeBeforeMount)&&Ce(q,H,_),V&&Ie(_,null,H,"beforeMount"),((q=y&&y.onVnodeMounted)||V)&&vo(()=>{q&&Ce(q,H,_),V&&Ie(_,null,H,"mounted")},S),I&16&&!(y&&(y.innerHTML||y.textContent))){let N=v(m.firstChild,_,m,H,S,B,W);for(;N;){Ke=!0;const ye=N;N=N.nextSibling,l(ye)}}else I&8&&m.textContent!==_.children&&(Ke=!0,m.textContent=_.children)}return m.nextSibling},v=(m,_,H,S,B,W,k)=>{k=k||!!_.dynamicChildren;const y=_.children,P=y.length;for(let I=0;I{const{slotScopeIds:k}=_;k&&(B=B?B.concat(k):k);const y=i(m),P=v(o(m),_,y,H,S,B,W);return P&&an(P)&&P.data==="]"?o(_.anchor=P):(Ke=!0,c(_.anchor=a("]"),y,P),P)},T=(m,_,H,S,B,W)=>{if(Ke=!0,_.el=null,W){const P=L(m);for(;;){const I=o(m);if(I&&I!==P)l(I);else break}}const k=o(m),y=i(m);return l(m),n(null,_,y,k,H,S,cn(y),B),k},L=m=>{let _=0;for(;m;)if(m=o(m),m&&an(m)&&(m.data==="["&&_++,m.data==="]")){if(_===0)return o(m);_--}return m};return[f,h]}const pe=vo;function nc(e){return $o(e)}function rc(e){return $o(e,tc)}function $o(e,t){const n=cr();n.__VUE__=!0;const{insert:r,remove:s,patchProp:o,createElement:i,createText:l,createComment:c,setText:a,setElementText:f,parentNode:h,nextSibling:p,setScopeId:v=Re,insertStaticContent:C}=e,T=(u,d,g,w=null,b=null,A=null,R=!1,x=null,O=!!d.dynamicChildren)=>{if(u===d)return;u&&!st(u,d)&&(w=en(u),Me(u,b,A,!0),u=null),d.patchFlag===-2&&(O=!1,d.dynamicChildren=null);const{type:E,ref:$,shapeFlag:F}=d;switch(E){case Ct:L(u,d,g,w);break;case be:m(u,d,g,w);break;case Ht:u==null&&_(d,g,w,R);break;case ge:M(u,d,g,w,b,A,R,x,O);break;default:F&1?B(u,d,g,w,b,A,R,x,O):F&6?q(u,d,g,w,b,A,R,x,O):(F&64||F&128)&&E.process(u,d,g,w,b,A,R,x,O,dt)}$!=null&&b&&An($,u&&u.ref,A,d||u,!d)},L=(u,d,g,w)=>{if(u==null)r(d.el=l(d.children),g,w);else{const b=d.el=u.el;d.children!==u.children&&a(b,d.children)}},m=(u,d,g,w)=>{u==null?r(d.el=c(d.children||""),g,w):d.el=u.el},_=(u,d,g,w)=>{[u.el,u.anchor]=C(u.children,d,g,w,u.el,u.anchor)},H=({el:u,anchor:d},g,w)=>{let b;for(;u&&u!==d;)b=p(u),r(u,g,w),u=b;r(d,g,w)},S=({el:u,anchor:d})=>{let g;for(;u&&u!==d;)g=p(u),s(u),u=g;s(d)},B=(u,d,g,w,b,A,R,x,O)=>{R=R||d.type==="svg",u==null?W(d,g,w,b,A,R,x,O):P(u,d,b,A,R,x,O)},W=(u,d,g,w,b,A,R,x)=>{let O,E;const{type:$,props:F,shapeFlag:j,transition:U,dirs:z}=u;if(O=u.el=i(u.type,A,F&&F.is,F),j&8?f(O,u.children):j&16&&y(u.children,O,null,w,b,A&&$!=="foreignObject",R,x),z&&Ie(u,null,w,"created"),k(O,u,u.scopeId,R,w),F){for(const Q in F)Q!=="value"&&!Ft(Q)&&o(O,Q,null,F[Q],A,u.children,w,b,je);"value"in F&&o(O,"value",null,F.value),(E=F.onVnodeBeforeMount)&&Ce(E,w,u)}z&&Ie(u,null,w,"beforeMount");const ee=(!b||b&&!b.pendingBranch)&&U&&!U.persisted;ee&&U.beforeEnter(O),r(O,d,g),((E=F&&F.onVnodeMounted)||ee||z)&&pe(()=>{E&&Ce(E,w,u),ee&&U.enter(O),z&&Ie(u,null,w,"mounted")},b)},k=(u,d,g,w,b)=>{if(g&&v(u,g),w)for(let A=0;A{for(let E=O;E{const x=d.el=u.el;let{patchFlag:O,dynamicChildren:E,dirs:$}=d;O|=u.patchFlag&16;const F=u.props||te,j=d.props||te;let U;g&&et(g,!1),(U=j.onVnodeBeforeUpdate)&&Ce(U,g,d,u),$&&Ie(d,u,g,"beforeUpdate"),g&&et(g,!0);const z=b&&d.type!=="foreignObject";if(E?I(u.dynamicChildren,E,x,g,w,z,A):R||G(u,d,x,null,g,w,z,A,!1),O>0){if(O&16)V(x,d,F,j,g,w,b);else if(O&2&&F.class!==j.class&&o(x,"class",null,j.class,b),O&4&&o(x,"style",F.style,j.style,b),O&8){const ee=d.dynamicProps;for(let Q=0;Q{U&&Ce(U,g,d,u),$&&Ie(d,u,g,"updated")},w)},I=(u,d,g,w,b,A,R)=>{for(let x=0;x{if(g!==w){if(g!==te)for(const x in g)!Ft(x)&&!(x in w)&&o(u,x,g[x],null,R,d.children,b,A,je);for(const x in w){if(Ft(x))continue;const O=w[x],E=g[x];O!==E&&x!=="value"&&o(u,x,E,O,R,d.children,b,A,je)}"value"in w&&o(u,"value",g.value,w.value)}},M=(u,d,g,w,b,A,R,x,O)=>{const E=d.el=u?u.el:l(""),$=d.anchor=u?u.anchor:l("");let{patchFlag:F,dynamicChildren:j,slotScopeIds:U}=d;U&&(x=x?x.concat(U):U),u==null?(r(E,g,w),r($,g,w),y(d.children,g,$,b,A,R,x,O)):F>0&&F&64&&j&&u.dynamicChildren?(I(u.dynamicChildren,j,g,b,A,R,x),(d.key!=null||b&&d===b.subTree)&&$r(u,d,!0)):G(u,d,g,$,b,A,R,x,O)},q=(u,d,g,w,b,A,R,x,O)=>{d.slotScopeIds=x,u==null?d.shapeFlag&512?b.ctx.activate(d,g,w,R,O):N(d,g,w,b,A,R,O):ye(u,d,O)},N=(u,d,g,w,b,A,R)=>{const x=u.component=pc(u,w,b);if(Qt(u)&&(x.ctx.renderer=dt),gc(x),x.asyncDep){if(b&&b.registerDep(x,X),!u.el){const O=x.subTree=se(be);m(null,O,d,g)}return}X(x,u,d,g,b,A,R)},ye=(u,d,g)=>{const w=d.component=u.component;if(xl(u,d,g))if(w.asyncDep&&!w.asyncResolved){re(w,d,g);return}else w.next=d,yl(w.update),w.update();else d.el=u.el,w.vnode=d},X=(u,d,g,w,b,A,R)=>{const x=()=>{if(u.isMounted){let{next:$,bu:F,u:j,parent:U,vnode:z}=u,ee=$,Q;et(u,!1),$?($.el=z.el,re(u,$,R)):$=z,F&&gn(F),(Q=$.props&&$.props.onVnodeBeforeUpdate)&&Ce(Q,U,$,z),et(u,!0);const ie=Zn(u),Te=u.subTree;u.subTree=ie,T(Te,ie,h(Te.el),en(Te),u,b,A),$.el=ie.el,ee===null&&Tl(u,ie.el),j&&pe(j,b),(Q=$.props&&$.props.onVnodeUpdated)&&pe(()=>Ce(Q,U,$,z),b)}else{let $;const{el:F,props:j}=d,{bm:U,m:z,parent:ee}=u,Q=vt(d);if(et(u,!1),U&&gn(U),!Q&&($=j&&j.onVnodeBeforeMount)&&Ce($,ee,d),et(u,!0),F&&Qn){const ie=()=>{u.subTree=Zn(u),Qn(F,u.subTree,u,b,null)};Q?d.type.__asyncLoader().then(()=>!u.isUnmounted&&ie()):ie()}else{const ie=u.subTree=Zn(u);T(null,ie,g,w,u,b,A),d.el=ie.el}if(z&&pe(z,b),!Q&&($=j&&j.onVnodeMounted)){const ie=d;pe(()=>Ce($,ee,ie),b)}(d.shapeFlag&256||ee&&vt(ee.vnode)&&ee.vnode.shapeFlag&256)&&u.a&&pe(u.a,b),u.isMounted=!0,d=g=w=null}},O=u.effect=new Sr(x,()=>kn(E),u.scope),E=u.update=()=>O.run();E.id=u.uid,et(u,!0),E()},re=(u,d,g)=>{d.component=u;const w=u.vnode.props;u.vnode=d,u.next=null,Ql(u,d.props,w,g),ec(u,d.children,g),Tt(),ts(),At()},G=(u,d,g,w,b,A,R,x,O=!1)=>{const E=u&&u.children,$=u?u.shapeFlag:0,F=d.children,{patchFlag:j,shapeFlag:U}=d;if(j>0){if(j&128){Gt(E,F,g,w,b,A,R,x,O);return}else if(j&256){Ze(E,F,g,w,b,A,R,x,O);return}}U&8?($&16&&je(E,b,A),F!==E&&f(g,F)):$&16?U&16?Gt(E,F,g,w,b,A,R,x,O):je(E,b,A,!0):($&8&&f(g,""),U&16&&y(F,g,w,b,A,R,x,O))},Ze=(u,d,g,w,b,A,R,x,O)=>{u=u||gt,d=d||gt;const E=u.length,$=d.length,F=Math.min(E,$);let j;for(j=0;j$?je(u,b,A,!0,!1,F):y(d,g,w,b,A,R,x,O,F)},Gt=(u,d,g,w,b,A,R,x,O)=>{let E=0;const $=d.length;let F=u.length-1,j=$-1;for(;E<=F&&E<=j;){const U=u[E],z=d[E]=O?qe(d[E]):Ae(d[E]);if(st(U,z))T(U,z,g,null,b,A,R,x,O);else break;E++}for(;E<=F&&E<=j;){const U=u[F],z=d[j]=O?qe(d[j]):Ae(d[j]);if(st(U,z))T(U,z,g,null,b,A,R,x,O);else break;F--,j--}if(E>F){if(E<=j){const U=j+1,z=U<$?d[U].el:w;for(;E<=j;)T(null,d[E]=O?qe(d[E]):Ae(d[E]),g,z,b,A,R,x,O),E++}}else if(E>j)for(;E<=F;)Me(u[E],b,A,!0),E++;else{const U=E,z=E,ee=new Map;for(E=z;E<=j;E++){const _e=d[E]=O?qe(d[E]):Ae(d[E]);_e.key!=null&&ee.set(_e.key,E)}let Q,ie=0;const Te=j-z+1;let ht=!1,Kr=0;const Ot=new Array(Te);for(E=0;E=Te){Me(_e,b,A,!0);continue}let Fe;if(_e.key!=null)Fe=ee.get(_e.key);else for(Q=z;Q<=j;Q++)if(Ot[Q-z]===0&&st(_e,d[Q])){Fe=Q;break}Fe===void 0?Me(_e,b,A,!0):(Ot[Fe-z]=E+1,Fe>=Kr?Kr=Fe:ht=!0,T(_e,d[Fe],g,null,b,A,R,x,O),ie++)}const Wr=ht?sc(Ot):gt;for(Q=Wr.length-1,E=Te-1;E>=0;E--){const _e=z+E,Fe=d[_e],Vr=_e+1<$?d[_e+1].el:w;Ot[E]===0?T(null,Fe,g,Vr,b,A,R,x,O):ht&&(Q<0||E!==Wr[Q]?Ge(Fe,g,Vr,2):Q--)}}},Ge=(u,d,g,w,b=null)=>{const{el:A,type:R,transition:x,children:O,shapeFlag:E}=u;if(E&6){Ge(u.component.subTree,d,g,w);return}if(E&128){u.suspense.move(d,g,w);return}if(E&64){R.move(u,d,g,dt);return}if(R===ge){r(A,d,g);for(let F=0;Fx.enter(A),b);else{const{leave:F,delayLeave:j,afterLeave:U}=x,z=()=>r(A,d,g),ee=()=>{F(A,()=>{z(),U&&U()})};j?j(A,z,ee):ee()}else r(A,d,g)},Me=(u,d,g,w=!1,b=!1)=>{const{type:A,props:R,ref:x,children:O,dynamicChildren:E,shapeFlag:$,patchFlag:F,dirs:j}=u;if(x!=null&&An(x,null,g,u,!0),$&256){d.ctx.deactivate(u);return}const U=$&1&&j,z=!vt(u);let ee;if(z&&(ee=R&&R.onVnodeBeforeUnmount)&&Ce(ee,d,u),$&6)pi(u.component,g,w);else{if($&128){u.suspense.unmount(g,w);return}U&&Ie(u,null,d,"beforeUnmount"),$&64?u.type.remove(u,d,g,b,dt,w):E&&(A!==ge||F>0&&F&64)?je(E,d,g,!1,!0):(A===ge&&F&384||!b&&$&16)&&je(O,d,g),w&&Br(u)}(z&&(ee=R&&R.onVnodeUnmounted)||U)&&pe(()=>{ee&&Ce(ee,d,u),U&&Ie(u,null,d,"unmounted")},g)},Br=u=>{const{type:d,el:g,anchor:w,transition:b}=u;if(d===ge){hi(g,w);return}if(d===Ht){S(u);return}const A=()=>{s(g),b&&!b.persisted&&b.afterLeave&&b.afterLeave()};if(u.shapeFlag&1&&b&&!b.persisted){const{leave:R,delayLeave:x}=b,O=()=>R(g,A);x?x(u.el,A,O):O()}else A()},hi=(u,d)=>{let g;for(;u!==d;)g=p(u),s(u),u=g;s(d)},pi=(u,d,g)=>{const{bum:w,scope:b,update:A,subTree:R,um:x}=u;w&&gn(w),b.stop(),A&&(A.active=!1,Me(R,u,d,g)),x&&pe(x,d),pe(()=>{u.isUnmounted=!0},d),d&&d.pendingBranch&&!d.isUnmounted&&u.asyncDep&&!u.asyncResolved&&u.suspenseId===d.pendingId&&(d.deps--,d.deps===0&&d.resolve())},je=(u,d,g,w=!1,b=!1,A=0)=>{for(let R=A;Ru.shapeFlag&6?en(u.component.subTree):u.shapeFlag&128?u.suspense.next():p(u.anchor||u.el),Ur=(u,d,g)=>{u==null?d._vnode&&Me(d._vnode,null,null,!0):T(d._vnode||null,u,d,null,null,null,g),ts(),Cn(),d._vnode=u},dt={p:T,um:Me,m:Ge,r:Br,mt:N,mc:y,pc:G,pbc:I,n:en,o:e};let Xn,Qn;return t&&([Xn,Qn]=t(dt)),{render:Ur,hydrate:Xn,createApp:Yl(Ur,Xn)}}function et({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function $r(e,t,n=!1){const r=e.children,s=t.children;if(D(r)&&D(s))for(let o=0;o>1,e[n[l]]0&&(t[r]=n[o-1]),n[o]=r)}}for(o=n.length,i=n[o-1];o-- >0;)n[o]=i,i=t[i];return n}const oc=e=>e.__isTeleport,Nt=e=>e&&(e.disabled||e.disabled===""),hs=e=>typeof SVGElement<"u"&&e instanceof SVGElement,vr=(e,t)=>{const n=e&&e.to;return ne(n)?t?t(n):null:n},ic={__isTeleport:!0,process(e,t,n,r,s,o,i,l,c,a){const{mc:f,pc:h,pbc:p,o:{insert:v,querySelector:C,createText:T,createComment:L}}=a,m=Nt(t.props);let{shapeFlag:_,children:H,dynamicChildren:S}=t;if(e==null){const B=t.el=T(""),W=t.anchor=T("");v(B,n,r),v(W,n,r);const k=t.target=vr(t.props,C),y=t.targetAnchor=T("");k&&(v(y,k),i=i||hs(k));const P=(I,V)=>{_&16&&f(H,I,V,s,o,i,l,c)};m?P(n,W):k&&P(k,y)}else{t.el=e.el;const B=t.anchor=e.anchor,W=t.target=e.target,k=t.targetAnchor=e.targetAnchor,y=Nt(e.props),P=y?n:W,I=y?B:k;if(i=i||hs(W),S?(p(e.dynamicChildren,S,P,s,o,i,l),$r(e,t,!0)):c||h(e,t,P,I,s,o,i,l,!1),m)y||un(t,n,B,a,1);else if((t.props&&t.props.to)!==(e.props&&e.props.to)){const V=t.target=vr(t.props,C);V&&un(t,V,null,a,0)}else y&&un(t,W,k,a,1)}jo(t)},remove(e,t,n,r,{um:s,o:{remove:o}},i){const{shapeFlag:l,children:c,anchor:a,targetAnchor:f,target:h,props:p}=e;if(h&&o(f),(i||!Nt(p))&&(o(a),l&16))for(let v=0;v0?Oe||gt:null,cc(),Vt>0&&Oe&&Oe.push(e),e}function su(e,t,n,r,s,o){return ko(Ko(e,t,n,r,s,o,!0))}function Bo(e,t,n,r,s){return ko(se(e,t,n,r,s,!0))}function Sn(e){return e?e.__v_isVNode===!0:!1}function st(e,t){return e.type===t.type&&e.key===t.key}const zn="__vInternal",Uo=({key:e})=>e??null,mn=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?ne(e)||ue(e)||K(e)?{i:fe,r:e,k:t,f:!!n}:e:null);function Ko(e,t=null,n=null,r=0,s=null,o=e===ge?0:1,i=!1,l=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Uo(t),ref:t&&mn(t),scopeId:Un,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:r,dynamicProps:s,dynamicChildren:null,appContext:null,ctx:fe};return l?(jr(c,n),o&128&&e.normalize(c)):n&&(c.shapeFlag|=ne(n)?8:16),Vt>0&&!i&&Oe&&(c.patchFlag>0||o&6)&&c.patchFlag!==32&&Oe.push(c),c}const se=ac;function ac(e,t=null,n=null,r=0,s=null,o=!1){if((!e||e===So)&&(e=be),Sn(e)){const l=Qe(e,t,!0);return n&&jr(l,n),Vt>0&&!o&&Oe&&(l.shapeFlag&6?Oe[Oe.indexOf(e)]=l:Oe.push(l)),l.patchFlag|=-2,l}if(vc(e)&&(e=e.__vccOpts),t){t=uc(t);let{class:l,style:c}=t;l&&!ne(l)&&(t.class=Fn(l)),Z(c)&&(ao(c)&&!D(c)&&(c=oe({},c)),t.style=Mn(c))}const i=ne(e)?1:Al(e)?128:oc(e)?64:Z(e)?4:K(e)?2:0;return Ko(e,t,n,r,s,i,o,!0)}function uc(e){return e?ao(e)||zn in e?oe({},e):e:null}function Qe(e,t,n=!1){const{props:r,ref:s,patchFlag:o,children:i}=e,l=t?fc(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&Uo(l),ref:t&&t.ref?n&&s?D(s)?s.concat(mn(t)):[s,mn(t)]:mn(t):s,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==ge?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Qe(e.ssContent),ssFallback:e.ssFallback&&Qe(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function Wo(e=" ",t=0){return se(Ct,null,e,t)}function ou(e,t){const n=se(Ht,null,e);return n.staticCount=t,n}function iu(e="",t=!1){return t?(Do(),Bo(be,null,e)):se(be,null,e)}function Ae(e){return e==null||typeof e=="boolean"?se(be):D(e)?se(ge,null,e.slice()):typeof e=="object"?qe(e):se(Ct,null,String(e))}function qe(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Qe(e)}function jr(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(D(t))n=16;else if(typeof t=="object")if(r&65){const s=t.default;s&&(s._c&&(s._d=!1),jr(e,s()),s._c&&(s._d=!0));return}else{n=32;const s=t._;!s&&!(zn in t)?t._ctx=fe:s===3&&fe&&(fe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else K(t)?(t={default:t,_ctx:fe},n=32):(t=String(t),r&64?(n=16,t=[Wo(t)]):n=8);e.children=t,e.shapeFlag|=n}function fc(...e){const t={};for(let n=0;nae||fe;let Dr,pt,gs="__VUE_INSTANCE_SETTERS__";(pt=cr()[gs])||(pt=cr()[gs]=[]),pt.push(e=>ae=e),Dr=e=>{pt.length>1?pt.forEach(t=>t(e)):pt[0](e)};const Et=e=>{Dr(e),e.scope.on()},ut=()=>{ae&&ae.scope.off(),Dr(null)};function Vo(e){return e.vnode.shapeFlag&4}let xt=!1;function gc(e,t=!1){xt=t;const{props:n,children:r}=e.vnode,s=Vo(e);Xl(e,n,s,t),Gl(e,r);const o=s?mc(e,t):void 0;return xt=!1,o}function mc(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=It(new Proxy(e.ctx,kl));const{setup:r}=n;if(r){const s=e.setupContext=r.length>1?zo(e):null;Et(e),Tt();const o=Ye(r,e,0,[e.props,s]);if(At(),ut(),Ws(o)){if(o.then(ut,ut),t)return o.then(i=>{ms(e,i,t)}).catch(i=>{Xt(i,e,0)});e.asyncDep=o}else ms(e,o,t)}else qo(e,t)}function ms(e,t,n){K(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Z(t)&&(e.setupState=ho(t)),qo(e,n)}let ys;function qo(e,t,n){const r=e.type;if(!e.render){if(!t&&ys&&!r.render){const s=r.template||Nr(e).template;if(s){const{isCustomElement:o,compilerOptions:i}=e.appContext.config,{delimiters:l,compilerOptions:c}=r,a=oe(oe({isCustomElement:o,delimiters:l},i),c);r.render=ys(s,a)}}e.render=r.render||Re}Et(e),Tt(),Ul(e),At(),ut()}function yc(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return me(e,"get","$attrs"),t[n]}}))}function zo(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return yc(e)},slots:e.slots,emit:e.emit,expose:t}}function Yn(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(ho(It(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Lt)return Lt[n](e)},has(t,n){return n in t||n in Lt}}))}function _c(e,t=!0){return K(e)?e.displayName||e.name:e.name||t&&e.__name}function vc(e){return K(e)&&"__vccOpts"in e}const ce=(e,t)=>pl(e,t,xt);function br(e,t,n){const r=arguments.length;return r===2?Z(t)&&!D(t)?Sn(t)?se(e,null,[t]):se(e,t):se(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Sn(n)&&(n=[n]),se(e,t,n))}const bc=Symbol.for("v-scx"),wc=()=>bt(bc),Cc="3.3.4",Ec="http://www.w3.org/2000/svg",ot=typeof document<"u"?document:null,_s=ot&&ot.createElement("template"),xc={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const s=t?ot.createElementNS(Ec,e):ot.createElement(e,n?{is:n}:void 0);return e==="select"&&r&&r.multiple!=null&&s.setAttribute("multiple",r.multiple),s},createText:e=>ot.createTextNode(e),createComment:e=>ot.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>ot.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,s,o){const i=n?n.previousSibling:t.lastChild;if(s&&(s===o||s.nextSibling))for(;t.insertBefore(s.cloneNode(!0),n),!(s===o||!(s=s.nextSibling)););else{_s.innerHTML=r?`${e}`:e;const l=_s.content;if(r){const c=l.firstChild;for(;c.firstChild;)l.appendChild(c.firstChild);l.removeChild(c)}t.insertBefore(l,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};function Tc(e,t,n){const r=e._vtc;r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}function Ac(e,t,n){const r=e.style,s=ne(n);if(n&&!s){if(t&&!ne(t))for(const o in t)n[o]==null&&wr(r,o,"");for(const o in n)wr(r,o,n[o])}else{const o=r.display;s?t!==n&&(r.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(r.display=o)}}const vs=/\s*!important$/;function wr(e,t,n){if(D(n))n.forEach(r=>wr(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=Sc(e,t);vs.test(n)?e.setProperty(ft(r),n.replace(vs,""),"important"):e[r]=n}}const bs=["Webkit","Moz","ms"],nr={};function Sc(e,t){const n=nr[t];if(n)return n;let r=He(t);if(r!=="filter"&&r in e)return nr[t]=r;r=Pn(r);for(let s=0;srr||(Ic.then(()=>rr=0),rr=Date.now());function Nc(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;xe(Hc(r,n.value),t,5,[r])};return n.value=e,n.attached=Lc(),n}function Hc(e,t){if(D(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>s=>!s._stopped&&r&&r(s))}else return t}const Es=/^on[a-z]/,$c=(e,t,n,r,s=!1,o,i,l,c)=>{t==="class"?Tc(e,r,s):t==="style"?Ac(e,n,r):zt(t)?Er(t)||Mc(e,t,n,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):jc(e,t,r,s))?Rc(e,t,r,o,i,l,c):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),Oc(e,t,r,s))};function jc(e,t,n,r){return r?!!(t==="innerHTML"||t==="textContent"||t in e&&Es.test(t)&&K(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Es.test(t)&&ne(n)?!1:t in e}const We="transition",Rt="animation",Yo=(e,{slots:t})=>br(Pl,Dc(e),t);Yo.displayName="Transition";const Jo={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};Yo.props=oe({},wo,Jo);const tt=(e,t=[])=>{D(e)?e.forEach(n=>n(...t)):e&&e(...t)},xs=e=>e?D(e)?e.some(t=>t.length>1):e.length>1:!1;function Dc(e){const t={};for(const M in e)M in Jo||(t[M]=e[M]);if(e.css===!1)return t;const{name:n="v",type:r,duration:s,enterFromClass:o=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:c=o,appearActiveClass:a=i,appearToClass:f=l,leaveFromClass:h=`${n}-leave-from`,leaveActiveClass:p=`${n}-leave-active`,leaveToClass:v=`${n}-leave-to`}=e,C=kc(s),T=C&&C[0],L=C&&C[1],{onBeforeEnter:m,onEnter:_,onEnterCancelled:H,onLeave:S,onLeaveCancelled:B,onBeforeAppear:W=m,onAppear:k=_,onAppearCancelled:y=H}=t,P=(M,q,N)=>{nt(M,q?f:l),nt(M,q?a:i),N&&N()},I=(M,q)=>{M._isLeaving=!1,nt(M,h),nt(M,v),nt(M,p),q&&q()},V=M=>(q,N)=>{const ye=M?k:_,X=()=>P(q,M,N);tt(ye,[q,X]),Ts(()=>{nt(q,M?c:o),Ve(q,M?f:l),xs(ye)||As(q,r,T,X)})};return oe(t,{onBeforeEnter(M){tt(m,[M]),Ve(M,o),Ve(M,i)},onBeforeAppear(M){tt(W,[M]),Ve(M,c),Ve(M,a)},onEnter:V(!1),onAppear:V(!0),onLeave(M,q){M._isLeaving=!0;const N=()=>I(M,q);Ve(M,h),Kc(),Ve(M,p),Ts(()=>{M._isLeaving&&(nt(M,h),Ve(M,v),xs(S)||As(M,r,L,N))}),tt(S,[M,N])},onEnterCancelled(M){P(M,!1),tt(H,[M])},onAppearCancelled(M){P(M,!0),tt(y,[M])},onLeaveCancelled(M){I(M),tt(B,[M])}})}function kc(e){if(e==null)return null;if(Z(e))return[sr(e.enter),sr(e.leave)];{const t=sr(e);return[t,t]}}function sr(e){return wi(e)}function Ve(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e._vtc||(e._vtc=new Set)).add(t)}function nt(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function Ts(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let Bc=0;function As(e,t,n,r){const s=e._endId=++Bc,o=()=>{s===e._endId&&r()};if(n)return setTimeout(o,n);const{type:i,timeout:l,propCount:c}=Uc(e,t);if(!i)return r();const a=i+"end";let f=0;const h=()=>{e.removeEventListener(a,p),o()},p=v=>{v.target===e&&++f>=c&&h()};setTimeout(()=>{f(n[C]||"").split(", "),s=r(`${We}Delay`),o=r(`${We}Duration`),i=Ss(s,o),l=r(`${Rt}Delay`),c=r(`${Rt}Duration`),a=Ss(l,c);let f=null,h=0,p=0;t===We?i>0&&(f=We,h=i,p=o.length):t===Rt?a>0&&(f=Rt,h=a,p=c.length):(h=Math.max(i,a),f=h>0?i>a?We:Rt:null,p=f?f===We?o.length:c.length:0);const v=f===We&&/\b(transform|all)(,|$)/.test(r(`${We}Property`).toString());return{type:f,timeout:h,propCount:p,hasTransform:v}}function Ss(e,t){for(;e.lengthOs(n)+Os(e[r])))}function Os(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function Kc(){return document.body.offsetHeight}const On=e=>{const t=e.props["onUpdate:modelValue"]||!1;return D(t)?n=>gn(t,n):t};function Wc(e){e.target.composing=!0}function Rs(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const lu={created(e,{modifiers:{lazy:t,trim:n,number:r}},s){e._assign=On(s);const o=r||s.props&&s.props.type==="number";it(e,t?"change":"input",i=>{if(i.target.composing)return;let l=e.value;n&&(l=l.trim()),o&&(l=lr(l)),e._assign(l)}),n&&it(e,"change",()=>{e.value=e.value.trim()}),t||(it(e,"compositionstart",Wc),it(e,"compositionend",Rs),it(e,"change",Rs))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,modifiers:{lazy:n,trim:r,number:s}},o){if(e._assign=On(o),e.composing||document.activeElement===e&&e.type!=="range"&&(n||r&&e.value.trim()===t||(s||e.type==="number")&&lr(e.value)===t))return;const i=t??"";e.value!==i&&(e.value=i)}},cu={created(e,{value:t},n){e.checked=vn(t,n.props.value),e._assign=On(n),it(e,"change",()=>{e._assign(Vc(e))})},beforeUpdate(e,{value:t,oldValue:n},r){e._assign=On(r),t!==n&&(e.checked=vn(t,r.props.value))}};function Vc(e){return"_value"in e?e._value:e.value}const qc=["ctrl","shift","alt","meta"],zc={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>qc.some(n=>e[`${n}Key`]&&!t.includes(n))},au=(e,t)=>(n,...r)=>{for(let s=0;sn=>{if(!("key"in n))return;const r=ft(n.key);if(t.some(s=>s===r||Yc[s]===r))return e(n)},Xo=oe({patchProp:$c},xc);let jt,Ps=!1;function Jc(){return jt||(jt=nc(Xo))}function Xc(){return jt=Ps?jt:rc(Xo),Ps=!0,jt}const fu=(...e)=>{const t=Jc().createApp(...e),{mount:n}=t;return t.mount=r=>{const s=Qo(r);if(!s)return;const o=t._component;!K(o)&&!o.render&&!o.template&&(o.template=s.innerHTML),s.innerHTML="";const i=n(s,!1,s instanceof SVGElement);return s instanceof Element&&(s.removeAttribute("v-cloak"),s.setAttribute("data-v-app","")),i},t},du=(...e)=>{const t=Xc().createApp(...e),{mount:n}=t;return t.mount=r=>{const s=Qo(r);if(s)return n(s,!0,s instanceof SVGElement)},t};function Qo(e){return ne(e)?document.querySelector(e):e}const hu=(e,t)=>{const n=e.__vccOpts||e;for(const[r,s]of t)n[r]=s;return n},Qc=window.__VP_SITE_DATA__;function pu(e,t){var n;const r=jn();return Kn(()=>{r.value=e()},{...t,flush:(n=t==null?void 0:t.flush)!=null?n:"sync"}),Jt(r)}function Jn(e){return Xs()?(Pi(e),!0):!1}function Pe(e){return typeof e=="function"?e():fo(e)}const Zo=typeof window<"u"&&typeof document<"u",Zc=e=>e!=null,Gc=Object.prototype.toString,ea=e=>Gc.call(e)==="[object Object]",Je=()=>{},Ms=ta();function ta(){var e;return Zo&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&/iP(ad|hone|od)/.test(window.navigator.userAgent)}function na(e,t){function n(...r){return new Promise((s,o)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(s).catch(o)})}return n}const Go=e=>e();function ra(e,t={}){let n,r,s=Je;const o=l=>{clearTimeout(l),s(),s=Je};return l=>{const c=Pe(e),a=Pe(t.maxWait);return n&&o(n),c<=0||a!==void 0&&a<=0?(r&&(o(r),r=null),Promise.resolve(l())):new Promise((f,h)=>{s=t.rejectOnCancel?h:f,a&&!r&&(r=setTimeout(()=>{n&&o(n),r=null,f(l())},a)),n=setTimeout(()=>{r&&o(r),r=null,f(l())},c)})}}function sa(e=Go){const t=le(!0);function n(){t.value=!1}function r(){t.value=!0}const s=(...o)=>{t.value&&e(...o)};return{isActive:Jt(t),pause:n,resume:r,eventFilter:s}}function ei(...e){if(e.length!==1)return fl(...e);const t=e[0];return typeof t=="function"?Jt(po(()=>({get:t,set:Je}))):le(t)}function ti(e,t,n={}){const{eventFilter:r=Go,...s}=n;return Ne(e,na(r,t),s)}function oa(e,t,n={}){const{eventFilter:r,...s}=n,{eventFilter:o,pause:i,resume:l,isActive:c}=sa(r);return{stop:ti(e,t,{...s,eventFilter:o}),pause:i,resume:l,isActive:c}}function ni(e,t=!0){Zt()?St(e):t?e():Dn(e)}function gu(e,t,n={}){const{debounce:r=0,maxWait:s=void 0,...o}=n;return ti(e,t,{...o,eventFilter:ra(r,{maxWait:s})})}function mu(e,t,n){return Ne(e,(r,s,o)=>{r&&t(r,s,o)},n)}function yu(e,t,n){let r;ue(n)?r={evaluating:n}:r=n||{};const{lazy:s=!1,evaluating:o=void 0,shallow:i=!0,onError:l=Je}=r,c=le(!s),a=i?jn(t):le(t);let f=0;return Kn(async h=>{if(!c.value)return;f++;const p=f;let v=!1;o&&Promise.resolve().then(()=>{o.value=!0});try{const C=await e(T=>{h(()=>{o&&(o.value=!1),v||T()})});p===f&&(a.value=C)}catch(C){l(C)}finally{o&&p===f&&(o.value=!1),v=!0}}),s?ce(()=>(c.value=!0,a.value)):a}function Dt(e){var t;const n=Pe(e);return(t=n==null?void 0:n.$el)!=null?t:n}const $e=Zo?window:void 0;function qt(...e){let t,n,r,s;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,r,s]=e,t=$e):[t,n,r,s]=e,!t)return Je;Array.isArray(n)||(n=[n]),Array.isArray(r)||(r=[r]);const o=[],i=()=>{o.forEach(f=>f()),o.length=0},l=(f,h,p,v)=>(f.addEventListener(h,p,v),()=>f.removeEventListener(h,p,v)),c=Ne(()=>[Dt(t),Pe(s)],([f,h])=>{if(i(),!f)return;const p=ea(h)?{...h}:h;o.push(...n.flatMap(v=>r.map(C=>l(f,v,C,p))))},{immediate:!0,flush:"post"}),a=()=>{c(),i()};return Jn(a),a}function ia(e){return typeof e=="function"?e:typeof e=="string"?t=>t.key===e:Array.isArray(e)?t=>e.includes(t.key):()=>!0}function _u(...e){let t,n,r={};e.length===3?(t=e[0],n=e[1],r=e[2]):e.length===2?typeof e[1]=="object"?(t=!0,n=e[0],r=e[1]):(t=e[0],n=e[1]):(t=!0,n=e[0]);const{target:s=$e,eventName:o="keydown",passive:i=!1,dedupe:l=!1}=r,c=ia(t);return qt(s,o,f=>{f.repeat&&Pe(l)||c(f)&&n(f)},i)}function vu(e,t=null){const n=Zt();let r=()=>{};const s=po((o,i)=>(r=i,{get(){var l,c;return o(),(c=(l=n==null?void 0:n.proxy)==null?void 0:l.$refs[e])!=null?c:t},set(){}}));return ni(r),To(r),s}function la(){const e=le(!1);return Zt()&&St(()=>{e.value=!0}),e}function ri(e){const t=la();return ce(()=>(t.value,!!e()))}function ca(e,t={}){const{window:n=$e}=t,r=ri(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let s;const o=le(!1),i=a=>{o.value=a.matches},l=()=>{s&&("removeEventListener"in s?s.removeEventListener("change",i):s.removeListener(i))},c=Kn(()=>{r.value&&(l(),s=n.matchMedia(Pe(e)),"addEventListener"in s?s.addEventListener("change",i):s.addListener(i),o.value=s.matches)});return Jn(()=>{c(),l(),s=void 0}),o}const fn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},dn="__vueuse_ssr_handlers__",aa=ua();function ua(){return dn in fn||(fn[dn]=fn[dn]||{}),fn[dn]}function si(e,t){return aa[e]||t}function fa(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const da={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},Fs="vueuse-storage";function kr(e,t,n,r={}){var s;const{flush:o="pre",deep:i=!0,listenToStorageChanges:l=!0,writeDefaults:c=!0,mergeDefaults:a=!1,shallow:f,window:h=$e,eventFilter:p,onError:v=y=>{console.error(y)}}=r,C=(f?jn:le)(t);if(!n)try{n=si("getDefaultStorage",()=>{var y;return(y=$e)==null?void 0:y.localStorage})()}catch(y){v(y)}if(!n)return C;const T=Pe(t),L=fa(T),m=(s=r.serializer)!=null?s:da[L],{pause:_,resume:H}=oa(C,()=>S(C.value),{flush:o,deep:i,eventFilter:p});return h&&l&&(qt(h,"storage",k),qt(h,Fs,W)),k(),C;function S(y){try{if(y==null)n.removeItem(e);else{const P=m.write(y),I=n.getItem(e);I!==P&&(n.setItem(e,P),h&&h.dispatchEvent(new CustomEvent(Fs,{detail:{key:e,oldValue:I,newValue:P,storageArea:n}})))}}catch(P){v(P)}}function B(y){const P=y?y.newValue:n.getItem(e);if(P==null)return c&&T!==null&&n.setItem(e,m.write(T)),T;if(!y&&a){const I=m.read(P);return typeof a=="function"?a(I,T):L==="object"&&!Array.isArray(I)?{...T,...I}:I}else return typeof P!="string"?P:m.read(P)}function W(y){k(y.detail)}function k(y){if(!(y&&y.storageArea!==n)){if(y&&y.key==null){C.value=T;return}if(!(y&&y.key!==e)){_();try{(y==null?void 0:y.newValue)!==m.write(C.value)&&(C.value=B(y))}catch(P){v(P)}finally{y?Dn(H):H()}}}}}function ha(e){return ca("(prefers-color-scheme: dark)",e)}function pa(e={}){const{selector:t="html",attribute:n="class",initialValue:r="auto",window:s=$e,storage:o,storageKey:i="vueuse-color-scheme",listenToStorageChanges:l=!0,storageRef:c,emitAuto:a,disableTransition:f=!0}=e,h={auto:"",light:"light",dark:"dark",...e.modes||{}},p=ha({window:s}),v=ce(()=>p.value?"dark":"light"),C=c||(i==null?ei(r):kr(i,r,o,{window:s,listenToStorageChanges:l})),T=ce(()=>C.value==="auto"?v.value:C.value),L=si("updateHTMLAttrs",(S,B,W)=>{const k=typeof S=="string"?s==null?void 0:s.document.querySelector(S):Dt(S);if(!k)return;let y;if(f){y=s.document.createElement("style");const P="*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}";y.appendChild(document.createTextNode(P)),s.document.head.appendChild(y)}if(B==="class"){const P=W.split(/\s/g);Object.values(h).flatMap(I=>(I||"").split(/\s/g)).filter(Boolean).forEach(I=>{P.includes(I)?k.classList.add(I):k.classList.remove(I)})}else k.setAttribute(B,W);f&&(s.getComputedStyle(y).opacity,document.head.removeChild(y))});function m(S){var B;L(t,n,(B=h[S])!=null?B:S)}function _(S){e.onChanged?e.onChanged(S,m):m(S)}Ne(T,_,{flush:"post",immediate:!0}),ni(()=>_(T.value));const H=ce({get(){return a?C.value:T.value},set(S){C.value=S}});try{return Object.assign(H,{store:C,system:v,state:T})}catch{return H}}function ga(e={}){const{valueDark:t="dark",valueLight:n=""}=e,r=pa({...e,onChanged:(o,i)=>{var l;e.onChanged?(l=e.onChanged)==null||l.call(e,o==="dark",i,o):i(o)},modes:{dark:t,light:n}});return ce({get(){return r.value==="dark"},set(o){const i=o?"dark":"light";r.system.value===i?r.value="auto":r.value=i}})}function bu(e,t,n={}){const{root:r,rootMargin:s="0px",threshold:o=.1,window:i=$e,immediate:l=!0}=n,c=ri(()=>i&&"IntersectionObserver"in i),a=ce(()=>{const C=Pe(e);return(Array.isArray(C)?C:[C]).map(Dt).filter(Zc)});let f=Je;const h=le(l),p=c.value?Ne(()=>[a.value,Dt(r),h.value],([C,T])=>{if(f(),!h.value||!C.length)return;const L=new IntersectionObserver(t,{root:Dt(T),rootMargin:s,threshold:o});C.forEach(m=>m&&L.observe(m)),f=()=>{L.disconnect(),f=Je}},{immediate:l,flush:"post"}):Je,v=()=>{f(),p(),h.value=!1};return Jn(v),{isSupported:c,isActive:h,pause(){f(),h.value=!1},resume(){h.value=!0},stop:v}}function or(e){return typeof Window<"u"&&e instanceof Window?e.document.documentElement:typeof Document<"u"&&e instanceof Document?e.documentElement:e}function wu(e,t,n={}){const{window:r=$e}=n;return kr(e,t,r==null?void 0:r.localStorage,n)}function oi(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}function Cu(e,t=!1){const n=le(t);let r=null,s;Ne(ei(e),l=>{const c=or(Pe(l));if(c){const a=c;s=a.style.overflow,n.value&&(a.style.overflow="hidden")}},{immediate:!0});const o=()=>{const l=or(Pe(e));!l||n.value||(Ms&&(r=qt(l,"touchmove",c=>{ma(c)},{passive:!1})),l.style.overflow="hidden",n.value=!0)},i=()=>{const l=or(Pe(e));!l||!n.value||(Ms&&(r==null||r()),l.style.overflow=s,n.value=!1)};return Jn(i),ce({get(){return n.value},set(l){l?o():i()}})}function Eu(e,t,n={}){const{window:r=$e}=n;return kr(e,t,r==null?void 0:r.sessionStorage,n)}function xu({window:e=$e}={}){if(!e)return{x:le(0),y:le(0)};const t=le(e.scrollX),n=le(e.scrollY);return qt(e,"scroll",()=>{t.value=e.scrollX,n.value=e.scrollY},{capture:!1,passive:!0}),{x:t,y:n}}const ii=/^[a-z]+:/i,ya="vitepress-theme-appearance",li=/#.*$/,_a=/(index)?\.(md|html)$/,Ee=typeof document<"u",ci={relativePath:"",filePath:"",title:"404",description:"Not Found",headers:[],frontmatter:{sidebar:!1,layout:"page"},lastUpdated:0,isNotFound:!0};function va(e,t,n=!1){if(t===void 0)return!1;if(e=Is(`/${e}`),n)return new RegExp(t).test(e);if(Is(t)!==e)return!1;const r=t.match(li);return r?(Ee?location.hash:"")===r[0]:!0}function Is(e){return decodeURI(e).replace(li,"").replace(_a,"")}function ba(e){return ii.test(e)}function wa(e,t){var r,s,o,i,l,c,a;const n=Object.keys(e.locales).find(f=>f!=="root"&&!ba(f)&&va(t,`/${f}/`,!0))||"root";return Object.assign({},e,{localeIndex:n,lang:((r=e.locales[n])==null?void 0:r.lang)??e.lang,dir:((s=e.locales[n])==null?void 0:s.dir)??e.dir,title:((o=e.locales[n])==null?void 0:o.title)??e.title,titleTemplate:((i=e.locales[n])==null?void 0:i.titleTemplate)??e.titleTemplate,description:((l=e.locales[n])==null?void 0:l.description)??e.description,head:ui(e.head,((c=e.locales[n])==null?void 0:c.head)??[]),themeConfig:{...e.themeConfig,...(a=e.locales[n])==null?void 0:a.themeConfig}})}function ai(e,t){const n=t.title||e.title,r=t.titleTemplate??e.titleTemplate;if(typeof r=="string"&&r.includes(":title"))return r.replace(/:title/g,n);const s=Ca(e.title,r);return`${n}${s}`}function Ca(e,t){return t===!1?"":t===!0||t===void 0?` | ${e}`:e===t?"":` | ${t}`}function Ea(e,t){const[n,r]=t;if(n!=="meta")return!1;const s=Object.entries(r)[0];return s==null?!1:e.some(([o,i])=>o===n&&i[s[0]]===s[1])}function ui(e,t){return[...e.filter(n=>!Ea(t,n)),...t]}const xa=/[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g,Ta=/^[a-z]:/i;function Ls(e){const t=Ta.exec(e),n=t?t[0]:"";return n+e.slice(n.length).replace(xa,"_").replace(/(^|\/)_+(?=[^/]*$)/,"$1")}const Aa=Symbol(),ct=jn(Qc);function Tu(e){const t=ce(()=>wa(ct.value,e.data.relativePath)),n=t.value.appearance,r=n==="force-dark"?le(!0):n?ga({storageKey:ya,initialValue:()=>typeof n=="string"?n:"auto",...typeof n=="object"?n:{}}):le(!1);return{site:t,theme:ce(()=>t.value.themeConfig),page:ce(()=>e.data),frontmatter:ce(()=>e.data.frontmatter),params:ce(()=>e.data.params),lang:ce(()=>t.value.lang),dir:ce(()=>t.value.dir),localeIndex:ce(()=>t.value.localeIndex||"root"),title:ce(()=>ai(t.value,e.data)),description:ce(()=>e.data.description||t.value.description),isDark:r}}function Sa(){const e=bt(Aa);if(!e)throw new Error("vitepress data not properly injected in app");return e}function Oa(e,t){return`${e}${t}`.replace(/\/+/g,"/")}function Ns(e){return ii.test(e)||!e.startsWith("/")?e:Oa(ct.value.base,e)}function Ra(e){let t=e.replace(/\.html$/,"");if(t=decodeURIComponent(t),t=t.replace(/\/$/,"/index"),Ee){const n="/iroha-2-docs/";t=Ls(t.slice(n.length).replace(/\//g,"_")||"index")+".md";let r=__VP_HASH_MAP__[t.toLowerCase()];if(r||(t=t.endsWith("_index.md")?t.slice(0,-9)+".md":t.slice(0,-3)+"_index.md",r=__VP_HASH_MAP__[t.toLowerCase()]),!r)return null;t=`${n}assets/${t}.${r}.js`}else t=`./${Ls(t.slice(1).replace(/\//g,"_"))}.md.js`;return t}let yn=[];function Au(e){yn.push(e),qn(()=>{yn=yn.filter(t=>t!==e)})}const Pa=Symbol(),Hs="http://a.com",Ma=()=>({path:"/",component:null,data:ci});function Su(e,t){const n=Hn(Ma()),r={route:n,go:s};async function s(l=Ee?location.href:"/"){var a,f;if(await((a=r.onBeforeRouteChange)==null?void 0:a.call(r,l))===!1)return;const c=new URL(l,Hs);ct.value.cleanUrls||!c.pathname.endsWith("/")&&!c.pathname.endsWith(".html")&&(c.pathname+=".html",l=c.pathname+c.search+c.hash),Ds(l),await i(l),await((f=r.onAfterRouteChanged)==null?void 0:f.call(r,l))}let o=null;async function i(l,c=0,a=!1){var p;if(await((p=r.onBeforePageLoad)==null?void 0:p.call(r,l))===!1)return;const f=new URL(l,Hs),h=o=f.pathname;try{let v=await e(h);if(!v)throw new Error(`Page not found: ${h}`);if(o===h){o=null;const{default:C,__pageData:T}=v;if(!C)throw new Error(`Invalid route component: ${C}`);n.path=Ee?h:Ns(h),n.component=It(C),n.data=It(T),Ee&&Dn(()=>{let L=ct.value.base+T.relativePath.replace(/(?:(^|\/)index)?\.md$/,"$1");if(!ct.value.cleanUrls&&!L.endsWith("/")&&(L+=".html"),L!==f.pathname&&(f.pathname=L,l=L+f.search+f.hash,history.replaceState(null,"",l)),f.hash&&!c){let m=null;try{m=document.getElementById(decodeURIComponent(f.hash).slice(1))}catch(_){console.warn(_)}if(m){$s(m,f.hash);return}}window.scrollTo(0,c)})}}catch(v){if(!/fetch|Page not found/.test(v.message)&&!/^\/404(\.html|\/)?$/.test(l)&&console.error(v),!a)try{const C=await fetch(ct.value.base+"hashmap.json");window.__VP_HASH_MAP__=await C.json(),await i(l,c,!0);return}catch{}o===h&&(o=null,n.path=Ee?h:Ns(h),n.component=t?It(t):null,n.data=ci)}}return Ee&&(window.addEventListener("click",l=>{if(l.target.closest("button"))return;const a=l.target.closest("a");if(a&&!a.closest(".vp-raw")&&(a instanceof SVGElement||!a.download)){const{target:f}=a,{href:h,origin:p,pathname:v,hash:C,search:T}=new URL(a.href instanceof SVGAnimatedString?a.href.animVal:a.href,a.baseURI),L=window.location,m=v.match(/\.\w+$/);!l.ctrlKey&&!l.shiftKey&&!l.altKey&&!l.metaKey&&!f&&p===L.origin&&!(m&&m[0]!==".html")&&(l.preventDefault(),v===L.pathname&&T===L.search?(C!==L.hash&&(history.pushState(null,"",C),window.dispatchEvent(new Event("hashchange"))),C?$s(a,C,a.classList.contains("header-anchor")):(Ds(h),window.scrollTo(0,0))):s(h))}},{capture:!0}),window.addEventListener("popstate",l=>{i(location.href,l.state&&l.state.scrollPosition||0)}),window.addEventListener("hashchange",l=>{l.preventDefault()})),r}function Fa(){const e=bt(Pa);if(!e)throw new Error("useRouter() is called without provider.");return e}function fi(){return Fa().route}function $s(e,t,n=!1){let r=null;try{r=e.classList.contains("header-anchor")?e:document.getElementById(decodeURIComponent(t).slice(1))}catch(s){console.warn(s)}if(r){let a=function(){!n||Math.abs(c-window.scrollY)>window.innerHeight?window.scrollTo(0,c):window.scrollTo({left:0,top:c,behavior:"smooth"})},s=ct.value.scrollOffset,o=0,i=24;if(typeof s=="object"&&"padding"in s&&(i=s.padding,s=s.selector),typeof s=="number")o=s;else if(typeof s=="string")o=js(s,i);else if(Array.isArray(s))for(const f of s){const h=js(f,i);if(h){o=h;break}}const l=parseInt(window.getComputedStyle(r).paddingTop,10),c=window.scrollY+r.getBoundingClientRect().top-o+l;requestAnimationFrame(a)}}function js(e,t){const n=document.querySelector(e);if(!n)return 0;const r=n.getBoundingClientRect().bottom;return r<0?0:r+t}function Ds(e){Ee&&e!==location.href&&(history.replaceState({scrollPosition:window.scrollY},document.title),history.pushState(null,"",e))}const ks=()=>yn.forEach(e=>e()),Ou=Ir({name:"VitePressContent",props:{as:{type:[Object,String],default:"div"}},setup(e){const t=fi(),{site:n}=Sa();return()=>br(e.as,n.value.contentProps??{style:{position:"relative"}},[t.component?br(t.component,{onVnodeMounted:ks,onVnodeUpdated:ks}):"404 Page Not Found"])}}),Ia="modulepreload",La=function(e){return"/iroha-2-docs/"+e},Bs={},Ru=function(t,n,r){if(!n||n.length===0)return t();const s=document.getElementsByTagName("link");return Promise.all(n.map(o=>{if(o=La(o),o in Bs)return;Bs[o]=!0;const i=o.endsWith(".css"),l=i?'[rel="stylesheet"]':"";if(!!r)for(let f=s.length-1;f>=0;f--){const h=s[f];if(h.href===o&&(!i||h.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${o}"]${l}`))return;const a=document.createElement("link");if(a.rel=i?"stylesheet":Ia,i||(a.as="script",a.crossOrigin=""),a.href=o,document.head.appendChild(a),i)return new Promise((f,h)=>{a.addEventListener("load",f),a.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${o}`)))})})).then(()=>t()).catch(o=>{const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=o,window.dispatchEvent(i),!i.defaultPrevented)throw o})},Pu=Ir({setup(e,{slots:t}){const n=le(!1);return St(()=>{n.value=!0}),()=>n.value&&t.default?t.default():null}});function Mu(){Ee&&window.addEventListener("click",e=>{var n;const t=e.target;if(t.matches(".vp-code-group input")){const r=(n=t.parentElement)==null?void 0:n.parentElement;if(!r)return;const s=Array.from(r.querySelectorAll("input")).indexOf(t);if(s<0)return;const o=r.querySelector(".blocks");if(!o)return;const i=Array.from(o.children).find(a=>a.classList.contains("active"));if(!i)return;const l=o.children[s];if(!l||i===l)return;i.classList.remove("active"),l.classList.add("active");const c=r==null?void 0:r.querySelector(`label[for="${t.id}"]`);c==null||c.scrollIntoView({block:"nearest"})}})}function Fu(){if(Ee){const e=new WeakMap;window.addEventListener("click",t=>{var r;const n=t.target;if(n.matches('div[class*="language-"] > button.copy')){const s=n.parentElement,o=(r=n.nextElementSibling)==null?void 0:r.nextElementSibling;if(!s||!o)return;const i=/language-(shellscript|shell|bash|sh|zsh)/.test(s.className);let l="";o.querySelectorAll("span.line:not(.diff.remove)").forEach(c=>l+=(c.textContent||"")+` +`),l=l.slice(0,-1),i&&(l=l.replace(/^ *(\$|>) /gm,"").trim()),Na(l).then(()=>{n.classList.add("copied"),clearTimeout(e.get(n));const c=setTimeout(()=>{n.classList.remove("copied"),n.blur(),e.delete(n)},2e3);e.set(n,c)})}})}}async function Na(e){try{return navigator.clipboard.writeText(e)}catch{const t=document.createElement("textarea"),n=document.activeElement;t.value=e,t.setAttribute("readonly",""),t.style.contain="strict",t.style.position="absolute",t.style.left="-9999px",t.style.fontSize="12pt";const r=document.getSelection(),s=r?r.rangeCount>0&&r.getRangeAt(0):null;document.body.appendChild(t),t.select(),t.selectionStart=0,t.selectionEnd=e.length,document.execCommand("copy"),document.body.removeChild(t),s&&(r.removeAllRanges(),r.addRange(s)),n&&n.focus()}}function Iu(e,t){let n=[],r=!0;const s=o=>{if(r){r=!1;return}n.forEach(i=>document.head.removeChild(i)),n=[],o.forEach(i=>{const l=Us(i);document.head.appendChild(l),n.push(l)})};Kn(()=>{const o=e.data,i=t.value,l=o&&o.description,c=o&&o.frontmatter.head||[];document.title=ai(i,o);const a=l||i.description;let f=document.querySelector("meta[name=description]");f?f.setAttribute("content",a):Us(["meta",{name:"description",content:a}]),s(ui(i.head,$a(c)))})}function Us([e,t,n]){const r=document.createElement(e);for(const s in t)r.setAttribute(s,t[s]);return n&&(r.innerHTML=n),e==="script"&&!t.async&&(r.async=!1),r}function Ha(e){return e[0]==="meta"&&e[1]&&e[1].name==="description"}function $a(e){return e.filter(t=>!Ha(t))}const ir=new Set,di=()=>document.createElement("link"),ja=e=>{const t=di();t.rel="prefetch",t.href=e,document.head.appendChild(t)},Da=e=>{const t=new XMLHttpRequest;t.open("GET",e,t.withCredentials=!0),t.send()};let hn;const ka=Ee&&(hn=di())&&hn.relList&&hn.relList.supports&&hn.relList.supports("prefetch")?ja:Da;function Lu(){if(!Ee||!window.IntersectionObserver)return;let e;if((e=navigator.connection)&&(e.saveData||/2g/.test(e.effectiveType)))return;const t=window.requestIdleCallback||setTimeout;let n=null;const r=()=>{n&&n.disconnect(),n=new IntersectionObserver(o=>{o.forEach(i=>{if(i.isIntersecting){const l=i.target;n.unobserve(l);const{pathname:c}=l;if(!ir.has(c)){ir.add(c);const a=Ra(c);a&&ka(a)}}})}),t(()=>{document.querySelectorAll("#app a").forEach(o=>{const{hostname:i,pathname:l}=new URL(o.href instanceof SVGAnimatedString?o.href.animVal:o.href,o.baseURI),c=l.match(/\.\w+$/);c&&c[0]!==".html"||o.target!=="_blank"&&i===location.hostname&&(l!==location.pathname?n.observe(o):ir.add(l))})})};St(r);const s=fi();Ne(()=>s.path,r),qn(()=>{n&&n.disconnect()})}export{au as $,za as A,To as B,Xa as C,Za as D,jn as E,ge as F,Au as G,se as H,Qa as I,ii as J,fi as K,fc as L,bt as M,Mn as N,Dn as O,xu as P,ou as Q,Jt as R,_u as S,Yo as T,Ja as U,Ru as V,Cu as W,Jl as X,tu as Y,uu as Z,hu as _,Wo as a,nu as a0,Ga as a1,Ba as a2,uc as a3,Iu as a4,Pa as a5,Tu as a6,Aa as a7,Ou as a8,Pu as a9,Jn as aA,yu as aB,Eu as aC,wu as aD,gu as aE,Fa as aF,qt as aG,Ao as aH,fu as aI,ct as aa,du as ab,Su as ac,Ra as ad,Lu as ae,Fu as af,Mu as ag,br as ah,ol as ai,It as aj,Ka as ak,Wa as al,ue as am,Xs as an,bu as ao,vu as ap,pu as aq,Qe as ar,ru as as,Hn as at,Pe as au,mu as av,Ya as aw,cu as ax,lu as ay,Dt as az,Bo as b,su as c,Ir as d,iu as e,Ns as f,ce as g,le as h,ba as i,St as j,Ko as k,fo as l,qa as m,Fn as n,Do as o,Va as p,va as q,eu as r,Ee as s,Ua as t,Sa as u,ca as v,wl as w,Ne as x,Kn as y,qn as z}; diff --git a/assets/chunks/theme.3c30bd29.js b/assets/chunks/theme.3c30bd29.js new file mode 100644 index 000000000..bd8bcee66 --- /dev/null +++ b/assets/chunks/theme.3c30bd29.js @@ -0,0 +1 @@ +import{d as g,o as a,c as i,r as u,n as C,a as x,t as L,_ as m,b as $,w as v,e as f,T as ce,u as De,i as Ee,f as ue,g as P,h as I,j as K,k as c,l as r,p as H,m as z,q as j,s as W,v as le,x as q,y as te,z as de,A as we,B as Fe,C as R,F as T,D as A,E as _e,G as Y,H as h,I as U,J as Le,K as se,L as Z,M as ne,N as Oe,O as Ge,P as Se,Q as Ue,R as je,S as ye,U as Ke,V as qe,W as Me,X as Ne,Y as Re,Z as We,$ as Ye,a0 as Je}from"./framework.1293becd.js";const Xe=g({__name:"VPBadge",props:{text:{},type:{default:"tip"}},setup(s){return(e,t)=>(a(),i("span",{class:C(["VPBadge",e.type])},[u(e.$slots,"default",{},()=>[x(L(e.text),1)],!0)],2))}});const Ze=m(Xe,[["__scopeId","data-v-55bb7998"]]),Qe={key:0,class:"VPBackdrop"},et=g({__name:"VPBackdrop",props:{show:{type:Boolean}},setup(s){return(e,t)=>(a(),$(ce,{name:"fade"},{default:v(()=>[e.show?(a(),i("div",Qe)):f("",!0)]),_:1}))}});const tt=m(et,[["__scopeId","data-v-b9dde47a"]]),V=De;function st(s,e){let t,n=!1;return()=>{t&&clearTimeout(t),n?t=setTimeout(s,e):(s(),(n=!0)&&setTimeout(()=>n=!1,e))}}function re(s){return/^\//.test(s)?s:`/${s}`}function J(s){if(Ee(s))return s;const{site:e}=V(),{pathname:t,search:n,hash:o}=new URL(s,"http://a.com"),l=t.endsWith("/")||t.endsWith(".html")?s:s.replace(/(?:(^\.+)\/)?.*$/,`$1${t.replace(/(\.md)?$/,e.value.cleanUrls?"":".html")}${n}${o}`);return ue(l)}function X({removeCurrent:s=!0,correspondingLink:e=!1}={}){const{site:t,localeIndex:n,page:o,theme:l}=V(),d=P(()=>{var _,b;return{label:(_=t.value.locales[n.value])==null?void 0:_.label,link:((b=t.value.locales[n.value])==null?void 0:b.link)||(n.value==="root"?"/":`/${n.value}/`)}});return{localeLinks:P(()=>Object.entries(t.value.locales).flatMap(([_,b])=>s&&d.value.label===b.label?[]:{text:b.label,link:nt(b.link||(_==="root"?"/":`/${_}/`),l.value.i18nRouting!==!1&&e,o.value.relativePath.slice(d.value.link.length-1),!t.value.cleanUrls)})),currentLang:d}}function nt(s,e,t,n){return e?s.replace(/\/$/,"")+re(t.replace(/(^|\/)index\.md$/,"$1").replace(/\.md$/,n?".html":"")):s}const ot=s=>(H("data-v-ef5d1a0f"),s=s(),z(),s),at={class:"NotFound"},lt={class:"code"},rt={class:"title"},it=ot(()=>c("div",{class:"divider"},null,-1)),ct={class:"quote"},ut={class:"action"},dt=["href","aria-label"],_t=g({__name:"NotFound",setup(s){const{site:e,theme:t}=V(),{localeLinks:n}=X({removeCurrent:!1}),o=I("/");return K(()=>{var d;const l=window.location.pathname.replace(e.value.base,"").replace(/(^.*?\/).*$/,"/$1");n.value.length&&(o.value=((d=n.value.find(({link:p})=>p.startsWith(l)))==null?void 0:d.link)||n.value[0].link)}),(l,d)=>{var p,_,b,w,S;return a(),i("div",at,[c("p",lt,L(((p=r(t).notFound)==null?void 0:p.code)??"404"),1),c("h1",rt,L(((_=r(t).notFound)==null?void 0:_.title)??"PAGE NOT FOUND"),1),it,c("blockquote",ct,L(((b=r(t).notFound)==null?void 0:b.quote)??"But if you don't change your direction, and if you keep looking, you may end up where you are heading."),1),c("div",ut,[c("a",{class:"link",href:r(ue)(o.value),"aria-label":((w=r(t).notFound)==null?void 0:w.linkLabel)??"go to home"},L(((S=r(t).notFound)==null?void 0:S.linkText)??"Take me home"),9,dt)])])}}});const vt=m(_t,[["__scopeId","data-v-ef5d1a0f"]]);function Ie(s,e){if(Array.isArray(s))return Q(s);if(s==null)return[];e=re(e);const t=Object.keys(s).sort((o,l)=>l.split("/").length-o.split("/").length).find(o=>e.startsWith(re(o))),n=t?s[t]:[];return Array.isArray(n)?Q(n):Q(n.items,n.base)}function pt(s){const e=[];let t=0;for(const n in s){const o=s[n];if(o.items){t=e.push(o);continue}e[t]||e.push({items:[]}),e[t].items.push(o)}return e}function ht(s){const e=[];function t(n){for(const o of n)o.text&&o.link&&e.push({text:o.text,link:o.link,docFooterText:o.docFooterText}),o.items&&t(o.items)}return t(s),e}function ie(s,e){return Array.isArray(e)?e.some(t=>ie(s,t)):j(s,e.link)?!0:e.items?ie(s,e.items):!1}function Q(s,e){return[...s].map(t=>{const n={...t},o=n.base||e;return o&&n.link&&(n.link=o+n.link),n.items&&(n.items=Q(n.items,o)),n})}function F(){const{frontmatter:s,page:e,theme:t}=V(),n=le("(min-width: 960px)"),o=I(!1),l=P(()=>{const N=t.value.sidebar,y=e.value.relativePath;return N?Ie(N,y):[]}),d=I(l.value);q(l,(N,y)=>{JSON.stringify(N)!==JSON.stringify(y)&&(d.value=l.value)});const p=P(()=>s.value.sidebar!==!1&&d.value.length>0&&s.value.layout!=="home"),_=P(()=>b?s.value.aside==null?t.value.aside==="left":s.value.aside==="left":!1),b=P(()=>s.value.layout==="home"?!1:s.value.aside!=null?!!s.value.aside:t.value.aside!==!1),w=P(()=>p.value&&n.value),S=P(()=>p.value?pt(d.value):[]);function B(){o.value=!0}function k(){o.value=!1}function M(){o.value?k():B()}return{isOpen:o,sidebar:d,sidebarGroups:S,hasSidebar:p,hasAside:b,leftAside:_,isSidebarEnabled:w,open:B,close:k,toggle:M}}function ft(s,e){let t;te(()=>{t=s.value?document.activeElement:void 0}),K(()=>{window.addEventListener("keyup",n)}),de(()=>{window.removeEventListener("keyup",n)});function n(o){o.key==="Escape"&&s.value&&(e(),t==null||t.focus())}}const Te=I(W?location.hash:"");W&&window.addEventListener("hashchange",()=>{Te.value=location.hash});function mt(s){const{page:e}=V(),t=I(!1),n=P(()=>s.value.collapsed!=null),o=P(()=>!!s.value.link),l=I(!1),d=()=>{l.value=j(e.value.relativePath,s.value.link)};q([e,s,Te],d),K(d);const p=P(()=>l.value?!0:s.value.items?ie(e.value.relativePath,s.value.items):!1),_=P(()=>!!(s.value.items&&s.value.items.length));te(()=>{t.value=!!(n.value&&s.value.collapsed)}),we(()=>{(l.value||p.value)&&(t.value=!1)});function b(){n.value&&(t.value=!t.value)}return{collapsed:t,collapsible:n,isLink:o,isActiveLink:l,hasActiveLink:p,hasChildren:_,toggle:b}}function gt(){const{hasSidebar:s}=F(),e=le("(min-width: 960px)"),t=le("(min-width: 1280px)");return{isAsideEnabled:P(()=>!t.value&&!e.value?!1:s.value?t.value:e.value)}}const bt=71;function ve(s){return typeof s.outline=="object"&&!Array.isArray(s.outline)&&s.outline.label||s.outlineTitle||"On this page"}function pe(s){const e=[...document.querySelectorAll(".VPDoc :where(h1,h2,h3,h4,h5,h6)")].filter(t=>t.id&&t.hasChildNodes()).map(t=>{const n=Number(t.tagName[1]);return{title:$t(t),link:"#"+t.id,level:n}});return kt(e,s)}function $t(s){let e="";for(const t of s.childNodes)if(t.nodeType===1){if(t.classList.contains("VPBadge")||t.classList.contains("header-anchor"))continue;e+=t.textContent}else t.nodeType===3&&(e+=t.textContent);return e.trim()}function kt(s,e){if(e===!1)return[];const t=(typeof e=="object"&&!Array.isArray(e)?e.level:e)||2,[n,o]=typeof t=="number"?[t,t]:t==="deep"?[2,6]:t;s=s.filter(d=>d.level>=n&&d.level<=o);const l=[];e:for(let d=0;d=0;_--){const b=s[_];if(b.level{requestAnimationFrame(l),window.addEventListener("scroll",n)}),Fe(()=>{d(location.hash)}),de(()=>{window.removeEventListener("scroll",n)});function l(){if(!t.value)return;const p=[].slice.call(s.value.querySelectorAll(".outline-link")),_=[].slice.call(document.querySelectorAll(".content .header-anchor")).filter(k=>p.some(M=>M.hash===k.hash&&k.offsetParent!==null)),b=window.scrollY,w=window.innerHeight,S=document.body.offsetHeight,B=Math.abs(b+w-S)<1;if(_.length&&B){d(_[_.length-1].hash);return}for(let k=0;k<_.length;k++){const M=_[k],N=_[k+1],[y,D]=Pt(k,M,N);if(y){d(D);return}}}function d(p){o&&o.classList.remove("active"),p==null?o=null:o=s.value.querySelector(`a[href="${decodeURIComponent(p)}"]`);const _=o;_?(_.classList.add("active"),e.value.style.top=_.offsetTop+33+"px",e.value.style.opacity="1"):(e.value.style.top="33px",e.value.style.opacity="0")}}function Pe(s){return s.parentElement.offsetTop-bt}function Pt(s,e,t){const n=window.scrollY;return s===0&&n===0?[!0,null]:n{const o=R("VPDocOutlineItem",!0);return a(),i("ul",{class:C(t.root?"root":"nested")},[(a(!0),i(T,null,A(t.headers,({children:l,link:d,title:p})=>(a(),i("li",null,[c("a",{class:"outline-link",href:d,onClick:e,title:p},L(p),9,Vt),l!=null&&l.length?(a(),$(o,{key:0,headers:l},null,8,["headers"])):f("",!0)]))),256))],2)}}});const he=m(wt,[["__scopeId","data-v-095ce067"]]),Lt=s=>(H("data-v-b6505075"),s=s(),z(),s),St={class:"content"},Mt={class:"outline-title",role:"heading","aria-level":"2"},Nt={"aria-labelledby":"doc-outline-aria-label"},It=Lt(()=>c("span",{class:"visually-hidden",id:"doc-outline-aria-label"}," Table of Contents for current page ",-1)),Tt=g({__name:"VPDocAsideOutline",setup(s){const{frontmatter:e,theme:t}=V(),n=_e([]);Y(()=>{n.value=pe(e.value.outline??t.value.outline)});const o=I(),l=I();return yt(o,l),(d,p)=>(a(),i("div",{class:C(["VPDocAsideOutline",{"has-outline":n.value.length>0}]),ref_key:"container",ref:o,role:"navigation"},[c("div",St,[c("div",{class:"outline-marker",ref_key:"marker",ref:l},null,512),c("div",Mt,L(r(ve)(r(t))),1),c("nav",Nt,[It,h(he,{headers:n.value,root:!0},null,8,["headers"])])])],2))}});const Ct=m(Tt,[["__scopeId","data-v-b6505075"]]),Bt={class:"VPDocAsideCarbonAds"},At=g({__name:"VPDocAsideCarbonAds",props:{carbonAds:{}},setup(s){const e=()=>null;return(t,n)=>(a(),i("div",Bt,[h(r(e),{"carbon-ads":t.carbonAds},null,8,["carbon-ads"])]))}}),xt=s=>(H("data-v-faa59fde"),s=s(),z(),s),Ht={class:"VPDocAside"},zt=xt(()=>c("div",{class:"spacer"},null,-1)),Dt=g({__name:"VPDocAside",setup(s){const{theme:e}=V();return(t,n)=>(a(),i("div",Ht,[u(t.$slots,"aside-top",{},void 0,!0),u(t.$slots,"aside-outline-before",{},void 0,!0),h(Ct),u(t.$slots,"aside-outline-after",{},void 0,!0),zt,u(t.$slots,"aside-ads-before",{},void 0,!0),r(e).carbonAds?(a(),$(At,{key:0,"carbon-ads":r(e).carbonAds},null,8,["carbon-ads"])):f("",!0),u(t.$slots,"aside-ads-after",{},void 0,!0),u(t.$slots,"aside-bottom",{},void 0,!0)]))}});const Et=m(Dt,[["__scopeId","data-v-faa59fde"]]);function Ft(){const{theme:s,page:e}=V();return P(()=>{const{text:t="Edit this page",pattern:n=""}=s.value.editLink||{};let o;return typeof n=="function"?o=n(e.value):o=n.replace(/:path/g,e.value.filePath),{url:o,text:t}})}function Ot(){const{page:s,theme:e,frontmatter:t}=V();return P(()=>{var _,b,w,S,B,k,M,N;const n=Ie(e.value.sidebar,s.value.relativePath),o=ht(n),l=o.findIndex(y=>j(s.value.relativePath,y.link)),d=((_=e.value.docFooter)==null?void 0:_.prev)===!1&&!t.value.prev||t.value.prev===!1,p=((b=e.value.docFooter)==null?void 0:b.next)===!1&&!t.value.next||t.value.next===!1;return{prev:d?void 0:{text:(typeof t.value.prev=="string"?t.value.prev:typeof t.value.prev=="object"?t.value.prev.text:void 0)??((w=o[l-1])==null?void 0:w.docFooterText)??((S=o[l-1])==null?void 0:S.text),link:(typeof t.value.prev=="object"?t.value.prev.link:void 0)??((B=o[l-1])==null?void 0:B.link)},next:p?void 0:{text:(typeof t.value.next=="string"?t.value.next:typeof t.value.next=="object"?t.value.next.text:void 0)??((k=o[l+1])==null?void 0:k.docFooterText)??((M=o[l+1])==null?void 0:M.text),link:(typeof t.value.next=="object"?t.value.next.link:void 0)??((N=o[l+1])==null?void 0:N.link)}}})}const Gt={},Ut={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},jt=c("path",{d:"M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z"},null,-1),Kt=c("path",{d:"M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z"},null,-1),qt=[jt,Kt];function Rt(s,e){return a(),i("svg",Ut,qt)}const Wt=m(Gt,[["render",Rt]]),G=g({__name:"VPLink",props:{tag:{},href:{},noIcon:{type:Boolean},target:{},rel:{}},setup(s){const e=s,t=P(()=>e.tag??(e.href?"a":"span")),n=P(()=>e.href&&Le.test(e.href));return(o,l)=>(a(),$(U(t.value),{class:C(["VPLink",{link:o.href,"vp-external-link-icon":n.value,"no-icon":o.noIcon}]),href:o.href?r(J)(o.href):void 0,target:o.target??(n.value?"_blank":void 0),rel:o.rel??(n.value?"noreferrer":void 0)},{default:v(()=>[u(o.$slots,"default")]),_:3},8,["class","href","target","rel"]))}}),Yt={class:"VPLastUpdated"},Jt=["datetime"],Xt=g({__name:"VPDocFooterLastUpdated",setup(s){const{theme:e,page:t,frontmatter:n,lang:o}=V(),l=P(()=>new Date(n.value.lastUpdated??t.value.lastUpdated)),d=P(()=>l.value.toISOString()),p=I("");return K(()=>{te(()=>{var _,b,w;p.value=new Intl.DateTimeFormat((b=(_=e.value.lastUpdated)==null?void 0:_.formatOptions)!=null&&b.forceLocale?o.value:void 0,((w=e.value.lastUpdated)==null?void 0:w.formatOptions)??{dateStyle:"short",timeStyle:"short"}).format(l.value)})}),(_,b)=>{var w;return a(),i("p",Yt,[x(L(((w=r(e).lastUpdated)==null?void 0:w.text)||r(e).lastUpdatedText||"Last updated")+": ",1),c("time",{datetime:d.value},L(p.value),9,Jt)])}}});const Zt=m(Xt,[["__scopeId","data-v-50a67d3d"]]),Qt={key:0,class:"VPDocFooter"},es={key:0,class:"edit-info"},ts={key:0,class:"edit-link"},ss={key:1,class:"last-updated"},ns={key:1,class:"prev-next"},os={class:"pager"},as=["href"],ls=["innerHTML"],rs=["innerHTML"],is={class:"pager"},cs=["href"],us=["innerHTML"],ds=["innerHTML"],_s=g({__name:"VPDocFooter",setup(s){const{theme:e,page:t,frontmatter:n}=V(),o=Ft(),l=Ot(),d=P(()=>e.value.editLink&&n.value.editLink!==!1),p=P(()=>t.value.lastUpdated&&n.value.lastUpdated!==!1),_=P(()=>d.value||p.value||l.value.prev||l.value.next);return(b,w)=>{var S,B,k,M,N,y;return _.value?(a(),i("footer",Qt,[u(b.$slots,"doc-footer-before",{},void 0,!0),d.value||p.value?(a(),i("div",es,[d.value?(a(),i("div",ts,[h(G,{class:"edit-link-button",href:r(o).url,"no-icon":!0},{default:v(()=>[h(Wt,{class:"edit-link-icon","aria-label":"edit icon"}),x(" "+L(r(o).text),1)]),_:1},8,["href"])])):f("",!0),p.value?(a(),i("div",ss,[h(Zt)])):f("",!0)])):f("",!0),(S=r(l).prev)!=null&&S.link||(B=r(l).next)!=null&&B.link?(a(),i("nav",ns,[c("div",os,[(k=r(l).prev)!=null&&k.link?(a(),i("a",{key:0,class:"pager-link prev",href:r(J)(r(l).prev.link)},[c("span",{class:"desc",innerHTML:((M=r(e).docFooter)==null?void 0:M.prev)||"Previous page"},null,8,ls),c("span",{class:"title",innerHTML:r(l).prev.text},null,8,rs)],8,as)):f("",!0)]),c("div",is,[(N=r(l).next)!=null&&N.link?(a(),i("a",{key:0,class:"pager-link next",href:r(J)(r(l).next.link)},[c("span",{class:"desc",innerHTML:((y=r(e).docFooter)==null?void 0:y.next)||"Next page"},null,8,us),c("span",{class:"title",innerHTML:r(l).next.text},null,8,ds)],8,cs)):f("",!0)])])):f("",!0)])):f("",!0)}}});const vs=m(_s,[["__scopeId","data-v-c6cf7d08"]]),ps={},hs={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},fs=c("path",{d:"M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"},null,-1),ms=[fs];function gs(s,e){return a(),i("svg",hs,ms)}const fe=m(ps,[["render",gs]]),bs={key:0,class:"VPDocOutlineDropdown"},$s={key:0,class:"items"},ks=g({__name:"VPDocOutlineDropdown",setup(s){const{frontmatter:e,theme:t}=V(),n=I(!1);Y(()=>{n.value=!1});const o=_e([]);return Y(()=>{o.value=pe(e.value.outline??t.value.outline)}),(l,d)=>o.value.length>0?(a(),i("div",bs,[c("button",{onClick:d[0]||(d[0]=p=>n.value=!n.value),class:C({open:n.value})},[x(L(r(ve)(r(t)))+" ",1),h(fe,{class:"icon"})],2),n.value?(a(),i("div",$s,[h(he,{headers:o.value},null,8,["headers"])])):f("",!0)])):f("",!0)}});const ys=m(ks,[["__scopeId","data-v-1b8a3c67"]]),Ps=s=>(H("data-v-4c05baee"),s=s(),z(),s),Vs={class:"container"},ws=Ps(()=>c("div",{class:"aside-curtain"},null,-1)),Ls={class:"aside-container"},Ss={class:"aside-content"},Ms={class:"content"},Ns={class:"content-container"},Is={class:"main"},Ts=g({__name:"VPDoc",setup(s){const{theme:e}=V(),t=se(),{hasSidebar:n,hasAside:o,leftAside:l}=F(),d=P(()=>t.path.replace(/[./]+/g,"_").replace(/_html$/,""));return(p,_)=>{const b=R("Content");return a(),i("div",{class:C(["VPDoc",{"has-sidebar":r(n),"has-aside":r(o)}])},[u(p.$slots,"doc-top",{},void 0,!0),c("div",Vs,[r(o)?(a(),i("div",{key:0,class:C(["aside",{"left-aside":r(l)}])},[ws,c("div",Ls,[c("div",Ss,[h(Et,null,{"aside-top":v(()=>[u(p.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":v(()=>[u(p.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":v(()=>[u(p.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(p.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(p.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(p.$slots,"aside-ads-after",{},void 0,!0)]),_:3})])])],2)):f("",!0),c("div",Ms,[c("div",Ns,[u(p.$slots,"doc-before",{},void 0,!0),h(ys),c("main",Is,[h(b,{class:C(["vp-doc",[d.value,r(e).externalLinkIcon&&"external-link-icon-enabled"]])},null,8,["class"])]),h(vs,null,{"doc-footer-before":v(()=>[u(p.$slots,"doc-footer-before",{},void 0,!0)]),_:3}),u(p.$slots,"doc-after",{},void 0,!0)])])]),u(p.$slots,"doc-bottom",{},void 0,!0)],2)}}});const Cs=m(Ts,[["__scopeId","data-v-4c05baee"]]),Bs=g({__name:"VPButton",props:{tag:{},size:{default:"medium"},theme:{default:"brand"},text:{},href:{}},setup(s){const e=s,t=P(()=>e.href&&Le.test(e.href)),n=P(()=>e.tag||e.href?"a":"button");return(o,l)=>(a(),$(U(n.value),{class:C(["VPButton",[o.size,o.theme]]),href:o.href?r(J)(o.href):void 0,target:t.value?"_blank":void 0,rel:t.value?"noreferrer":void 0},{default:v(()=>[x(L(o.text),1)]),_:1},8,["class","href","target","rel"]))}});const As=m(Bs,[["__scopeId","data-v-7f452ae2"]]),xs=["src","alt"],Hs=g({inheritAttrs:!1,__name:"VPImage",props:{image:{},alt:{}},setup(s){return(e,t)=>{const n=R("VPImage",!0);return e.image?(a(),i(T,{key:0},[typeof e.image=="string"||"src"in e.image?(a(),i("img",Z({key:0,class:"VPImage"},typeof e.image=="string"?e.$attrs:{...e.image,...e.$attrs},{src:r(ue)(typeof e.image=="string"?e.image:e.image.src),alt:e.alt??(typeof e.image=="string"?"":e.image.alt||"")}),null,16,xs)):(a(),i(T,{key:1},[h(n,Z({class:"dark",image:e.image.dark,alt:e.image.alt},e.$attrs),null,16,["image","alt"]),h(n,Z({class:"light",image:e.image.light,alt:e.image.alt},e.$attrs),null,16,["image","alt"])],64))],64)):f("",!0)}}});const ee=m(Hs,[["__scopeId","data-v-726efd0b"]]),zs=s=>(H("data-v-620e6b40"),s=s(),z(),s),Ds={class:"container"},Es={class:"main"},Fs={key:0,class:"name"},Os=["innerHTML"],Gs=["innerHTML"],Us=["innerHTML"],js={key:0,class:"actions"},Ks={key:0,class:"image"},qs={class:"image-container"},Rs=zs(()=>c("div",{class:"image-bg"},null,-1)),Ws=g({__name:"VPHero",props:{name:{},text:{},tagline:{},image:{},actions:{}},setup(s){const e=ne("hero-image-slot-exists");return(t,n)=>(a(),i("div",{class:C(["VPHero",{"has-image":t.image||r(e)}])},[c("div",Ds,[c("div",Es,[u(t.$slots,"home-hero-info",{},()=>[t.name?(a(),i("h1",Fs,[c("span",{innerHTML:t.name,class:"clip"},null,8,Os)])):f("",!0),t.text?(a(),i("p",{key:1,innerHTML:t.text,class:"text"},null,8,Gs)):f("",!0),t.tagline?(a(),i("p",{key:2,innerHTML:t.tagline,class:"tagline"},null,8,Us)):f("",!0)],!0),t.actions?(a(),i("div",js,[(a(!0),i(T,null,A(t.actions,o=>(a(),i("div",{key:o.link,class:"action"},[h(As,{tag:"a",size:"medium",theme:o.theme,text:o.text,href:o.link},null,8,["theme","text","href"])]))),128))])):f("",!0)]),t.image||r(e)?(a(),i("div",Ks,[c("div",qs,[Rs,u(t.$slots,"home-hero-image",{},()=>[t.image?(a(),$(ee,{key:0,class:"image-src",image:t.image},null,8,["image"])):f("",!0)],!0)])])):f("",!0)])],2))}});const Ys=m(Ws,[["__scopeId","data-v-620e6b40"]]),Js=g({__name:"VPHomeHero",setup(s){const{frontmatter:e}=V();return(t,n)=>r(e).hero?(a(),$(Ys,{key:0,class:"VPHomeHero",name:r(e).hero.name,text:r(e).hero.text,tagline:r(e).hero.tagline,image:r(e).hero.image,actions:r(e).hero.actions},{"home-hero-info":v(()=>[u(t.$slots,"home-hero-info")]),"home-hero-image":v(()=>[u(t.$slots,"home-hero-image")]),_:3},8,["name","text","tagline","image","actions"])):f("",!0)}}),Xs={},Zs={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Qs=c("path",{d:"M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"},null,-1),en=[Qs];function tn(s,e){return a(),i("svg",Zs,en)}const sn=m(Xs,[["render",tn]]),nn={class:"box"},on={key:0,class:"icon"},an=["innerHTML"],ln=["innerHTML"],rn=["innerHTML"],cn={key:4,class:"link-text"},un={class:"link-text-value"},dn=g({__name:"VPFeature",props:{icon:{},title:{},details:{},link:{},linkText:{},rel:{},target:{}},setup(s){return(e,t)=>(a(),$(G,{class:"VPFeature",href:e.link,rel:e.rel,target:e.target,"no-icon":!0,tag:e.link?"a":"div"},{default:v(()=>[c("article",nn,[typeof e.icon=="object"&&e.icon.wrap?(a(),i("div",on,[h(ee,{image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])])):typeof e.icon=="object"?(a(),$(ee,{key:1,image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])):e.icon?(a(),i("div",{key:2,class:"icon",innerHTML:e.icon},null,8,an)):f("",!0),c("h2",{class:"title",innerHTML:e.title},null,8,ln),e.details?(a(),i("p",{key:3,class:"details",innerHTML:e.details},null,8,rn)):f("",!0),e.linkText?(a(),i("div",cn,[c("p",un,[x(L(e.linkText)+" ",1),h(sn,{class:"link-text-icon"})])])):f("",!0)])]),_:1},8,["href","rel","target","tag"]))}});const _n=m(dn,[["__scopeId","data-v-ba9ef0bd"]]),vn={key:0,class:"VPFeatures"},pn={class:"container"},hn={class:"items"},fn=g({__name:"VPFeatures",props:{features:{}},setup(s){const e=s,t=P(()=>{const n=e.features.length;if(n){if(n===2)return"grid-2";if(n===3)return"grid-3";if(n%3===0)return"grid-6";if(n>3)return"grid-4"}else return});return(n,o)=>n.features?(a(),i("div",vn,[c("div",pn,[c("div",hn,[(a(!0),i(T,null,A(n.features,l=>(a(),i("div",{key:l.title,class:C(["item",[t.value]])},[h(_n,{icon:l.icon,title:l.title,details:l.details,link:l.link,"link-text":l.linkText,rel:l.rel,target:l.target},null,8,["icon","title","details","link","link-text","rel","target"])],2))),128))])])])):f("",!0)}});const mn=m(fn,[["__scopeId","data-v-2f5ee4d1"]]),gn=g({__name:"VPHomeFeatures",setup(s){const{frontmatter:e}=V();return(t,n)=>r(e).features?(a(),$(mn,{key:0,class:"VPHomeFeatures",features:r(e).features},null,8,["features"])):f("",!0)}}),bn={class:"VPHome"},$n=g({__name:"VPHome",setup(s){return(e,t)=>{const n=R("Content");return a(),i("div",bn,[u(e.$slots,"home-hero-before",{},void 0,!0),h(Js,null,{"home-hero-info":v(()=>[u(e.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(e.$slots,"home-hero-image",{},void 0,!0)]),_:3}),u(e.$slots,"home-hero-after",{},void 0,!0),u(e.$slots,"home-features-before",{},void 0,!0),h(gn),u(e.$slots,"home-features-after",{},void 0,!0),h(n)])}}});const kn=m($n,[["__scopeId","data-v-3e3c67d4"]]),yn={},Pn={class:"VPPage"};function Vn(s,e){const t=R("Content");return a(),i("div",Pn,[u(s.$slots,"page-top"),h(t),u(s.$slots,"page-bottom")])}const wn=m(yn,[["render",Vn]]),Ln=g({__name:"VPContent",setup(s){const{page:e,frontmatter:t}=V(),{hasSidebar:n}=F();return(o,l)=>(a(),i("div",{class:C(["VPContent",{"has-sidebar":r(n),"is-home":r(t).layout==="home"}]),id:"VPContent"},[r(e).isNotFound?u(o.$slots,"not-found",{key:0},()=>[h(vt)],!0):r(t).layout==="page"?(a(),$(wn,{key:1},{"page-top":v(()=>[u(o.$slots,"page-top",{},void 0,!0)]),"page-bottom":v(()=>[u(o.$slots,"page-bottom",{},void 0,!0)]),_:3})):r(t).layout==="home"?(a(),$(kn,{key:2},{"home-hero-before":v(()=>[u(o.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":v(()=>[u(o.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(o.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":v(()=>[u(o.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":v(()=>[u(o.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":v(()=>[u(o.$slots,"home-features-after",{},void 0,!0)]),_:3})):r(t).layout&&r(t).layout!=="doc"?(a(),$(U(r(t).layout),{key:3})):(a(),$(Cs,{key:4},{"doc-top":v(()=>[u(o.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":v(()=>[u(o.$slots,"doc-bottom",{},void 0,!0)]),"doc-footer-before":v(()=>[u(o.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":v(()=>[u(o.$slots,"doc-before",{},void 0,!0)]),"doc-after":v(()=>[u(o.$slots,"doc-after",{},void 0,!0)]),"aside-top":v(()=>[u(o.$slots,"aside-top",{},void 0,!0)]),"aside-outline-before":v(()=>[u(o.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(o.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(o.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(o.$slots,"aside-ads-after",{},void 0,!0)]),"aside-bottom":v(()=>[u(o.$slots,"aside-bottom",{},void 0,!0)]),_:3}))],2))}});const Sn=m(Ln,[["__scopeId","data-v-a0e631d0"]]),Mn={class:"container"},Nn=["innerHTML"],In=["innerHTML"],Tn=g({__name:"VPFooter",setup(s){const{theme:e,frontmatter:t}=V(),{hasSidebar:n}=F();return(o,l)=>r(e).footer&&r(t).footer!==!1?(a(),i("footer",{key:0,class:C(["VPFooter",{"has-sidebar":r(n)}])},[c("div",Mn,[r(e).footer.message?(a(),i("p",{key:0,class:"message",innerHTML:r(e).footer.message},null,8,Nn)):f("",!0),r(e).footer.copyright?(a(),i("p",{key:1,class:"copyright",innerHTML:r(e).footer.copyright},null,8,In)):f("",!0)])],2)):f("",!0)}});const Cn=m(Tn,[["__scopeId","data-v-8ea47eff"]]),Bn={class:"header"},An={class:"outline"},xn=g({__name:"VPLocalNavOutlineDropdown",props:{headers:{},navHeight:{}},setup(s){const e=s,{theme:t}=V(),n=I(!1),o=I(0),l=I();Y(()=>{n.value=!1});function d(){n.value=!n.value,o.value=window.innerHeight+Math.min(window.scrollY-e.navHeight,0)}function p(b){b.target.classList.contains("outline-link")&&(l.value&&(l.value.style.transition="none"),Ge(()=>{n.value=!1}))}function _(){n.value=!1,window.scrollTo({top:0,left:0,behavior:"smooth"})}return(b,w)=>(a(),i("div",{class:"VPLocalNavOutlineDropdown",style:Oe({"--vp-vh":o.value+"px"})},[b.headers.length>0?(a(),i("button",{key:0,onClick:d,class:C({open:n.value})},[x(L(r(ve)(r(t)))+" ",1),h(fe,{class:"icon"})],2)):(a(),i("button",{key:1,onClick:_},L(r(t).returnToTopLabel||"Return to top"),1)),h(ce,{name:"flyout"},{default:v(()=>[n.value?(a(),i("div",{key:0,ref_key:"items",ref:l,class:"items",onClick:p},[c("div",Bn,[c("a",{class:"top-link",href:"#",onClick:_},L(r(t).returnToTopLabel||"Return to top"),1)]),c("div",An,[h(he,{headers:b.headers},null,8,["headers"])])],512)):f("",!0)]),_:1})],4))}});const Hn=m(xn,[["__scopeId","data-v-86af0a73"]]),zn={},Dn={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},En=c("path",{d:"M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"},null,-1),Fn=c("path",{d:"M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"},null,-1),On=c("path",{d:"M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"},null,-1),Gn=c("path",{d:"M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"},null,-1),Un=[En,Fn,On,Gn];function jn(s,e){return a(),i("svg",Dn,Un)}const Kn=m(zn,[["render",jn]]),qn=["aria-expanded"],Rn={class:"menu-text"},Wn=g({__name:"VPLocalNav",props:{open:{type:Boolean}},emits:["open-menu"],setup(s){const{theme:e,frontmatter:t}=V(),{hasSidebar:n}=F(),{y:o}=Se(),l=_e([]),d=I(0);K(()=>{d.value=parseInt(getComputedStyle(document.documentElement).getPropertyValue("--vp-nav-height"))}),Y(()=>{l.value=pe(t.value.outline??e.value.outline)});const p=P(()=>l.value.length===0&&!n.value),_=P(()=>({VPLocalNav:!0,fixed:p.value,"reached-top":o.value>=d.value}));return(b,w)=>r(t).layout!=="home"&&(!p.value||r(o)>=d.value)?(a(),i("div",{key:0,class:C(_.value)},[r(n)?(a(),i("button",{key:0,class:"menu","aria-expanded":b.open,"aria-controls":"VPSidebarNav",onClick:w[0]||(w[0]=S=>b.$emit("open-menu"))},[h(Kn,{class:"menu-icon"}),c("span",Rn,L(r(e).sidebarMenuLabel||"Menu"),1)],8,qn)):f("",!0),h(Hn,{headers:l.value,navHeight:d.value},null,8,["headers","navHeight"])],2)):f("",!0)}});const Yn=m(Wn,[["__scopeId","data-v-e86af244"]]);function Jn(){const s=I(!1);function e(){s.value=!0,window.addEventListener("resize",o)}function t(){s.value=!1,window.removeEventListener("resize",o)}function n(){s.value?t():e()}function o(){window.outerWidth>=768&&t()}const l=se();return q(()=>l.path,t),{isScreenOpen:s,openScreen:e,closeScreen:t,toggleScreen:n}}const Xn={},Zn={class:"VPSwitch",type:"button",role:"switch"},Qn={class:"check"},eo={key:0,class:"icon"};function to(s,e){return a(),i("button",Zn,[c("span",Qn,[s.$slots.default?(a(),i("span",eo,[u(s.$slots,"default",{},void 0,!0)])):f("",!0)])])}const so=m(Xn,[["render",to],["__scopeId","data-v-70543c77"]]),no={},oo={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},ao=c("path",{d:"M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"},null,-1),lo=[ao];function ro(s,e){return a(),i("svg",oo,lo)}const io=m(no,[["render",ro]]),co={},uo={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},_o=Ue('',9),vo=[_o];function po(s,e){return a(),i("svg",uo,vo)}const ho=m(co,[["render",po]]),fo=g({__name:"VPSwitchAppearance",setup(s){const{isDark:e}=V(),t=ne("toggle-appearance",()=>{e.value=!e.value});return(n,o)=>(a(),$(so,{title:"toggle dark mode",class:"VPSwitchAppearance","aria-checked":r(e),onClick:r(t)},{default:v(()=>[h(ho,{class:"sun"}),h(io,{class:"moon"})]),_:1},8,["aria-checked","onClick"]))}});const me=m(fo,[["__scopeId","data-v-9e8fb728"]]),mo={key:0,class:"VPNavBarAppearance"},go=g({__name:"VPNavBarAppearance",setup(s){const{site:e}=V();return(t,n)=>r(e).appearance&&r(e).appearance!=="force-dark"?(a(),i("div",mo,[h(me)])):f("",!0)}});const bo=m(go,[["__scopeId","data-v-8024a3fd"]]),ge=I();let Ce=!1,ae=0;function $o(s){const e=I(!1);if(W){!Ce&&ko(),ae++;const t=q(ge,n=>{var o,l,d;n===s.el.value||(o=s.el.value)!=null&&o.contains(n)?(e.value=!0,(l=s.onFocus)==null||l.call(s)):(e.value=!1,(d=s.onBlur)==null||d.call(s))});de(()=>{t(),ae--,ae||yo()})}return je(e)}function ko(){document.addEventListener("focusin",Be),Ce=!0,ge.value=document.activeElement}function yo(){document.removeEventListener("focusin",Be)}function Be(){ge.value=document.activeElement}const Po={},Vo={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},wo=c("path",{d:"M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"},null,-1),Lo=[wo];function So(s,e){return a(),i("svg",Vo,Lo)}const Ae=m(Po,[["render",So]]),Mo={},No={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Io=c("circle",{cx:"12",cy:"12",r:"2"},null,-1),To=c("circle",{cx:"19",cy:"12",r:"2"},null,-1),Co=c("circle",{cx:"5",cy:"12",r:"2"},null,-1),Bo=[Io,To,Co];function Ao(s,e){return a(),i("svg",No,Bo)}const xo=m(Mo,[["render",Ao]]),Ho={class:"VPMenuLink"},zo=g({__name:"VPMenuLink",props:{item:{}},setup(s){const{page:e}=V();return(t,n)=>(a(),i("div",Ho,[h(G,{class:C({active:r(j)(r(e).relativePath,t.item.activeMatch||t.item.link,!!t.item.activeMatch)}),href:t.item.link,target:t.item.target,rel:t.item.rel},{default:v(()=>[x(L(t.item.text),1)]),_:1},8,["class","href","target","rel"])]))}});const oe=m(zo,[["__scopeId","data-v-567fbcba"]]),Do={class:"VPMenuGroup"},Eo={key:0,class:"title"},Fo=g({__name:"VPMenuGroup",props:{text:{},items:{}},setup(s){return(e,t)=>(a(),i("div",Do,[e.text?(a(),i("p",Eo,L(e.text),1)):f("",!0),(a(!0),i(T,null,A(e.items,n=>(a(),i(T,null,["link"in n?(a(),$(oe,{key:0,item:n},null,8,["item"])):f("",!0)],64))),256))]))}});const Oo=m(Fo,[["__scopeId","data-v-b0ffa15d"]]),Go={class:"VPMenu"},Uo={key:0,class:"items"},jo=g({__name:"VPMenu",props:{items:{}},setup(s){return(e,t)=>(a(),i("div",Go,[e.items?(a(),i("div",Uo,[(a(!0),i(T,null,A(e.items,n=>(a(),i(T,{key:n.text},["link"in n?(a(),$(oe,{key:0,item:n},null,8,["item"])):(a(),$(Oo,{key:1,text:n.text,items:n.items},null,8,["text","items"]))],64))),128))])):f("",!0),u(e.$slots,"default",{},void 0,!0)]))}});const Ko=m(jo,[["__scopeId","data-v-923db0f3"]]),qo=["aria-expanded","aria-label"],Ro={key:0,class:"text"},Wo=["innerHTML"],Yo={class:"menu"},Jo=g({__name:"VPFlyout",props:{icon:{},button:{},label:{},items:{}},setup(s){const e=I(!1),t=I();$o({el:t,onBlur:n});function n(){e.value=!1}return(o,l)=>(a(),i("div",{class:"VPFlyout",ref_key:"el",ref:t,onMouseenter:l[1]||(l[1]=d=>e.value=!0),onMouseleave:l[2]||(l[2]=d=>e.value=!1)},[c("button",{type:"button",class:"button","aria-haspopup":"true","aria-expanded":e.value,"aria-label":o.label,onClick:l[0]||(l[0]=d=>e.value=!e.value)},[o.button||o.icon?(a(),i("span",Ro,[o.icon?(a(),$(U(o.icon),{key:0,class:"option-icon"})):f("",!0),o.button?(a(),i("span",{key:1,innerHTML:o.button},null,8,Wo)):f("",!0),h(Ae,{class:"text-icon"})])):(a(),$(xo,{key:1,class:"icon"}))],8,qo),c("div",Yo,[h(Ko,{items:o.items},{default:v(()=>[u(o.$slots,"default",{},void 0,!0)]),_:3},8,["items"])])],544))}});const be=m(Jo,[["__scopeId","data-v-8377b1cc"]]),Xo={discord:'Discord',facebook:'Facebook',github:'GitHub',instagram:'Instagram',linkedin:'LinkedIn',mastodon:'Mastodon',slack:'Slack',twitter:'Twitter',x:'X',youtube:'YouTube'},Zo=["href","aria-label","innerHTML"],Qo=g({__name:"VPSocialLink",props:{icon:{},link:{},ariaLabel:{}},setup(s){const e=s,t=P(()=>typeof e.icon=="object"?e.icon.svg:Xo[e.icon]);return(n,o)=>(a(),i("a",{class:"VPSocialLink no-icon",href:n.link,"aria-label":n.ariaLabel??(typeof n.icon=="string"?n.icon:""),target:"_blank",rel:"noopener",innerHTML:t.value},null,8,Zo))}});const ea=m(Qo,[["__scopeId","data-v-5c356b5c"]]),ta={class:"VPSocialLinks"},sa=g({__name:"VPSocialLinks",props:{links:{}},setup(s){return(e,t)=>(a(),i("div",ta,[(a(!0),i(T,null,A(e.links,({link:n,icon:o,ariaLabel:l})=>(a(),$(ea,{key:n,icon:o,link:n,ariaLabel:l},null,8,["icon","link","ariaLabel"]))),128))]))}});const $e=m(sa,[["__scopeId","data-v-ba919e0f"]]),na={key:0,class:"group translations"},oa={class:"trans-title"},aa={key:1,class:"group"},la={class:"item appearance"},ra={class:"label"},ia={class:"appearance-action"},ca={key:2,class:"group"},ua={class:"item social-links"},da=g({__name:"VPNavBarExtra",setup(s){const{site:e,theme:t}=V(),{localeLinks:n,currentLang:o}=X({correspondingLink:!0}),l=P(()=>n.value.length&&o.value.label||e.value.appearance||t.value.socialLinks);return(d,p)=>l.value?(a(),$(be,{key:0,class:"VPNavBarExtra",label:"extra navigation"},{default:v(()=>[r(n).length&&r(o).label?(a(),i("div",na,[c("p",oa,L(r(o).label),1),(a(!0),i(T,null,A(r(n),_=>(a(),$(oe,{key:_.link,item:_},null,8,["item"]))),128))])):f("",!0),r(e).appearance?(a(),i("div",aa,[c("div",la,[c("p",ra,L(r(t).darkModeSwitchLabel||"Appearance"),1),c("div",ia,[h(me)])])])):f("",!0),r(t).socialLinks?(a(),i("div",ca,[c("div",ua,[h($e,{class:"social-links-list",links:r(t).socialLinks},null,8,["links"])])])):f("",!0)]),_:1})):f("",!0)}});const _a=m(da,[["__scopeId","data-v-1425926a"]]),va=s=>(H("data-v-b37633d2"),s=s(),z(),s),pa=["aria-expanded"],ha=va(()=>c("span",{class:"container"},[c("span",{class:"top"}),c("span",{class:"middle"}),c("span",{class:"bottom"})],-1)),fa=[ha],ma=g({__name:"VPNavBarHamburger",props:{active:{type:Boolean}},emits:["click"],setup(s){return(e,t)=>(a(),i("button",{type:"button",class:C(["VPNavBarHamburger",{active:e.active}]),"aria-label":"mobile navigation","aria-expanded":e.active,"aria-controls":"VPNavScreen",onClick:t[0]||(t[0]=n=>e.$emit("click"))},fa,10,pa))}});const ga=m(ma,[["__scopeId","data-v-b37633d2"]]),ba=["innerHTML"],$a=g({__name:"VPNavBarMenuLink",props:{item:{}},setup(s){const{page:e}=V();return(t,n)=>(a(),$(G,{class:C({VPNavBarMenuLink:!0,active:r(j)(r(e).relativePath,t.item.activeMatch||t.item.link,!!t.item.activeMatch)}),href:t.item.link,target:t.item.target,rel:t.item.rel,tabindex:"0"},{default:v(()=>[c("span",{innerHTML:t.item.text},null,8,ba)]),_:1},8,["class","href","target","rel"]))}});const ka=m($a,[["__scopeId","data-v-b0bf7172"]]),ya=g({__name:"VPNavBarMenuGroup",props:{item:{}},setup(s){const e=s,{page:t}=V(),n=l=>"link"in l?j(t.value.relativePath,l.link,!!e.item.activeMatch):l.items.some(n),o=P(()=>n(e.item));return(l,d)=>(a(),$(be,{class:C({VPNavBarMenuGroup:!0,active:r(j)(r(t).relativePath,l.item.activeMatch,!!l.item.activeMatch)||o.value}),button:l.item.text,items:l.item.items},null,8,["class","button","items"]))}}),Pa=s=>(H("data-v-4907edd6"),s=s(),z(),s),Va={key:0,"aria-labelledby":"main-nav-aria-label",class:"VPNavBarMenu"},wa=Pa(()=>c("span",{id:"main-nav-aria-label",class:"visually-hidden"},"Main Navigation",-1)),La=g({__name:"VPNavBarMenu",setup(s){const{theme:e}=V();return(t,n)=>r(e).nav?(a(),i("nav",Va,[wa,(a(!0),i(T,null,A(r(e).nav,o=>(a(),i(T,{key:o.text},["link"in o?(a(),$(ka,{key:0,item:o},null,8,["item"])):(a(),$(ya,{key:1,item:o},null,8,["item"]))],64))),128))])):f("",!0)}});const Sa=m(La,[["__scopeId","data-v-4907edd6"]]);const Ma={type:"button",class:"DocSearch DocSearch-Button","aria-label":"Search"},Na={class:"DocSearch-Button-Container"},Ia=c("svg",{class:"DocSearch-Search-Icon",width:"20",height:"20",viewBox:"0 0 20 20","aria-label":"search icon"},[c("path",{d:"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z",stroke:"currentColor",fill:"none","fill-rule":"evenodd","stroke-linecap":"round","stroke-linejoin":"round"})],-1),Ta={class:"DocSearch-Button-Placeholder"},Ca=c("span",{class:"DocSearch-Button-Keys"},[c("kbd",{class:"DocSearch-Button-Key"}),c("kbd",{class:"DocSearch-Button-Key"},"K")],-1),Ve=g({__name:"VPNavBarSearchButton",props:{placeholder:{}},setup(s){return(e,t)=>(a(),i("button",Ma,[c("span",Na,[Ia,c("span",Ta,L(e.placeholder),1)]),Ca]))}});const Ba={class:"VPNavBarSearch"},Aa={id:"local-search"},xa={key:1,id:"docsearch"},Ha=g({__name:"VPNavBarSearch",setup(s){const e=Ke(()=>qe(()=>import("./VPLocalSearchBox.3ae6ae48.js"),["assets/chunks/VPLocalSearchBox.3ae6ae48.js","assets/chunks/framework.1293becd.js"])),t=()=>null,{theme:n,localeIndex:o}=V(),l=I(!1),d=I(!1),p=P(()=>{var M,N,y,D,O,E,ke;const k=((M=n.value.search)==null?void 0:M.options)??n.value.algolia;return((O=(D=(y=(N=k==null?void 0:k.locales)==null?void 0:N[o.value])==null?void 0:y.translations)==null?void 0:D.button)==null?void 0:O.buttonText)||((ke=(E=k==null?void 0:k.translations)==null?void 0:E.button)==null?void 0:ke.buttonText)||"Search"});K(()=>{});function _(){l.value||(l.value=!0,setTimeout(b,16))}function b(){const k=new Event("keydown");k.key="k",k.metaKey=!0,window.dispatchEvent(k),setTimeout(()=>{document.querySelector(".DocSearch-Modal")||b()},16)}function w(k){const M=k.target,N=M.tagName;return M.isContentEditable||N==="INPUT"||N==="SELECT"||N==="TEXTAREA"}const S=I(!1);ye("k",k=>{(k.ctrlKey||k.metaKey)&&(k.preventDefault(),S.value=!0)}),ye("/",k=>{w(k)||(k.preventDefault(),S.value=!0)});const B="local";return(k,M)=>{var N;return a(),i("div",Ba,[r(B)==="local"?(a(),i(T,{key:0},[S.value?(a(),$(r(e),{key:0,placeholder:p.value,onClose:M[0]||(M[0]=y=>S.value=!1)},null,8,["placeholder"])):f("",!0),c("div",Aa,[h(Ve,{placeholder:p.value,onClick:M[1]||(M[1]=y=>S.value=!0)},null,8,["placeholder"])])],64)):r(B)==="algolia"?(a(),i(T,{key:1},[l.value?(a(),$(r(t),{key:0,algolia:((N=r(n).search)==null?void 0:N.options)??r(n).algolia,onVnodeBeforeMount:M[2]||(M[2]=y=>d.value=!0)},null,8,["algolia"])):f("",!0),d.value?f("",!0):(a(),i("div",xa,[h(Ve,{placeholder:p.value,onClick:_},null,8,["placeholder"])]))],64)):f("",!0)])}}});const za=g({__name:"VPNavBarSocialLinks",setup(s){const{theme:e}=V();return(t,n)=>r(e).socialLinks?(a(),$($e,{key:0,class:"VPNavBarSocialLinks",links:r(e).socialLinks},null,8,["links"])):f("",!0)}});const Da=m(za,[["__scopeId","data-v-24391483"]]),Ea=["href"],Fa=g({__name:"VPNavBarTitle",setup(s){const{site:e,theme:t}=V(),{hasSidebar:n}=F(),{currentLang:o}=X();return(l,d)=>(a(),i("div",{class:C(["VPNavBarTitle",{"has-sidebar":r(n)}])},[c("a",{class:"title",href:r(t).logoLink??r(J)(r(o).link)},[u(l.$slots,"nav-bar-title-before",{},void 0,!0),r(t).logo?(a(),$(ee,{key:0,class:"logo",image:r(t).logo},null,8,["image"])):f("",!0),r(t).siteTitle?(a(),i(T,{key:1},[x(L(r(t).siteTitle),1)],64)):r(t).siteTitle===void 0?(a(),i(T,{key:2},[x(L(r(e).title),1)],64)):f("",!0),u(l.$slots,"nav-bar-title-after",{},void 0,!0)],8,Ea)],2))}});const Oa=m(Fa,[["__scopeId","data-v-a332c19e"]]),Ga={},Ua={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},ja=c("path",{d:"M0 0h24v24H0z",fill:"none"},null,-1),Ka=c("path",{d:" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z ",class:"css-c4d79v"},null,-1),qa=[ja,Ka];function Ra(s,e){return a(),i("svg",Ua,qa)}const xe=m(Ga,[["render",Ra]]),Wa={class:"items"},Ya={class:"title"},Ja=g({__name:"VPNavBarTranslations",setup(s){const{theme:e}=V(),{localeLinks:t,currentLang:n}=X({correspondingLink:!0});return(o,l)=>r(t).length&&r(n).label?(a(),$(be,{key:0,class:"VPNavBarTranslations",icon:xe,label:r(e).langMenuLabel||"Change language"},{default:v(()=>[c("div",Wa,[c("p",Ya,L(r(n).label),1),(a(!0),i(T,null,A(r(t),d=>(a(),$(oe,{key:d.link,item:d},null,8,["item"]))),128))])]),_:1},8,["label"])):f("",!0)}});const Xa=m(Ja,[["__scopeId","data-v-c9642a5b"]]),Za=s=>(H("data-v-37ae32c0"),s=s(),z(),s),Qa={class:"container"},el={class:"title"},tl={class:"content"},sl=Za(()=>c("div",{class:"curtain"},null,-1)),nl={class:"content-body"},ol=g({__name:"VPNavBar",props:{isScreenOpen:{type:Boolean}},emits:["toggle-screen"],setup(s){const{y:e}=Se(),{hasSidebar:t}=F(),{frontmatter:n}=V(),o=I({});return we(()=>{o.value={"has-sidebar":t.value,top:n.value.layout==="home"&&e.value===0}}),(l,d)=>(a(),i("div",{class:C(["VPNavBar",o.value])},[c("div",Qa,[c("div",el,[h(Oa,null,{"nav-bar-title-before":v(()=>[u(l.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(l.$slots,"nav-bar-title-after",{},void 0,!0)]),_:3})]),c("div",tl,[sl,c("div",nl,[u(l.$slots,"nav-bar-content-before",{},void 0,!0),h(Ha,{class:"search"}),h(Sa,{class:"menu"}),h(Xa,{class:"translations"}),h(bo,{class:"appearance"}),h(Da,{class:"social-links"}),h(_a,{class:"extra"}),u(l.$slots,"nav-bar-content-after",{},void 0,!0),h(ga,{class:"hamburger",active:l.isScreenOpen,onClick:d[0]||(d[0]=p=>l.$emit("toggle-screen"))},null,8,["active"])])])])],2))}});const al=m(ol,[["__scopeId","data-v-37ae32c0"]]),ll={key:0,class:"VPNavScreenAppearance"},rl={class:"text"},il=g({__name:"VPNavScreenAppearance",setup(s){const{site:e,theme:t}=V();return(n,o)=>r(e).appearance?(a(),i("div",ll,[c("p",rl,L(r(t).darkModeSwitchLabel||"Appearance"),1),h(me)])):f("",!0)}});const cl=m(il,[["__scopeId","data-v-5e6cad11"]]),ul=g({__name:"VPNavScreenMenuLink",props:{item:{}},setup(s){const e=ne("close-screen");return(t,n)=>(a(),$(G,{class:"VPNavScreenMenuLink",href:t.item.link,target:t.item.target,rel:t.item.rel,onClick:r(e)},{default:v(()=>[x(L(t.item.text),1)]),_:1},8,["href","target","rel","onClick"]))}});const dl=m(ul,[["__scopeId","data-v-ba033da0"]]),_l={},vl={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},pl=c("path",{d:"M18.9,10.9h-6v-6c0-0.6-0.4-1-1-1s-1,0.4-1,1v6h-6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6v6c0,0.6,0.4,1,1,1s1-0.4,1-1v-6h6c0.6,0,1-0.4,1-1S19.5,10.9,18.9,10.9z"},null,-1),hl=[pl];function fl(s,e){return a(),i("svg",vl,hl)}const ml=m(_l,[["render",fl]]),gl=g({__name:"VPNavScreenMenuGroupLink",props:{item:{}},setup(s){const e=ne("close-screen");return(t,n)=>(a(),$(G,{class:"VPNavScreenMenuGroupLink",href:t.item.link,target:t.item.target,rel:t.item.rel,onClick:r(e)},{default:v(()=>[x(L(t.item.text),1)]),_:1},8,["href","target","rel","onClick"]))}});const He=m(gl,[["__scopeId","data-v-eaf005d4"]]),bl={class:"VPNavScreenMenuGroupSection"},$l={key:0,class:"title"},kl=g({__name:"VPNavScreenMenuGroupSection",props:{text:{},items:{}},setup(s){return(e,t)=>(a(),i("div",bl,[e.text?(a(),i("p",$l,L(e.text),1)):f("",!0),(a(!0),i(T,null,A(e.items,n=>(a(),$(He,{key:n.text,item:n},null,8,["item"]))),128))]))}});const yl=m(kl,[["__scopeId","data-v-a8341ee1"]]),Pl=["aria-controls","aria-expanded"],Vl={class:"button-text"},wl=["id"],Ll={key:1,class:"group"},Sl=g({__name:"VPNavScreenMenuGroup",props:{text:{},items:{}},setup(s){const e=s,t=I(!1),n=P(()=>`NavScreenGroup-${e.text.replace(" ","-").toLowerCase()}`);function o(){t.value=!t.value}return(l,d)=>(a(),i("div",{class:C(["VPNavScreenMenuGroup",{open:t.value}])},[c("button",{class:"button","aria-controls":n.value,"aria-expanded":t.value,onClick:o},[c("span",Vl,L(l.text),1),h(ml,{class:"button-icon"})],8,Pl),c("div",{id:n.value,class:"items"},[(a(!0),i(T,null,A(l.items,p=>(a(),i(T,{key:p.text},["link"in p?(a(),i("div",{key:p.text,class:"item"},[h(He,{item:p},null,8,["item"])])):(a(),i("div",Ll,[h(yl,{text:p.text,items:p.items},null,8,["text","items"])]))],64))),128))],8,wl)],2))}});const Ml=m(Sl,[["__scopeId","data-v-7cc3174b"]]),Nl={key:0,class:"VPNavScreenMenu"},Il=g({__name:"VPNavScreenMenu",setup(s){const{theme:e}=V();return(t,n)=>r(e).nav?(a(),i("nav",Nl,[(a(!0),i(T,null,A(r(e).nav,o=>(a(),i(T,{key:o.text},["link"in o?(a(),$(dl,{key:0,item:o},null,8,["item"])):(a(),$(Ml,{key:1,text:o.text||"",items:o.items},null,8,["text","items"]))],64))),128))])):f("",!0)}}),Tl=g({__name:"VPNavScreenSocialLinks",setup(s){const{theme:e}=V();return(t,n)=>r(e).socialLinks?(a(),$($e,{key:0,class:"VPNavScreenSocialLinks",links:r(e).socialLinks},null,8,["links"])):f("",!0)}}),Cl={class:"list"},Bl=g({__name:"VPNavScreenTranslations",setup(s){const{localeLinks:e,currentLang:t}=X({correspondingLink:!0}),n=I(!1);function o(){n.value=!n.value}return(l,d)=>r(e).length&&r(t).label?(a(),i("div",{key:0,class:C(["VPNavScreenTranslations",{open:n.value}])},[c("button",{class:"title",onClick:o},[h(xe,{class:"icon lang"}),x(" "+L(r(t).label)+" ",1),h(Ae,{class:"icon chevron"})]),c("ul",Cl,[(a(!0),i(T,null,A(r(e),p=>(a(),i("li",{key:p.link,class:"item"},[h(G,{class:"link",href:p.link},{default:v(()=>[x(L(p.text),1)]),_:2},1032,["href"])]))),128))])],2)):f("",!0)}});const Al=m(Bl,[["__scopeId","data-v-60f40e20"]]),xl={class:"container"},Hl=g({__name:"VPNavScreen",props:{open:{type:Boolean}},setup(s){const e=I(null),t=Me(W?document.body:null);return(n,o)=>(a(),$(ce,{name:"fade",onEnter:o[0]||(o[0]=l=>t.value=!0),onAfterLeave:o[1]||(o[1]=l=>t.value=!1)},{default:v(()=>[n.open?(a(),i("div",{key:0,class:"VPNavScreen",ref_key:"screen",ref:e,id:"VPNavScreen"},[c("div",xl,[u(n.$slots,"nav-screen-content-before",{},void 0,!0),h(Il,{class:"menu"}),h(Al,{class:"translations"}),h(cl,{class:"appearance"}),h(Tl,{class:"social-links"}),u(n.$slots,"nav-screen-content-after",{},void 0,!0)])],512)):f("",!0)]),_:3}))}});const zl=m(Hl,[["__scopeId","data-v-c0c39f05"]]),Dl={key:0,class:"VPNav"},El=g({__name:"VPNav",setup(s){const{isScreenOpen:e,closeScreen:t,toggleScreen:n}=Jn(),{frontmatter:o}=V(),l=P(()=>o.value.navbar!==!1);return Ne("close-screen",t),te(()=>{W&&document.documentElement.classList.toggle("hide-nav",!l.value)}),(d,p)=>l.value?(a(),i("header",Dl,[h(al,{"is-screen-open":r(e),onToggleScreen:r(n)},{"nav-bar-title-before":v(()=>[u(d.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(d.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":v(()=>[u(d.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":v(()=>[u(d.$slots,"nav-bar-content-after",{},void 0,!0)]),_:3},8,["is-screen-open","onToggleScreen"]),h(zl,{open:r(e)},{"nav-screen-content-before":v(()=>[u(d.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":v(()=>[u(d.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3},8,["open"])])):f("",!0)}});const Fl=m(El,[["__scopeId","data-v-ddf930b5"]]),Ol=s=>(H("data-v-9e3d5071"),s=s(),z(),s),Gl=["role","tabindex"],Ul=Ol(()=>c("div",{class:"indicator"},null,-1)),jl=["onKeydown"],Kl={key:1,class:"items"},ql=g({__name:"VPSidebarItem",props:{item:{},depth:{}},setup(s){const e=s,{collapsed:t,collapsible:n,isLink:o,isActiveLink:l,hasActiveLink:d,hasChildren:p,toggle:_}=mt(P(()=>e.item)),b=P(()=>p.value?"section":"div"),w=P(()=>o.value?"a":"div"),S=P(()=>p.value?e.depth+2===7?"p":`h${e.depth+2}`:"p"),B=P(()=>o.value?void 0:"button"),k=P(()=>[[`level-${e.depth}`],{collapsible:n.value},{collapsed:t.value},{"is-link":o.value},{"is-active":l.value},{"has-active":d.value}]);function M(y){"key"in y&&y.key!=="Enter"||!e.item.link&&_()}function N(){e.item.link&&_()}return(y,D)=>{const O=R("VPSidebarItem",!0);return a(),$(U(b.value),{class:C(["VPSidebarItem",k.value])},{default:v(()=>[y.item.text?(a(),i("div",Z({key:0,class:"item",role:B.value},Re(y.item.items?{click:M,keydown:M}:{},!0),{tabindex:y.item.items&&0}),[Ul,y.item.link?(a(),$(G,{key:0,tag:w.value,class:"link",href:y.item.link,rel:y.item.rel,target:y.item.target},{default:v(()=>[(a(),$(U(S.value),{class:"text",innerHTML:y.item.text},null,8,["innerHTML"]))]),_:1},8,["tag","href","rel","target"])):(a(),$(U(S.value),{key:1,class:"text",innerHTML:y.item.text},null,8,["innerHTML"])),y.item.collapsed!=null?(a(),i("div",{key:2,class:"caret",role:"button","aria-label":"toggle section",onClick:N,onKeydown:We(N,["enter"]),tabindex:"0"},[h(fe,{class:"caret-icon"})],40,jl)):f("",!0)],16,Gl)):f("",!0),y.item.items&&y.item.items.length?(a(),i("div",Kl,[y.depth<5?(a(!0),i(T,{key:0},A(y.item.items,E=>(a(),$(O,{key:E.text,item:E,depth:y.depth+1},null,8,["item","depth"]))),128)):f("",!0)])):f("",!0)]),_:1},8,["class"])}}});const Rl=m(ql,[["__scopeId","data-v-9e3d5071"]]),ze=s=>(H("data-v-727d01b3"),s=s(),z(),s),Wl=ze(()=>c("div",{class:"curtain"},null,-1)),Yl={class:"nav",id:"VPSidebarNav","aria-labelledby":"sidebar-aria-label",tabindex:"-1"},Jl=ze(()=>c("span",{class:"visually-hidden",id:"sidebar-aria-label"}," Sidebar Navigation ",-1)),Xl=g({__name:"VPSidebar",props:{open:{type:Boolean}},setup(s){const e=s,{sidebarGroups:t,hasSidebar:n}=F(),o=I(null),l=Me(W?document.body:null);return q([e,o],()=>{var d;e.open?(l.value=!0,(d=o.value)==null||d.focus()):l.value=!1},{immediate:!0,flush:"post"}),(d,p)=>r(n)?(a(),i("aside",{key:0,class:C(["VPSidebar",{open:d.open}]),ref_key:"navEl",ref:o,onClick:p[0]||(p[0]=Ye(()=>{},["stop"]))},[Wl,c("nav",Yl,[Jl,u(d.$slots,"sidebar-nav-before",{},void 0,!0),(a(!0),i(T,null,A(r(t),_=>(a(),i("div",{key:_.text,class:"group"},[h(Rl,{item:_,depth:0},null,8,["item"])]))),128)),u(d.$slots,"sidebar-nav-after",{},void 0,!0)])],2)):f("",!0)}});const Zl=m(Xl,[["__scopeId","data-v-727d01b3"]]),Ql=g({__name:"VPSkipLink",setup(s){const e=se(),t=I();q(()=>e.path,()=>t.value.focus());function n({target:o}){const l=document.getElementById(decodeURIComponent(o.hash).slice(1));if(l){const d=()=>{l.removeAttribute("tabindex"),l.removeEventListener("blur",d)};l.setAttribute("tabindex","-1"),l.addEventListener("blur",d),l.focus(),window.scrollTo(0,0)}}return(o,l)=>(a(),i(T,null,[c("span",{ref_key:"backToTop",ref:t,tabindex:"-1"},null,512),c("a",{href:"#VPContent",class:"VPSkipLink visually-hidden",onClick:n}," Skip to content ")],64))}});const er=m(Ql,[["__scopeId","data-v-7190744d"]]),tr=g({__name:"Layout",setup(s){const{isOpen:e,open:t,close:n}=F(),o=se();q(()=>o.path,n),ft(e,n);const{frontmatter:l}=V(),d=Je(),p=P(()=>!!d["home-hero-image"]);return Ne("hero-image-slot-exists",p),(_,b)=>{const w=R("Content");return r(l).layout!==!1?(a(),i("div",{key:0,class:C(["Layout",r(l).pageClass])},[u(_.$slots,"layout-top",{},void 0,!0),h(er),h(tt,{class:"backdrop",show:r(e),onClick:r(n)},null,8,["show","onClick"]),h(Fl,null,{"nav-bar-title-before":v(()=>[u(_.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(_.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":v(()=>[u(_.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":v(()=>[u(_.$slots,"nav-bar-content-after",{},void 0,!0)]),"nav-screen-content-before":v(()=>[u(_.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":v(()=>[u(_.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3}),h(Yn,{open:r(e),onOpenMenu:r(t)},null,8,["open","onOpenMenu"]),h(Zl,{open:r(e)},{"sidebar-nav-before":v(()=>[u(_.$slots,"sidebar-nav-before",{},void 0,!0)]),"sidebar-nav-after":v(()=>[u(_.$slots,"sidebar-nav-after",{},void 0,!0)]),_:3},8,["open"]),h(Sn,null,{"page-top":v(()=>[u(_.$slots,"page-top",{},void 0,!0)]),"page-bottom":v(()=>[u(_.$slots,"page-bottom",{},void 0,!0)]),"not-found":v(()=>[u(_.$slots,"not-found",{},void 0,!0)]),"home-hero-before":v(()=>[u(_.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":v(()=>[u(_.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(_.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":v(()=>[u(_.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":v(()=>[u(_.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":v(()=>[u(_.$slots,"home-features-after",{},void 0,!0)]),"doc-footer-before":v(()=>[u(_.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":v(()=>[u(_.$slots,"doc-before",{},void 0,!0)]),"doc-after":v(()=>[u(_.$slots,"doc-after",{},void 0,!0)]),"doc-top":v(()=>[u(_.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":v(()=>[u(_.$slots,"doc-bottom",{},void 0,!0)]),"aside-top":v(()=>[u(_.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":v(()=>[u(_.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":v(()=>[u(_.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(_.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(_.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(_.$slots,"aside-ads-after",{},void 0,!0)]),_:3}),h(Cn),u(_.$slots,"layout-bottom",{},void 0,!0)],2)):(a(),$(w,{key:1}))}}});const sr=m(tr,[["__scopeId","data-v-18c0e4a1"]]);const or={Layout:sr,enhanceApp:({app:s})=>{s.component("Badge",Ze)}};function ar(s,e){const{localeIndex:t}=V();function n(o){var k,M;const l=o.split("."),d=s&&typeof s=="object",p=d&&((M=(k=s.locales)==null?void 0:k[t.value])==null?void 0:M.translations)||null,_=d&&s.translations||null;let b=p,w=_,S=e;const B=l.pop();for(const N of l){let y=null;const D=S==null?void 0:S[N];D&&(y=S=D);const O=w==null?void 0:w[N];O&&(y=w=O);const E=b==null?void 0:b[N];E&&(y=b=E),D||(S=y),O||(w=y),E||(b=y)}return(b==null?void 0:b[B])??(w==null?void 0:w[B])??(S==null?void 0:S[B])??""}return n}export{ar as c,or as t,V as u}; diff --git a/assets/chunks/vue-kakuyaku.a530ead7.js b/assets/chunks/vue-kakuyaku.a530ead7.js new file mode 100644 index 000000000..8c2d3cd55 --- /dev/null +++ b/assets/chunks/vue-kakuyaku.a530ead7.js @@ -0,0 +1 @@ +import{x as s,ai as f,g as a,aj as o,ak as c,E as i,al as d,am as p,an as m}from"./framework.1293becd.js";function R(e,n,l){return s(()=>e.fulfilled,u=>u&&n(u.value),l)}function S(e,n,l){return s(()=>e.rejected,u=>u&&n(u.reason),l)}function y(){let e=null;const n=f({pending:!1,fulfilled:null,rejected:null});function l(t){e=t,n.pending=!0,n.fulfilled=null,n.rejected=null,t.then(r=>{t===e&&(n.pending=!1,n.fulfilled=o({value:r}))}).catch(r=>{t===e&&(n.pending=!1,n.rejected=o({reason:r}))})}function u(){e=null,n.pending=!1,n.fulfilled=n.rejected=null}return{state:n,set:l,clear:u}}function P(e,n){const{state:l,set:u,clear:t}=y(),r=()=>u(e());return n!=null&&n.immediate&&r(),{state:l,run:r,clear:t}}function v(){const e=m()||c();let n=null;const l=i(null);function u(r){t(),e.run(()=>{n=c(),n.run(()=>{l.value={expose:r()}})})}function t(){n&&(n.stop(),n=l.value=null)}return{setup:u,dispose:t,scope:d(l)}}function g(e){const n=typeof e;return e===!0||n==="string"||n==="number"||n==="symbol"}function h(e){return g(e)?e:e.key}function j(e){return typeof e=="object"?e:{key:e}}function w(e){return e==null||e===!1?null:{some:e}}function T(e,n){const l=v(),u=a(()=>{const t=p(e)?e.value:e();return w(t)});return s(()=>{const t=u.value;return t?h(t.some):null},t=>{t?l.setup(()=>{if(t===!0)return{expose:n(!0)};const r=u.value.some;return{expose:n(r),...j(r)}}):l.dispose()},{immediate:!0}),a(()=>{var t;return((t=l.scope.value)==null?void 0:t.expose)??null})}export{T as a,y as b,S as c,P as u,R as w}; diff --git a/assets/documenting_snippets.md.12d116dc.js b/assets/documenting_snippets.md.12d116dc.js new file mode 100644 index 000000000..d763db8b9 --- /dev/null +++ b/assets/documenting_snippets.md.12d116dc.js @@ -0,0 +1,73 @@ +import{_ as s,o as a,c as n,Q as e}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Code Snippets","description":"","frontmatter":{},"headers":[],"relativePath":"documenting/snippets.md","filePath":"documenting/snippets.md","lastUpdated":1700563625000}'),p={name:"documenting/snippets.md"},l=e(`

Code Snippets

To make code snippets in the documentation more "real" and robust, it is better to fetch them directly from the source files. The sources are located in other repositories, where they are built, run, and tested.

How it works

Snippet Sources

Snippet sources are defined in snippet_sources.ts. The snippet_sources.ts file is located at the documentation repository and has the following format:

ts
export default [
+  {
+    src: 'https://raw.githubusercontent.com/hyperledger/iroha/iroha2-stable/MAINTAINERS.md',
+    filename: 'iroha-maintainers-at-stable.md',
+  },
+  {
+    src: './src/example_code/lorem.rs',
+  },
+]
export default [
+  {
+    src: 'https://raw.githubusercontent.com/hyperledger/iroha/iroha2-stable/MAINTAINERS.md',
+    filename: 'iroha-maintainers-at-stable.md',
+  },
+  {
+    src: './src/example_code/lorem.rs',
+  },
+]
  • src defines the source file location and could be either an HTTP(s) URI or a relative file path.
  • filename (optional) explicitly defines the local filename.

Fetching Snippets

Code snippets are fetched from the locations specified in snippet_sources.ts and written into the /src/snippets directory. There are two ways to fetch the snippets:

  • Automatically after dependencies were installed (i.e. pnpm install)
  • Manually by calling pnpm get-snippets

TIP

By default, snippets are deleted and reloaded each time pnpm get-snippets is called. For local development it might be more convenient to enable "lazy" behavior by calling pnpm get-snippets --force false.

Using Snippets in Markdown

Use Code Snippets feature in VitePress to include snippets into documentation:

Input

md
<<<@/snippets/lorem.rs
+
+<<<@/snippets/lorem.rs#ipsum
<<<@/snippets/lorem.rs
+
+<<<@/snippets/lorem.rs#ipsum

Output

rs
fn main() {
+    // #region ipsum
+    println!("Lorem ipsum");
+    // #endregion ipsum
+}
fn main() {
+    // #region ipsum
+    println!("Lorem ipsum");
+    // #endregion ipsum
+}
rs
println!("Lorem ipsum");
println!("Lorem ipsum");

Note that we included only the #ipsum code region, not the entire file. This feature is essential when it comes to including code from real source files into the documentation.

Example

Let's add a code snippet from Iroha JavaScript SDK. For example, this one: /packages/docs-recipes/src/1.client-install.ts.

  1. First, get a permalink to the file. Open the file on GitHub and click Raw button to get the link. For example: https://raw.githubusercontent.com/hyperledger/iroha-javascript/e300886e76c777776efad1e2f5cb245bfb8ed02e/packages/docs-recipes/src/1.client-install.ts

  2. Define the new snippet in the Snippet Sources:

    ts
    export default [
    +  /// ...
    +
    +  {
    +    src: 'https://raw.githubusercontent.com/hyperledger/iroha-javascript/e300886e76c777776efad1e2f5cb245bfb8ed02e/packages/docs-recipes/src/1.client-install.ts',
    +    filename: 'js-sdk-1-client-install.ts',
    +  },
    +]
    export default [
    +  /// ...
    +
    +  {
    +    src: 'https://raw.githubusercontent.com/hyperledger/iroha-javascript/e300886e76c777776efad1e2f5cb245bfb8ed02e/packages/docs-recipes/src/1.client-install.ts',
    +    filename: 'js-sdk-1-client-install.ts',
    +  },
    +]

    TIP

    Since snippet_sources.ts is a TypeScript file, we can use all scripting features in it. Meanwhile, we are trying to keep it as simple as possible, so even the one who doesn't know TypeScript at all could edit it.

    However, we use a bit of scripting. We defined several constants with git revisions from multiple repositories:

    ts
    const IROHA_REV_STABLE = 'c4af68c4f7959b154eb5380aa93c894e2e63fe4e'
    +
    +const IROHA_REV_DEV = '...'
    +
    +const IROHA_JS_REV = '...'
    const IROHA_REV_STABLE = 'c4af68c4f7959b154eb5380aa93c894e2e63fe4e'
    +
    +const IROHA_REV_DEV = '...'
    +
    +const IROHA_JS_REV = '...'

    Then we use them in links to snippet sources in place of git revisions, like this:

    ts
    export default [
    +  // ...
    +
    +  {
    +    src: \`https://raw.githubusercontent.com/hyperledger/iroha/\${IROHA_REV_STABLE}/MAINTAINERS.md\`,
    +    //                                                        ^^^^^^^^^^^^^^^^^^^
    +    filename: 'iroha-maintainers-at-stable.md',
    +  },
    +]
    export default [
    +  // ...
    +
    +  {
    +    src: \`https://raw.githubusercontent.com/hyperledger/iroha/\${IROHA_REV_STABLE}/MAINTAINERS.md\`,
    +    //                                                        ^^^^^^^^^^^^^^^^^^^
    +    filename: 'iroha-maintainers-at-stable.md',
    +  },
    +]

    It helps us to reduce repetitions and keep sources clean.

  3. Include the snippet in any Markdown file in the documentation as follows:

    Input

    md
    <<<@/snippets/js-sdk-1-client-install.ts
    <<<@/snippets/js-sdk-1-client-install.ts

    Output

    ts
    import { crypto } from '@iroha2/crypto-target-node'
    +import { setCrypto } from '@iroha2/client'
    +
    +setCrypto(crypto)
    import { crypto } from '@iroha2/crypto-target-node'
    +import { setCrypto } from '@iroha2/client'
    +
    +setCrypto(crypto)
`,22),o=[l];function t(c,r,i,d,E,y){return a(),n("div",null,o)}const m=s(p,[["render",t]]);export{u as __pageData,m as default}; diff --git a/assets/documenting_snippets.md.12d116dc.lean.js b/assets/documenting_snippets.md.12d116dc.lean.js new file mode 100644 index 000000000..f9df79456 --- /dev/null +++ b/assets/documenting_snippets.md.12d116dc.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as e}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Code Snippets","description":"","frontmatter":{},"headers":[],"relativePath":"documenting/snippets.md","filePath":"documenting/snippets.md","lastUpdated":1700563625000}'),p={name:"documenting/snippets.md"},l=e("",22),o=[l];function t(c,r,i,d,E,y){return a(),n("div",null,o)}const m=s(p,[["render",t]]);export{u as __pageData,m as default}; diff --git a/assets/ffi.5bfc24bc.png b/assets/ffi.5bfc24bc.png new file mode 100644 index 000000000..36e9b5388 Binary files /dev/null and b/assets/ffi.5bfc24bc.png differ diff --git a/assets/guide_advanced_hot-reload.md.d25b5901.js b/assets/guide_advanced_hot-reload.md.d25b5901.js new file mode 100644 index 000000000..152b3c3d9 --- /dev/null +++ b/assets/guide_advanced_hot-reload.md.d25b5901.js @@ -0,0 +1 @@ +import{_ as s,o,c as e,Q as a}from"./chunks/framework.1293becd.js";const E=JSON.parse('{"title":"How to hot reload Iroha in a Docker container","description":"","frontmatter":{},"headers":[],"relativePath":"guide/advanced/hot-reload.md","filePath":"guide/advanced/hot-reload.md","lastUpdated":1700563625000}'),n={name:"guide/advanced/hot-reload.md"},t=a('

How to hot reload Iroha in a Docker container

Here is the overall procedure for hot reloading Iroha in a Docker container:

  1. Build Iroha on your host OS.

    To avoid issues with dynamic linking, run:

    bash
    $ cargo build --release --target x86_64-unknown-linux-musl --features "vendored"
    $ cargo build --release --target x86_64-unknown-linux-musl --features "vendored"
    An explanation for using `cargo build` with these parameters.

    You may experience an issue with dynamic linking if your host OS has a newer version of glibc compared to the one in the Docker container. The options used in the command above resolve the issue:

    • --target x86_64-unknown-linux-musl forces static linking against musl libc implementation
    • --features "vendored" facilitates static linkage of the openssl library
  2. Enter Docker container. For example:

    bash
    $ docker exec -it iroha-iroha0-1 bash
    $ docker exec -it iroha-iroha0-1 bash
  3. Copy Iroha to the current directory:

    bash
    $ docker cp root/soramitsu/iroha/target/x86_64-unknown-linux-musl/release/iroha .
    $ docker cp root/soramitsu/iroha/target/x86_64-unknown-linux-musl/release/iroha .
  4. (Optional) Make any modifications you need:

  5. Exit docker container and restart it using docker restart.

    Note: If you use the combination of container down and container up, any modifications you made on the previous step will be lost. Use docker restart to preserve changes.

If you skip the optional step (step 4), the state of the blockchain after hot reload will be the same as it was before the Docker container was restarted.

Note that if you get the Kura initialisation failed error message, it might mean one of two things: corruption or binary incompatibility of the stored block. To fix this, remove the blocks/ directory.

Wiping previous blockchain state (recommit genesis)

To recommit a custom genesis block, remove the previously stored blocks before restarting the container:

bash
$ rm blocks/*
$ rm blocks/*

The new genesis block will be automatically recommited upon container restart.

Use custom configuration files

To use custom configuration files, such as config.json or genesis.json, copy (or bind mount) them to the config/ subvolume before restarting the Docker container.

The changes will take effect upon container restart.

Use custom environment variables

To use custom environment variables (e.g. IROHA_PUBLIC_KEY), simply modify them before restarting the Docker container. For example:

bash
$ IROHA_PUBLIC_KEY=<new_key> docker restart
$ IROHA_PUBLIC_KEY=<new_key> docker restart

The changes will take effect upon container restart.

',16),l=[t];function r(p,c,i,d,h,u){return o(),e("div",null,l)}const m=s(n,[["render",r]]);export{E as __pageData,m as default}; diff --git a/assets/guide_advanced_hot-reload.md.d25b5901.lean.js b/assets/guide_advanced_hot-reload.md.d25b5901.lean.js new file mode 100644 index 000000000..2dbd05051 --- /dev/null +++ b/assets/guide_advanced_hot-reload.md.d25b5901.lean.js @@ -0,0 +1 @@ +import{_ as s,o,c as e,Q as a}from"./chunks/framework.1293becd.js";const E=JSON.parse('{"title":"How to hot reload Iroha in a Docker container","description":"","frontmatter":{},"headers":[],"relativePath":"guide/advanced/hot-reload.md","filePath":"guide/advanced/hot-reload.md","lastUpdated":1700563625000}'),n={name:"guide/advanced/hot-reload.md"},t=a("",16),l=[t];function r(p,c,i,d,h,u){return o(),e("div",null,l)}const m=s(n,[["render",r]]);export{E as __pageData,m as default}; diff --git a/assets/guide_advanced_metrics.md.66cfb76b.js b/assets/guide_advanced_metrics.md.66cfb76b.js new file mode 100644 index 000000000..ffa25724c --- /dev/null +++ b/assets/guide_advanced_metrics.md.66cfb76b.js @@ -0,0 +1,29 @@ +import{_ as s,o as a,c as n,Q as e}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Metrics","description":"","frontmatter":{},"headers":[],"relativePath":"guide/advanced/metrics.md","filePath":"guide/advanced/metrics.md","lastUpdated":1700563625000}'),o={name:"guide/advanced/metrics.md"},l=e(`

Metrics

To conveniently and thoroughly monitor the performance of the network, we recommend using prometheus. Prometheus is a program that can monitor your Iroha peer over a separate socket and provide different kinds of performance metrics.

This data can help you find performance bottlenecks and optimise your Iroha configuration.

How to use metrics

To use metrics, you need to configure the /metrics endpoint in the Iroha configuration. By default, the endpoint is exposed at 127.0.0.1:8180/metrics. If the port is not available, Iroha will still start and work normally, but metrics won't be accessible.

After that, use the IP address to access the data from the running Iroha instance. For example:

bash
$ curl http://127.0.0.1:8080/metrics
$ curl http://127.0.0.1:8080/metrics

This will give you a result like this:

bash
# HELP blocks_height Total number of blocks in chain
+# TYPE blocks_height gauge
+blocks_height 135543
+# HELP peers_number Total number peers to send transactions and request proposals
+# TYPE peers_number gauge
+peers_number 7
+# HELP number_of_domains Total number of domains in WSV
+# TYPE number_of_domains gauge
+number_of_domains 14
+# HELP total_number_of_transactions Total number of transactions in blockchain
+# TYPE total_number_of_transactions gauge
+total_number_of_transactions 216499
+# HELP number_of_signatures_in_last_block Number of signatures in last block
+# TYPE number_of_signatures_in_last_block gauge
+number_of_signatures_in_last_block 5
# HELP blocks_height Total number of blocks in chain
+# TYPE blocks_height gauge
+blocks_height 135543
+# HELP peers_number Total number peers to send transactions and request proposals
+# TYPE peers_number gauge
+peers_number 7
+# HELP number_of_domains Total number of domains in WSV
+# TYPE number_of_domains gauge
+number_of_domains 14
+# HELP total_number_of_transactions Total number of transactions in blockchain
+# TYPE total_number_of_transactions gauge
+total_number_of_transactions 216499
+# HELP number_of_signatures_in_last_block Number of signatures in last block
+# TYPE number_of_signatures_in_last_block gauge
+number_of_signatures_in_last_block 5

/metrics endpoint

Refer to the API specification.

`,11),t=[l];function p(r,c,i,d,u,_){return a(),n("div",null,t)}const b=s(o,[["render",p]]);export{m as __pageData,b as default}; diff --git a/assets/guide_advanced_metrics.md.66cfb76b.lean.js b/assets/guide_advanced_metrics.md.66cfb76b.lean.js new file mode 100644 index 000000000..b84951fbb --- /dev/null +++ b/assets/guide_advanced_metrics.md.66cfb76b.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as e}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Metrics","description":"","frontmatter":{},"headers":[],"relativePath":"guide/advanced/metrics.md","filePath":"guide/advanced/metrics.md","lastUpdated":1700563625000}'),o={name:"guide/advanced/metrics.md"},l=e("",11),t=[l];function p(r,c,i,d,u,_){return a(),n("div",null,t)}const b=s(o,[["render",p]]);export{m as __pageData,b as default}; diff --git a/assets/guide_advanced_running-iroha-on-bare-metal.md.552acb26.js b/assets/guide_advanced_running-iroha-on-bare-metal.md.552acb26.js new file mode 100644 index 000000000..931882544 --- /dev/null +++ b/assets/guide_advanced_running-iroha-on-bare-metal.md.552acb26.js @@ -0,0 +1,251 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const e="/iroha-2-docs/assets/appendix_running-iroha_cli-output.159236c6.png",f=JSON.parse('{"title":"Iroha on bare metal","description":"","frontmatter":{},"headers":[],"relativePath":"guide/advanced/running-iroha-on-bare-metal.md","filePath":"guide/advanced/running-iroha-on-bare-metal.md","lastUpdated":1700563625000}'),l={name:"guide/advanced/running-iroha-on-bare-metal.md"},p=o(`

Iroha on bare metal

What we are going to do is replicate the setup that we have in docker compose and run Iroha directly, without going through the intermediary of containers. Running Iroha on bare metal involves manipulating files and/or environment variables.

The file-based approach is the easiest to get right. Using environment variables can offer a better user experience if done right, but is more error-prone, particularly for exotic systems (Windows).

INFO

For this chapter, we assume you have learned about configuration and management in Iroha 2. Here we offer you instructions to run Iroha on bare metal without going into details about various configuration options available.

You can always check sample configuration files for configs/peer/genesis.json and configs/peer/config.json, or refer to peer configuration options for more details.

The complete list of options is available in the Iroha Configuration Reference.

Prerequisites

First of all, we should note that we have only built the Iroha client so far in this tutorial. We also need to build the peer software to run Iroha on bare metal.

INFO

Building in debug mode retains much more information and optimises the binary to a far lesser extent. As such, we advise you to build Iroha in debug mode for testing: it’s faster and it makes it easier for you to find issues and fix them. However, if you intend to actually deploy Iroha, you should build it in --release mode.

  • To build the peer software in debug mode, run:

    kotlin
    cargo build -p iroha
    cargo build -p iroha
  • To build the peer software in release mode, run:

    kotlin
    cargo build --bin iroha --release
    cargo build --bin iroha --release

    The release mode binary takes significantly longer to compile than debug mode, but the result is a smaller and faster binary, suitable for deployment in the actual blockchains.

Setup

Setup: Environment variables

We want to make sure that we have the right configuration.

There are different ways to do this. You can copy the contents of the ~/Git/iroha/configs/peer/ into a new directory, or, alternatively, just run all commands from that directory:

bash
$ cd ~/Git/iroha/configs/peer
$ cd ~/Git/iroha/configs/peer

The third option is to specify the full path to the configuration file in an environment variable. For simplicity, we shall do the latter:

bash
$ cd ~
+$ export IROHA2_GENESIS_PATH="$(pwd)/Git/iroha/configs/peer/genesis.json"
+$ export IROHA2_CONFIG_PATH="$(pwd)/Git/iroha/configs/peer/config.json"
$ cd ~
+$ export IROHA2_GENESIS_PATH="$(pwd)/Git/iroha/configs/peer/genesis.json"
+$ export IROHA2_CONFIG_PATH="$(pwd)/Git/iroha/configs/peer/config.json"

For extra convenience, you could add the Iroha 2 target directory to your PATH. This means that instead of having to specify the full path to the executable iroha, you can instead type iroha directly into your command line.

bash
$ export PATH="$PATH:$(pwd)/Git/iroha/target/debug"
$ export PATH="$PATH:$(pwd)/Git/iroha/target/debug"

TIP

Don’t forget to replace debug with release when you’re ready to deploy in the real world.

This way you can run iroha from any directory without having to worry about configuration paths and/or specifying the full path to the Iroha executable.

It is a good idea to make these instructions permanent, which you can do by adding the environment variables to your startup shell.

Save the instructions to the startup shell

On older Linux systems, you copy and paste the instructions (without the cd ~) to ~/.bashrc. On Mac OS X 10.6 and later, as well as some Linux systems, you want to add the same lines to ~/.zshrc.

Copy these instructions to the specified files (replace debug with release when you are ready to deploy):

bash
$ export IROHA2_GENESIS_PATH="$(pwd)/Git/iroha/configs/peer/genesis.json"
+$ export IROHA2_CONFIG_PATH="$(pwd)/Git/iroha/configs/peer/config.json"
+$ export PATH="$PATH:$(pwd)/Git/iroha/target/debug"
$ export IROHA2_GENESIS_PATH="$(pwd)/Git/iroha/configs/peer/genesis.json"
+$ export IROHA2_CONFIG_PATH="$(pwd)/Git/iroha/configs/peer/config.json"
+$ export PATH="$PATH:$(pwd)/Git/iroha/target/debug"

TIP

This process is almost universally unreliable and messy, and it is likely that your system is special in that it breaks some of our assumptions.

If the above optional steps didn’t work for you, you can keep working in the ~/Git/iroha/configs/peer/ folder, and run Iroha via ~/Git/iroha/target/debug/iroha.

This makes the command-line a little harder to read, which is why we recommend setting up your environment first.

Note

The tutorial assumes that you’re running either Linux, Mac OS X, or Windows using WSL. It should be possible to run directly on Windows, but that is neither recommended nor easy. If you don’t want to use a Unix-like system, we suggest that you wait until we publish a detailed guide for Windows users.

Setup: Files

This is the recommended method of bringing up an Iroha peer. What we do is:

  1. Create a new directory for the configuration files:

    bash
    $ mkdir -p ~/Git/iroha/deploy
    $ mkdir -p ~/Git/iroha/deploy
  2. Copy the peer configuration into it:

    bash
    $ cp -vfr ~/Git/iroha/configs/peer/*.json ~/Git/iroha/deploy
    $ cp -vfr ~/Git/iroha/configs/peer/*.json ~/Git/iroha/deploy
  3. Copy the respective Iroha binary into your binary folder:

    bash
    $ sudo cp -vfr ~/Git/iroha/target/debug/iroha /usr/bin/
    $ sudo cp -vfr ~/Git/iroha/target/debug/iroha /usr/bin/

    which will install Iroha 2 system wide.

TIP

You could also use the iroha peer binary locally by copying it into the same folder. The only difference would be that you’d be calling Iroha like so: ./iroha instead of iroha.

First run of Iroha on bare metal

If you’ve done everything correctly, you can now do

bash
$ iroha
$ iroha

to start your first peer and be greeted with

Untitled

This means that everything is working, but also that we need to do some more work.

You have just started a single peer, which can tolerate exactly 0 faults. Running two peers is also possible, but again, can tolerate 0 faults. You must run at least 4 peers in order to have the capacity to tolerate at least one fault.

In general, if you want to be resistant to f faults, you want to have 3f+1 peers: (4, 7, 10, etc.).

You cannot really start the peers in any way you want, though. When we started our original peer, in its configuration, we specified that it has to trust very specific peers, which have the given private key and listen on a specific address. In order to know how to run them appropriately, take a look at docker-compose.yml:

docker-compose.yaml
yaml
version: '3.8'
+services:
+  iroha0:
+    image: hyperledger/iroha2:dev
+    environment:
+      TORII_P2P_ADDR: iroha0:1337
+      TORII_API_URL: iroha0:8080
+      TORII_TELEMETRY_URL: iroha0:8180
+      IROHA_PUBLIC_KEY: 'ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b'
+      IROHA_PRIVATE_KEY:
+        '{"digest_function": "ed25519", "payload":
+        "282ed9f3cf92811c3818dbc4ae594ed59dc1a2f78e4241e31924e101d6b1fb831c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"}'
+      SUMERAGI_TRUSTED_PEERS:
+        '[{"address":"iroha0:1337", "public_key":
+        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
+        {"address":"iroha1:1338", "public_key":
+        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
+        {"address": "iroha2:1339", "public_key":
+        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
+        {"address": "iroha3:1340", "public_key":
+        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
+      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
+      IROHA_GENESIS_ACCOUNT_PRIVATE_KEY:
+        '{ "digest_function": "ed25519", "payload":
+        "038ae16b219da35aa036335ed0a43c28a2cc737150112c78a7b8034b9d99c9023f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255"
+        }'
+    ports:
+      - '1337:1337'
+      - '8080:8080'
+      - '8180:8180'
+    volumes:
+      - './configs/peer:/config'
+    init: true
+    command: iroha --submit-genesis
+
+  iroha1:
+    image: hyperledger/iroha2:dev
+    environment:
+      TORII_P2P_ADDR: iroha1:1338
+      TORII_API_URL: iroha1:8081
+      TORII_TELEMETRY_URL: iroha1:8181
+      IROHA_PUBLIC_KEY: 'ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1'
+      IROHA_PRIVATE_KEY:
+        '{"digest_function": "ed25519", "payload":
+        "3bac34cda9e3763fa069c1198312d1ec73b53023b8180c822ac355435edc4a24cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"}'
+      SUMERAGI_TRUSTED_PEERS:
+        '[{"address":"iroha0:1337", "public_key":
+        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
+        {"address":"iroha1:1338", "public_key":
+        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
+        {"address": "iroha2:1339", "public_key":
+        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
+        {"address": "iroha3:1340", "public_key":
+        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
+      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
+    ports:
+      - '1338:1338'
+      - '8081:8081'
+      - '8181:8181'
+    volumes:
+      - './configs/peer:/config'
+    init: true
+
+  iroha2:
+    image: hyperledger/iroha2:dev
+    environment:
+      TORII_P2P_ADDR: iroha2:1339
+      TORII_API_URL: iroha2:8082
+      TORII_TELEMETRY_URL: iroha2:8182
+      IROHA_PUBLIC_KEY: 'ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020'
+      IROHA_PRIVATE_KEY:
+        '{"digest_function": "ed25519", "payload":
+        "1261a436d36779223d7d6cf20e8b644510e488e6a50bafd77a7485264d27197dfaca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"}'
+      SUMERAGI_TRUSTED_PEERS:
+        '[{"address":"iroha0:1337", "public_key":
+        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
+        {"address":"iroha1:1338", "public_key":
+        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
+        {"address": "iroha2:1339", "public_key":
+        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
+        {"address": "iroha3:1340", "public_key":
+        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
+      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
+    ports:
+      - '1339:1339'
+      - '8082:8082'
+      - '8182:8182'
+    volumes:
+      - './configs/peer:/config'
+    init: true
+
+  iroha3:
+    image: hyperledger/iroha2:dev
+    environment:
+      TORII_P2P_ADDR: iroha3:1340
+      TORII_API_URL: iroha3:8083
+      TORII_TELEMETRY_URL: iroha3:8183
+      IROHA_PUBLIC_KEY: 'ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f'
+      IROHA_PRIVATE_KEY:
+        '{"digest_function": "ed25519", "payload":
+        "a70dab95c7482eb9f159111b65947e482108cfe67df877bd8d3b9441a781c7c98e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}'
+      SUMERAGI_TRUSTED_PEERS:
+        '[{"address":"iroha0:1337", "public_key":
+        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
+        {"address":"iroha1:1338", "public_key":
+        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
+        {"address": "iroha2:1339", "public_key":
+        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
+        {"address": "iroha3:1340", "public_key":
+        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
+      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
+    ports:
+      - '1340:1340'
+      - '8083:8083'
+      - '8183:8183'
+    volumes:
+      - './configs/peer:/config'
+    init: true
version: '3.8'
+services:
+  iroha0:
+    image: hyperledger/iroha2:dev
+    environment:
+      TORII_P2P_ADDR: iroha0:1337
+      TORII_API_URL: iroha0:8080
+      TORII_TELEMETRY_URL: iroha0:8180
+      IROHA_PUBLIC_KEY: 'ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b'
+      IROHA_PRIVATE_KEY:
+        '{"digest_function": "ed25519", "payload":
+        "282ed9f3cf92811c3818dbc4ae594ed59dc1a2f78e4241e31924e101d6b1fb831c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"}'
+      SUMERAGI_TRUSTED_PEERS:
+        '[{"address":"iroha0:1337", "public_key":
+        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
+        {"address":"iroha1:1338", "public_key":
+        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
+        {"address": "iroha2:1339", "public_key":
+        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
+        {"address": "iroha3:1340", "public_key":
+        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
+      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
+      IROHA_GENESIS_ACCOUNT_PRIVATE_KEY:
+        '{ "digest_function": "ed25519", "payload":
+        "038ae16b219da35aa036335ed0a43c28a2cc737150112c78a7b8034b9d99c9023f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255"
+        }'
+    ports:
+      - '1337:1337'
+      - '8080:8080'
+      - '8180:8180'
+    volumes:
+      - './configs/peer:/config'
+    init: true
+    command: iroha --submit-genesis
+
+  iroha1:
+    image: hyperledger/iroha2:dev
+    environment:
+      TORII_P2P_ADDR: iroha1:1338
+      TORII_API_URL: iroha1:8081
+      TORII_TELEMETRY_URL: iroha1:8181
+      IROHA_PUBLIC_KEY: 'ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1'
+      IROHA_PRIVATE_KEY:
+        '{"digest_function": "ed25519", "payload":
+        "3bac34cda9e3763fa069c1198312d1ec73b53023b8180c822ac355435edc4a24cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"}'
+      SUMERAGI_TRUSTED_PEERS:
+        '[{"address":"iroha0:1337", "public_key":
+        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
+        {"address":"iroha1:1338", "public_key":
+        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
+        {"address": "iroha2:1339", "public_key":
+        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
+        {"address": "iroha3:1340", "public_key":
+        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
+      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
+    ports:
+      - '1338:1338'
+      - '8081:8081'
+      - '8181:8181'
+    volumes:
+      - './configs/peer:/config'
+    init: true
+
+  iroha2:
+    image: hyperledger/iroha2:dev
+    environment:
+      TORII_P2P_ADDR: iroha2:1339
+      TORII_API_URL: iroha2:8082
+      TORII_TELEMETRY_URL: iroha2:8182
+      IROHA_PUBLIC_KEY: 'ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020'
+      IROHA_PRIVATE_KEY:
+        '{"digest_function": "ed25519", "payload":
+        "1261a436d36779223d7d6cf20e8b644510e488e6a50bafd77a7485264d27197dfaca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"}'
+      SUMERAGI_TRUSTED_PEERS:
+        '[{"address":"iroha0:1337", "public_key":
+        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
+        {"address":"iroha1:1338", "public_key":
+        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
+        {"address": "iroha2:1339", "public_key":
+        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
+        {"address": "iroha3:1340", "public_key":
+        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
+      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
+    ports:
+      - '1339:1339'
+      - '8082:8082'
+      - '8182:8182'
+    volumes:
+      - './configs/peer:/config'
+    init: true
+
+  iroha3:
+    image: hyperledger/iroha2:dev
+    environment:
+      TORII_P2P_ADDR: iroha3:1340
+      TORII_API_URL: iroha3:8083
+      TORII_TELEMETRY_URL: iroha3:8183
+      IROHA_PUBLIC_KEY: 'ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f'
+      IROHA_PRIVATE_KEY:
+        '{"digest_function": "ed25519", "payload":
+        "a70dab95c7482eb9f159111b65947e482108cfe67df877bd8d3b9441a781c7c98e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}'
+      SUMERAGI_TRUSTED_PEERS:
+        '[{"address":"iroha0:1337", "public_key":
+        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
+        {"address":"iroha1:1338", "public_key":
+        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
+        {"address": "iroha2:1339", "public_key":
+        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
+        {"address": "iroha3:1340", "public_key":
+        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
+      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
+    ports:
+      - '1340:1340'
+      - '8083:8083'
+      - '8183:8183'
+    volumes:
+      - './configs/peer:/config'
+    init: true

For every peer, the environment section is a set of things that you should put in front of the iroha command, replacing colons with equals signs. All the socket addresses are also given internal to the docker network, so we should replace them with [localhost](http://localhost), which is 127.0.0.1 on most machines.

TIP

Each Iroha instance is going to listen on three ports: the Peer-to-peer communications channel (133X), the API url, where most client requests are posted (808X), and finally, a telemetry endpoint 818X. All three ports need to be adjusted so there are no collisions. See the docker-compose.yml for an example, and adjust as needed.

Deploy a minimal BFT network

Both of there approaches are messy and error-prone, which is why the tutorial uses docker-compose. However, this brings you closer to the experience of actually maintaining a functional Iroha peer.

Using Environment Variables

To run the First peer, we need to write

bash
$ TORII_P2P_ADDR="127.0.0.1:1337"
+$ TORII_API_URL="127.0.0.1:8080"
+$ TORII_STATUS_URL="127.0.0.1:8180"
+$ IROHA_PUBLIC_KEY="ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" IROHA_PRIVATE_KEY='{"digest_function": "ed25519", "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"}'
+$ iroha --submit
$ TORII_P2P_ADDR="127.0.0.1:1337"
+$ TORII_API_URL="127.0.0.1:8080"
+$ TORII_STATUS_URL="127.0.0.1:8180"
+$ IROHA_PUBLIC_KEY="ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" IROHA_PRIVATE_KEY='{"digest_function": "ed25519", "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"}'
+$ iroha --submit

and three other similar lines of bash code for the remaining deployments.

TIP

To copy and paste into the terminal on Linux systems, you should remember that Control + Shift + V is the appropriate paste shortcut.

Also note that we asked this peer to --submit or --submit-genesis. This means that in the initial network topology, this peer is the leader. At least one peer (usually the first) needs to be the leader in the initial topology.

Now you should do the same for the other four peers. Be mindful not to mix up which address goes where, replace irohaX with 127.0.0.1 in the addresses, and make sure that they correspond to the right public key.

Using Files

Our first peer can run off of the original configuration file. What we should do is create three more similar files and move them to three different folders e.g. peer1, peer2.

What you need to do is change the TORII:P2P_ADDR, TORII:API_URLTORII:STATUS_URL and the PUBLIC_KEY configuration options to align with their docker-compose.yml counterparts.

Be mindful not to mix up which address goes where, replace irohaX with 127.0.0.1 in the addresses, and make sure that they correspond to the right public key.

Then, in each of the new folders (with the exception of peer0) run:

bash
$ iroha
$ iroha

In the first folder peer0 you should run:

bash
$ iroha --submit-genesis
$ iroha --submit-genesis

We effectively asked this peer to --submit or --submit-genesis in the initial, or bootstrap, network. This means that in the initial network topology, this peer is the leader.

Note

Only the leader of the genesis network needs to have access to configs/peer/genesis.json. Having the same genesis in the initial folders of the other peers could be useful, since future versions of iroha will also sanity-check the genesis blocks.

If all went well, you should be greeted with nice logs on each of the nodes, and the nodes should commit the blocks to the blocks/ directory.

Real-world deployment

Suppose now, that you have done all of the tinkering and want to deploy Iroha in the real world.

  1. Build Iroha in release mode:

    bash
    $ cargo build --release
    $ cargo build --release
  2. Generate a key pair for your peer and take note of that key:

    bash
    $ cargo run --bin iroha_crypto_cli
    $ cargo run --bin iroha_crypto_cli
  3. Register your peer to a network, and make sure to add at least four of the peers on that network to the TRUSTED_PEERS array in your configuration file.

  4. Determine the web socket that the other peers will use to connect to you. Make sure that the port is open and use that address (P2P_ADDR) in your configs/peer/config.json file.

  5. After you have finished editing the configuration file, deploy Iroha by running

    bash
    $ ~/Git/iroha/target/release/iroha
    $ ~/Git/iroha/target/release/iroha

Note

There is no need to pass the --submit flag unless you are starting the initial peer on the network.

`,63),t=[p];function c(r,i,d,y,E,u){return a(),n("div",null,t)}const b=s(l,[["render",c]]);export{f as __pageData,b as default}; diff --git a/assets/guide_advanced_running-iroha-on-bare-metal.md.552acb26.lean.js b/assets/guide_advanced_running-iroha-on-bare-metal.md.552acb26.lean.js new file mode 100644 index 000000000..4da76f321 --- /dev/null +++ b/assets/guide_advanced_running-iroha-on-bare-metal.md.552acb26.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const e="/iroha-2-docs/assets/appendix_running-iroha_cli-output.159236c6.png",f=JSON.parse('{"title":"Iroha on bare metal","description":"","frontmatter":{},"headers":[],"relativePath":"guide/advanced/running-iroha-on-bare-metal.md","filePath":"guide/advanced/running-iroha-on-bare-metal.md","lastUpdated":1700563625000}'),l={name:"guide/advanced/running-iroha-on-bare-metal.md"},p=o("",63),t=[p];function c(r,i,d,y,E,u){return a(),n("div",null,t)}const b=s(l,[["render",c]]);export{f as __pageData,b as default}; diff --git a/assets/guide_blockchain_accounts.md.62495945.js b/assets/guide_blockchain_accounts.md.62495945.js new file mode 100644 index 000000000..352718816 --- /dev/null +++ b/assets/guide_blockchain_accounts.md.62495945.js @@ -0,0 +1 @@ +import{_ as t,o as a,c,k as e,a as o}from"./chunks/framework.1293becd.js";const k=JSON.parse('{"title":"Accounts","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/accounts.md","filePath":"guide/blockchain/accounts.md","lastUpdated":1700563625000}'),s={name:"guide/blockchain/accounts.md"},n=e("h1",{id:"accounts",tabindex:"-1"},[o("Accounts "),e("a",{class:"header-anchor",href:"#accounts","aria-label":'Permalink to "Accounts"'},"​")],-1),r=e("p",null,"TBD",-1),d=[n,r];function i(l,u,_,h,p,m){return a(),c("div",null,d)}const b=t(s,[["render",i]]);export{k as __pageData,b as default}; diff --git a/assets/guide_blockchain_accounts.md.62495945.lean.js b/assets/guide_blockchain_accounts.md.62495945.lean.js new file mode 100644 index 000000000..352718816 --- /dev/null +++ b/assets/guide_blockchain_accounts.md.62495945.lean.js @@ -0,0 +1 @@ +import{_ as t,o as a,c,k as e,a as o}from"./chunks/framework.1293becd.js";const k=JSON.parse('{"title":"Accounts","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/accounts.md","filePath":"guide/blockchain/accounts.md","lastUpdated":1700563625000}'),s={name:"guide/blockchain/accounts.md"},n=e("h1",{id:"accounts",tabindex:"-1"},[o("Accounts "),e("a",{class:"header-anchor",href:"#accounts","aria-label":'Permalink to "Accounts"'},"​")],-1),r=e("p",null,"TBD",-1),d=[n,r];function i(l,u,_,h,p,m){return a(),c("div",null,d)}const b=t(s,[["render",i]]);export{k as __pageData,b as default}; diff --git a/assets/guide_blockchain_assets.md.ae63cc7e.js b/assets/guide_blockchain_assets.md.ae63cc7e.js new file mode 100644 index 000000000..b370d7104 --- /dev/null +++ b/assets/guide_blockchain_assets.md.ae63cc7e.js @@ -0,0 +1 @@ +import{_ as s,C as a,o as i,c as n,H as r,Q as e}from"./chunks/framework.1293becd.js";const _=JSON.parse('{"title":"Assets","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/assets.md","filePath":"guide/blockchain/assets.md","lastUpdated":1700563625000}'),o={name:"guide/blockchain/assets.md"},d=e('

Assets

Iroha has been built with few underlying assumptions about what the assets need to be.

The assets can be fungible (every £1 is exactly the same as every other £1) or non-fungible (a £1 bill signed by the Queen of Hearts is not the same as a £1 bill signed by the King of Spades).

The assets can also be mintable (you can make more of them) and non-mintable (you can only specify their initial quantity in the genesis block).

Value Types

Additionally, the assets have different underlying value types. Specifically, we have AssetValueType.Quantity, which is effectively an unsigned 32-bit integer, a BigQuantity, which is an unsigned 128-bit integer, and Fixed, which is a positive (though signed) 64-bit fixed-precision number with nine significant digits after the decimal point. All three types can be registered as either mintable or non-mintable.

There is also the Store asset type, which is used for storing key-values in object's metadata. We talk in detail about Store asset in the chapter related to metadata.

Asset Structure

',8),c=e('

Instructions

Assets can be registered, minted or burned, and transferred.

Refer to one of the language-specific guides to walk you through the process of registering and minting assets in a blockchain:

',4);function l(h,u,A,g,f,m){const t=a("MermaidRenderWrap");return i(),n("div",null,[d,r(t,{id:"mermaid_a3d76a1e8c22e2a4fec3e81161936e15807e50db5c7561e99c8404dda107b2ce13905a63e504fe8025fbd017689351a498e9e84078c427fce5506985540c3a9e",text:"classDiagram%0A%0Aclass%20Asset%0Aclass%20AssetDefinition%0A%0Aclass%20Id%20%7B%0A%20%20definition_id%0A%20%20account_id%0A%7D%0A%0Aclass%20Mintable%20%7B%0A%20%20%3C%3Cenumeration%3E%3E%0A%20%20Infinitely%0A%20%20Once%0A%20%20Not%0A%7D%0A%0Aclass%20AssetValue%20%7B%0A%20%20%3C%3Cenumeration%3E%3E%0A%20%20Quantity%0A%20%20BigQuantity%0A%20%20Fixed%0A%20%20Store%0A%7D%0A%0AAsset%20--%20AssetDefinition%0AAsset%20--%20Id%0AAssetDefinition%20--%20Mintable%0AAssetDefinition%20--%20AssetValue%20%0AAssetDefinition%20--%20Id%0A%0AAsset%20%3A%20id%20%7BId%7D%0AAsset%20%3A%20value%0A%0AAssetDefinition%20%3A%20id%20%7BId%7D%0AAssetDefinition%20%3A%20value_type%20%7BAssetValueType%7D%0AAssetDefinition%20%3A%20mintable%20%7BMintable%7D%0AAssetDefinition%20%3A%20metadata"}),c])}const b=s(o,[["render",l]]);export{_ as __pageData,b as default}; diff --git a/assets/guide_blockchain_assets.md.ae63cc7e.lean.js b/assets/guide_blockchain_assets.md.ae63cc7e.lean.js new file mode 100644 index 000000000..155116c75 --- /dev/null +++ b/assets/guide_blockchain_assets.md.ae63cc7e.lean.js @@ -0,0 +1 @@ +import{_ as s,C as a,o as i,c as n,H as r,Q as e}from"./chunks/framework.1293becd.js";const _=JSON.parse('{"title":"Assets","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/assets.md","filePath":"guide/blockchain/assets.md","lastUpdated":1700563625000}'),o={name:"guide/blockchain/assets.md"},d=e("",8),c=e("",4);function l(h,u,A,g,f,m){const t=a("MermaidRenderWrap");return i(),n("div",null,[d,r(t,{id:"mermaid_a3d76a1e8c22e2a4fec3e81161936e15807e50db5c7561e99c8404dda107b2ce13905a63e504fe8025fbd017689351a498e9e84078c427fce5506985540c3a9e",text:"classDiagram%0A%0Aclass%20Asset%0Aclass%20AssetDefinition%0A%0Aclass%20Id%20%7B%0A%20%20definition_id%0A%20%20account_id%0A%7D%0A%0Aclass%20Mintable%20%7B%0A%20%20%3C%3Cenumeration%3E%3E%0A%20%20Infinitely%0A%20%20Once%0A%20%20Not%0A%7D%0A%0Aclass%20AssetValue%20%7B%0A%20%20%3C%3Cenumeration%3E%3E%0A%20%20Quantity%0A%20%20BigQuantity%0A%20%20Fixed%0A%20%20Store%0A%7D%0A%0AAsset%20--%20AssetDefinition%0AAsset%20--%20Id%0AAssetDefinition%20--%20Mintable%0AAssetDefinition%20--%20AssetValue%20%0AAssetDefinition%20--%20Id%0A%0AAsset%20%3A%20id%20%7BId%7D%0AAsset%20%3A%20value%0A%0AAssetDefinition%20%3A%20id%20%7BId%7D%0AAssetDefinition%20%3A%20value_type%20%7BAssetValueType%7D%0AAssetDefinition%20%3A%20mintable%20%7BMintable%7D%0AAssetDefinition%20%3A%20metadata"}),c])}const b=s(o,[["render",l]]);export{_ as __pageData,b as default}; diff --git a/assets/guide_blockchain_consensus.md.6e226678.js b/assets/guide_blockchain_consensus.md.6e226678.js new file mode 100644 index 000000000..44b44c1ae --- /dev/null +++ b/assets/guide_blockchain_consensus.md.6e226678.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as s,Q as o}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Consensus","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/consensus.md","filePath":"guide/blockchain/consensus.md","lastUpdated":1700563625000}'),a={name:"guide/blockchain/consensus.md"},n=o('

Consensus

Each time you send a transaction to Iroha, it gets put into a queue. When it's time to produce a new block, the queue is emptied, and the consensus process begins. This process is equal parts common sense and black magic[1].

The mundane aspect is that a special set of peers needs to take the transaction queue and reproduce the same world state. If the world state cannot be reproduced for some reason or another, none of the transactions get committed to a block.

The consensus starts over from scratch by choosing a different special set of peers. This is where the black magic comes in. There is a number of things that are fine-tuned: the number of peers in the voting process, the way in which subsequent voting peers are chosen, and the way in which the peers communicate that consensus has failed. Because this changes the view of the world, the process is called a view change. The exact reason for why the view was changed is encoded in the view change proof, but decoding that information is an advanced topic that we won't cover here.

The reasoning behind this algorithm is simple: if someone had some evil peers and connected them to the existing network, if they tried to fake data, some good™ peers would not get the same (evil™) world state. If that's the case, the evil™ peers would not be allowed to participate in consensus, and you would eventually produce a block using only good™ peers.

As a natural consequence, if any changes to the world state are made without the use of ISI, the good™ peers cannot know of them. They won't be able to reproduce the hash of the world state, and thus consensus will fail. The same thing happens if the peers have different instructions.


  1. For prospective wizards, the Iroha 2 Whitepaper is a good start. ↩︎

',8),i=[n];function r(c,h,d,p,l,u){return t(),s("div",null,i)}const g=e(a,[["render",r]]);export{m as __pageData,g as default}; diff --git a/assets/guide_blockchain_consensus.md.6e226678.lean.js b/assets/guide_blockchain_consensus.md.6e226678.lean.js new file mode 100644 index 000000000..ebb42af2d --- /dev/null +++ b/assets/guide_blockchain_consensus.md.6e226678.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as s,Q as o}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Consensus","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/consensus.md","filePath":"guide/blockchain/consensus.md","lastUpdated":1700563625000}'),a={name:"guide/blockchain/consensus.md"},n=o("",8),i=[n];function r(c,h,d,p,l,u){return t(),s("div",null,i)}const g=e(a,[["render",r]]);export{m as __pageData,g as default}; diff --git a/assets/guide_blockchain_data-model.md.563e4b72.js b/assets/guide_blockchain_data-model.md.563e4b72.js new file mode 100644 index 000000000..9b45fc909 --- /dev/null +++ b/assets/guide_blockchain_data-model.md.563e4b72.js @@ -0,0 +1,35 @@ +import{_ as t,C as e,o as n,c as i,k as o,H as d,Q as s}from"./chunks/framework.1293becd.js";const y=JSON.parse('{"title":"Data Model","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/data-model.md","filePath":"guide/blockchain/data-model.md","lastUpdated":1700563625000}'),l={name:"guide/blockchain/data-model.md"},c=s(`

Data Model

In language-specific guides we already walked you through registering domains, accounts, and assets. Here we merely wish to illustrate the relationship between various objects in the blockchain.

+-----------------------------------------------+
+   |                                               |
+   |     +-----------------+                       |
+   |     |Domain           |                       |
+   |     +--------------+  |                       |
+   |     ||Asset        |  |                       |
++--+--+  ||Definition(s)|  |                       |
+|World|  +--------------+  |                       |
++--+--+  |                 |                       |
+   |     +------------+    |                       |
+   |     ||Account(s)||    | has   +-----------+   |
+   |     |------------------------->Signatories|   |
+   |     +-----------------+       +-----------+   |
+   |                       |                       |
+   |                       |  has  +--------+      |
+   |                       +------->Asset(s)|      |
+   |                               +--------+      |
+   +-----------------------------------------------+
+-----------------------------------------------+
+   |                                               |
+   |     +-----------------+                       |
+   |     |Domain           |                       |
+   |     +--------------+  |                       |
+   |     ||Asset        |  |                       |
++--+--+  ||Definition(s)|  |                       |
+|World|  +--------------+  |                       |
++--+--+  |                 |                       |
+   |     +------------+    |                       |
+   |     ||Account(s)||    | has   +-----------+   |
+   |     |------------------------->Signatories|   |
+   |     +-----------------+       +-----------+   |
+   |                       |                       |
+   |                       |  has  +--------+      |
+   |                       +------->Asset(s)|      |
+   |                               +--------+      |
+   +-----------------------------------------------+

The following example shows the relationship between domains, accounts, and assets.

`,4),r={class:"domains-example-scope"},p=s('
Language-specific guides to register these objects
LanguageGuide
BashRegister a domain, an account, an asset
RustRegister a domain, an account, an asset
Kotlin/JavaRegister a domain, an account, an asset
PythonRegister a domain, an account, an asset
JavaScript/TypeScriptRegister a domain, an account, an asset

The diagram below provides a more detailed illustration of the relationship between domains, accounts, and assets in the blockchain. You can learn more about permissions and roles and metadata in the corresponding sections. The asset structure is illustrated in a dedicated chapter.

',2);function v(A,g,h,m,_,u){const a=e("MermaidRenderWrap");return n(),i("div",null,[c,o("div",r,[d(a,{id:"mermaid_64b197bdaace931806f4329f68663c9a8c80858f31e40d2ebba2122ea46778c3d097a1942b854c14a7221d95be1bee95e77925bdcd094a656409f42a381c3bf7",text:"classDiagram%0A%0Aclass%20domain_wonderland%20%7B%0A%20%20id%20%3D%20%22wonderland%22%0A%7D%0Aclass%20account_alice%3A%3A%3Aaliceblue%20%7B%0A%20%20id%20%3D%20%22alice%40wonderland%22%0A%7D%0Aclass%20account_mad_hatter%3A%3A%3Aaliceblue%20%7B%0A%20%20id%20%3D%20%22mad_hatter%40wonderland%22%0A%7D%0A%0Aclass%20asset_rose%3A%3A%3Apink%20%7B%0A%20%20id%20%3D%20%22rose%23wonderland%22%0A%7D%0A%0Adomain_wonderland%20*--%20account_alice%20%3A%20registered%20in%0Adomain_wonderland%20*--%20asset_rose%20%3A%20registered%20in%0Aaccount_alice%20*--%20asset_rose%20%3A%20registered%20by%0Adomain_wonderland%20*--%20account_mad_hatter%20%3A%20registered%20in%0A%0Aclass%20domain_looking_glass%20%7B%0A%20%20id%20%3D%20%22looking_glass%22%0A%7D%0A%0Aclass%20account_rabbit%3A%3A%3Aaliceblue%20%7B%0A%20%20id%20%3D%20%22white_rabbit%40looking_glass%22%0A%7D%0A%0Adomain_looking_glass%20*--%20account_rabbit%20%3A%20registered%20in"})]),p,d(a,{id:"mermaid_2f20c1df6224dadf81d23a1a43dbb7e72a27090b7ef9cc03c1b0fdd9b6d843ff1140d32332b2f72dcf65fe3d4803afc3d28bbcc35e6ff26c72c8e605c8988cc0",text:"classDiagram%0A%0Aclass%20Domain%0Aclass%20Account%0Aclass%20AssetDefinition%0Aclass%20Asset%0A%0ADomain%20*--%20%22many%22%20Account%20%3A%20contains%0ADomain%20*--%20%22many%22%20AssetDefinition%20%3A%20contains%0AAccount%20*--%20%22many%22%20Asset%20%3A%20contains%0AAsset%20--%20AssetDefinition%0A%0ADomain%20%3A%20id%0ADomain%20%3A%20accounts%0ADomain%20%3A%20asset_definitions%0ADomain%20%3A%20logo%0ADomain%20%3A%20metadata%0A%0AAccount%20%3A%20id%0AAccount%20%3A%20assets%0AAccount%20%3A%20signatories%0AAccount%20%3A%20signature_check_condition%0AAccount%20%3A%20metadata%0AAccount%20%3A%20roles%0A%0A%0AAssetDefinition%20%3A%20id%0AAssetDefinition%20%3A%20value_type%0AAssetDefinition%20%3A%20mintable%0AAssetDefinition%20%3A%20metadata%0A%0AAsset%20%3A%20id%0AAsset%20%3A%20value"})])}const b=t(l,[["render",v],["__scopeId","data-v-5d111d5d"]]);export{y as __pageData,b as default}; diff --git a/assets/guide_blockchain_data-model.md.563e4b72.lean.js b/assets/guide_blockchain_data-model.md.563e4b72.lean.js new file mode 100644 index 000000000..660d8fadc --- /dev/null +++ b/assets/guide_blockchain_data-model.md.563e4b72.lean.js @@ -0,0 +1 @@ +import{_ as t,C as e,o as n,c as i,k as o,H as d,Q as s}from"./chunks/framework.1293becd.js";const y=JSON.parse('{"title":"Data Model","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/data-model.md","filePath":"guide/blockchain/data-model.md","lastUpdated":1700563625000}'),l={name:"guide/blockchain/data-model.md"},c=s("",4),r={class:"domains-example-scope"},p=s("",2);function v(A,g,h,m,_,u){const a=e("MermaidRenderWrap");return n(),i("div",null,[c,o("div",r,[d(a,{id:"mermaid_64b197bdaace931806f4329f68663c9a8c80858f31e40d2ebba2122ea46778c3d097a1942b854c14a7221d95be1bee95e77925bdcd094a656409f42a381c3bf7",text:"classDiagram%0A%0Aclass%20domain_wonderland%20%7B%0A%20%20id%20%3D%20%22wonderland%22%0A%7D%0Aclass%20account_alice%3A%3A%3Aaliceblue%20%7B%0A%20%20id%20%3D%20%22alice%40wonderland%22%0A%7D%0Aclass%20account_mad_hatter%3A%3A%3Aaliceblue%20%7B%0A%20%20id%20%3D%20%22mad_hatter%40wonderland%22%0A%7D%0A%0Aclass%20asset_rose%3A%3A%3Apink%20%7B%0A%20%20id%20%3D%20%22rose%23wonderland%22%0A%7D%0A%0Adomain_wonderland%20*--%20account_alice%20%3A%20registered%20in%0Adomain_wonderland%20*--%20asset_rose%20%3A%20registered%20in%0Aaccount_alice%20*--%20asset_rose%20%3A%20registered%20by%0Adomain_wonderland%20*--%20account_mad_hatter%20%3A%20registered%20in%0A%0Aclass%20domain_looking_glass%20%7B%0A%20%20id%20%3D%20%22looking_glass%22%0A%7D%0A%0Aclass%20account_rabbit%3A%3A%3Aaliceblue%20%7B%0A%20%20id%20%3D%20%22white_rabbit%40looking_glass%22%0A%7D%0A%0Adomain_looking_glass%20*--%20account_rabbit%20%3A%20registered%20in"})]),p,d(a,{id:"mermaid_2f20c1df6224dadf81d23a1a43dbb7e72a27090b7ef9cc03c1b0fdd9b6d843ff1140d32332b2f72dcf65fe3d4803afc3d28bbcc35e6ff26c72c8e605c8988cc0",text:"classDiagram%0A%0Aclass%20Domain%0Aclass%20Account%0Aclass%20AssetDefinition%0Aclass%20Asset%0A%0ADomain%20*--%20%22many%22%20Account%20%3A%20contains%0ADomain%20*--%20%22many%22%20AssetDefinition%20%3A%20contains%0AAccount%20*--%20%22many%22%20Asset%20%3A%20contains%0AAsset%20--%20AssetDefinition%0A%0ADomain%20%3A%20id%0ADomain%20%3A%20accounts%0ADomain%20%3A%20asset_definitions%0ADomain%20%3A%20logo%0ADomain%20%3A%20metadata%0A%0AAccount%20%3A%20id%0AAccount%20%3A%20assets%0AAccount%20%3A%20signatories%0AAccount%20%3A%20signature_check_condition%0AAccount%20%3A%20metadata%0AAccount%20%3A%20roles%0A%0A%0AAssetDefinition%20%3A%20id%0AAssetDefinition%20%3A%20value_type%0AAssetDefinition%20%3A%20mintable%0AAssetDefinition%20%3A%20metadata%0A%0AAsset%20%3A%20id%0AAsset%20%3A%20value"})])}const b=t(l,[["render",v],["__scopeId","data-v-5d111d5d"]]);export{y as __pageData,b as default}; diff --git a/assets/guide_blockchain_domains.md.97f6e325.js b/assets/guide_blockchain_domains.md.97f6e325.js new file mode 100644 index 000000000..a7d5c043e --- /dev/null +++ b/assets/guide_blockchain_domains.md.97f6e325.js @@ -0,0 +1 @@ +import{_ as e,o,c as t,k as a,a as s}from"./chunks/framework.1293becd.js";const k=JSON.parse('{"title":"Domains","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/domains.md","filePath":"guide/blockchain/domains.md","lastUpdated":1700563625000}'),n={name:"guide/blockchain/domains.md"},i=a("h1",{id:"domains",tabindex:"-1"},[s("Domains "),a("a",{class:"header-anchor",href:"#domains","aria-label":'Permalink to "Domains"'},"​")],-1),d=a("p",null,"TBD",-1),c=[i,d];function r(l,m,_,h,p,f){return o(),t("div",null,c)}const b=e(n,[["render",r]]);export{k as __pageData,b as default}; diff --git a/assets/guide_blockchain_domains.md.97f6e325.lean.js b/assets/guide_blockchain_domains.md.97f6e325.lean.js new file mode 100644 index 000000000..a7d5c043e --- /dev/null +++ b/assets/guide_blockchain_domains.md.97f6e325.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as t,k as a,a as s}from"./chunks/framework.1293becd.js";const k=JSON.parse('{"title":"Domains","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/domains.md","filePath":"guide/blockchain/domains.md","lastUpdated":1700563625000}'),n={name:"guide/blockchain/domains.md"},i=a("h1",{id:"domains",tabindex:"-1"},[s("Domains "),a("a",{class:"header-anchor",href:"#domains","aria-label":'Permalink to "Domains"'},"​")],-1),d=a("p",null,"TBD",-1),c=[i,d];function r(l,m,_,h,p,f){return o(),t("div",null,c)}const b=e(n,[["render",r]]);export{k as __pageData,b as default}; diff --git a/assets/guide_blockchain_events.md.eb70e642.js b/assets/guide_blockchain_events.md.eb70e642.js new file mode 100644 index 000000000..95249a1b8 --- /dev/null +++ b/assets/guide_blockchain_events.md.eb70e642.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as i,Q as a}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Events","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/events.md","filePath":"guide/blockchain/events.md","lastUpdated":1700563625000}'),n={name:"guide/blockchain/events.md"},s=a('

Events

Events are emitted when certain things happen within the blockchain, e.g. a new account is created or a block is committed. There are different types of events:

  • pipeline events
  • data events
  • time events
  • trigger execution events

Pipeline Events

Pipeline events are emitted when transactions are submitted, executed, or committed to a block. A pipeline event contains the following information: the kind of entity that caused an event (transaction or block), its hash and status. The status can be either Validating (validation in progress), Rejected, or Committed. If an entity was rejected, the reason for the rejection is provided.

Data Events

Data events are emitted when there is a change related to one of the following entities: peers, domains, accounts, asset definitions, assets, triggers, roles, permission tokens, permission validators, or Iroha configuration. These types of events are used in entity filters.

Time Events

Time events are emitted when the world state view is ready to handle time triggers.

Trigger Execution Events

Trigger execution events are emitted when the ExecuteTrigger instruction is executed

',11),r=[s];function o(c,d,l,h,v,p){return t(),i("div",null,r)}const g=e(n,[["render",o]]);export{u as __pageData,g as default}; diff --git a/assets/guide_blockchain_events.md.eb70e642.lean.js b/assets/guide_blockchain_events.md.eb70e642.lean.js new file mode 100644 index 000000000..ee354c960 --- /dev/null +++ b/assets/guide_blockchain_events.md.eb70e642.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as i,Q as a}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Events","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/events.md","filePath":"guide/blockchain/events.md","lastUpdated":1700563625000}'),n={name:"guide/blockchain/events.md"},s=a("",11),r=[s];function o(c,d,l,h,v,p){return t(),i("div",null,r)}const g=e(n,[["render",o]]);export{u as __pageData,g as default}; diff --git a/assets/guide_blockchain_expressions.md.1f016bcd.js b/assets/guide_blockchain_expressions.md.1f016bcd.js new file mode 100644 index 000000000..d8d5e60ff --- /dev/null +++ b/assets/guide_blockchain_expressions.md.1f016bcd.js @@ -0,0 +1 @@ +import{_ as e,o,c as s,Q as i}from"./chunks/framework.1293becd.js";const _=JSON.parse('{"title":"Expressions, Conditionals, Logic","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/expressions.md","filePath":"guide/blockchain/expressions.md","lastUpdated":1700563625000}'),t={name:"guide/blockchain/expressions.md"},a=i('

Expressions, Conditionals, Logic

All Iroha Special Instructions operate on expressions. Each expression has an EvaluatesTo, which is used in instruction execution. While you could specify the account name directly, you could also specify the account ID via some mathematical or string operation. You can check if an account is registered on the blockchain too.

Using expressions that implement EvaluatesTo<bool>, you can set up conditional logic and execute more sophisticated operations on-chain. For example, you can submit a Mint instruction only if a specific account is registered.

Recall that you can combine this with queries, and as such can program the blockchain to do some amazing stuff. This is what we refer to as smart contracts, the defining feature of the advanced usage of blockchain technology.

',4),n=[a];function c(r,l,d,h,p,u){return o(),s("div",null,n)}const f=e(t,[["render",c]]);export{_ as __pageData,f as default}; diff --git a/assets/guide_blockchain_expressions.md.1f016bcd.lean.js b/assets/guide_blockchain_expressions.md.1f016bcd.lean.js new file mode 100644 index 000000000..588901b5f --- /dev/null +++ b/assets/guide_blockchain_expressions.md.1f016bcd.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as s,Q as i}from"./chunks/framework.1293becd.js";const _=JSON.parse('{"title":"Expressions, Conditionals, Logic","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/expressions.md","filePath":"guide/blockchain/expressions.md","lastUpdated":1700563625000}'),t={name:"guide/blockchain/expressions.md"},a=i("",4),n=[a];function c(r,l,d,h,p,u){return o(),s("div",null,n)}const f=e(t,[["render",c]]);export{_ as __pageData,f as default}; diff --git a/assets/guide_blockchain_filters.md.3bc7d228.js b/assets/guide_blockchain_filters.md.3bc7d228.js new file mode 100644 index 000000000..c9e4bb6d4 --- /dev/null +++ b/assets/guide_blockchain_filters.md.3bc7d228.js @@ -0,0 +1 @@ +import{_ as i,C as a,o as n,c as s,H as t,Q as A}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Filters","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/filters.md","filePath":"guide/blockchain/filters.md","lastUpdated":1700563625000}'),r={name:"guide/blockchain/filters.md"},d=A('

Filters

Iroha uses filter-map paradigm to monitor events. Let's look at different types of filters that can be used in Iroha.

Data Filters

A data filter is a tuple with a single variant, which is a FilterOpt of an EntityFilter:

',4),o=A("

FilterOpt stands for Optional Filter. It can either AcceptAll or accept BySome of another Filter. An EntityFilter is a filter that matches events produced by a certain type entity, e.g. by account or domain.

Here is the list of EntityFilters in Iroha:

",2);function l(c,E,m,D,v,u){const e=a("MermaidRenderWrap");return n(),s("div",null,[d,t(e,{id:"mermaid_dc1d04f60b4f5b960fd5a56516975b14aceeaba39d2a33822de629ad938f1941161b4864b3165a36ba4c243c4c8184cc4b0f28204cd375175550d0e0ddbbb2a4",text:"classDiagram%0A%0Aclass%20EntityFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByPeer(FilterOpt~PeerFilter~)%0A%20%20%20%20ByDomain(FilterOpt~DomainFilter~)%0A%20%20%20%20ByAccount(FilterOpt~AccountFilter~)%0A%20%20%20%20ByAssetDefinition(FilterOpt~AssetDefinitionFilter~)%0A%20%20%20%20ByAsset(FilterOpt~AssetFilter~)%0A%20%20%20%20ByTrigger(FilterOpt~TriggerFilter~)%0A%20%20%20%20ByRole(FilterOpt~RoleFilter~)%0A%7D%0A%0Aclass%20FilterOpt~F%3A%20Filter~%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20AcceptAll%0A%20%20%20%20BySome(F)%0A%7D%0A%0Aclass%20EventFilter%20%7B%0A%20%20%20%20FilterOpt~EntityFilter~%0A%7D%0A%0AFilterOpt%20..%20EventFilter%0AFilterOpt%20..%20EntityFilter%0AEntityFilter%20..%20EventFilter"}),o,t(e,{id:"mermaid_c6f7dcaf44bbc0f986880725c420ae3c8e4ace31ddb708d70e3328e746856ebb2310b7adc671fa5c17d4afff9df55b5e88522ae8fe9021dabb61682f4bfc5e08",text:"classDiagram%0A%0Adirection%20LR%0A%0Aclass%20TriggerFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20TriggerEvent%0A%20%20%20%20type%20EventFilter%20%3D%20TriggerEventFilter%0A%7D%0A%0Aclass%20TriggerEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByExtended%0A%20%20%20%20ByShortened%0A%7D%0A%0Aclass%20TriggerEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Created(TriggerId)%0A%20%20%20%20Deleted(TriggerId)%0A%20%20%20%20Extended(TriggerNumberOfExecutionsChanged)%0A%20%20%20%20Shortened(TriggerNumberOfExecutionsChanged)%0A%7D%0A%0Aclass%20TriggerNumberOfExecutionsChanged%20%7B%0A%20%20%20%20trigger_id%3A%20TriggerId%0A%20%20%20%20by%3A%20u32%0A%7D%0A%0ATriggerFilter%20..%20TriggerEvent%0ATriggerEvent%20..%20TriggerNumberOfExecutionsChanged%0ATriggerFilter%20..%20TriggerEventFilter%0A%0A%0Aclass%20RoleFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20RoleEvent%0A%20%20%20%20type%20EventFilter%20%3D%20RoleEventFilter%0A%7D%0A%0Aclass%20RoleEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%7D%0A%0Aclass%20RoleEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Created(RoleId)%0A%20%20%20%20Deleted(RoleId)%0A%20%20%20%20PermissionRemoved(PermissionRemoved)%0A%7D%0A%0Aclass%20PermissionRemoved%20%7B%0A%20%20%20%20role_id%3A%20RoleId%0A%20%20%20%20permission_definition_id%3A%20PermissionTokenDefinitionId%0A%7D%0A%0ARoleFilter%20..%20RoleEvent%0ARoleEvent%20..%20PermissionRemoved%0ARoleFilter%20..%20RoleEventFilter%0A%0A%0Aclass%20PeerFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20PeerEvent%0A%20%20%20%20type%20EventFilter%20%3D%20PeerEventFilter%0A%7D%0A%0Aclass%20PeerEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByAdded%0A%20%20%20%20ByRemoved%0A%7D%0A%0Aclass%20PeerEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Added(PeerId)%0A%20%20%20%20Removed(PeerId)%0A%7D%0A%0APeerFilter%20..%20PeerEvent%0APeerFilter%20..%20PeerEventFilter%0A%0A%0A%0A%0Aclass%20AssetDefinitionFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20AssetDefinitionEvent%0A%20%20%20%20type%20EventFilter%20%3D%20AssetDefinitionEventFilter%0A%7D%0A%0Aclass%20AssetDefinitionEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByMintabilityChanged%0A%20%20%20%20ByMetadataInserted%0A%20%20%20%20ByMetadataRemoved%0A%7D%0A%0Aclass%20AssetDefinitionEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Created(AssetDefinitionId)%0A%20%20%20%20MintabilityChanged(AssetDefinitionId)%0A%20%20%20%20Deleted(AssetDefinitionId)%0A%20%20%20%20MetadataInserted(AssetDefinitionMetadataChanged)%0A%20%20%20%20MetadataRemoved(AssetDefinitionMetadataChanged)%0A%7D%0A%0Aclass%20AssetDefinitionMetadataChanged%20%7B%0A%20%20%20%20target_id%3A%20AssetDefinitionId%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0AAssetDefinitionFilter%20..%20AssetDefinitionEvent%0AAssetDefinitionEvent%20..%20AssetDefinitionMetadataChanged%0AAssetDefinitionFilter%20..%20AssetDefinitionEventFilter%0A%0A%0A%0Aclass%20AssetFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20AssetEvent%0A%20%20%20%20type%20EventFilter%20%3D%20AssetEventFilter%0A%7D%0A%0Aclass%20AssetEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByAdded%0A%20%20%20%20ByRemoved%0A%20%20%20%20ByMetadataInserted%0A%20%20%20%20ByMetadataRemoved%0A%7D%0A%0Aclass%20AssetEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Created(AssetId)%0A%20%20%20%20Deleted(AssetId)%0A%20%20%20%20Added(AssetChanged)%0A%20%20%20%20Removed(AssetChanged)%0A%20%20%20%20MetadataInserted(AssetMetadataChanged)%0A%20%20%20%20MetadataRemoved(AssetMetadataChanged)%0A%7D%0A%0Aclass%20AssetChanged%20%7B%0A%20%20%20%20asset_id%3A%20AssetId%0A%20%20%20%20amount%3A%20AssetValue%0A%7D%0A%0Aclass%20AssetMetadataChanged%20%7B%0A%20%20%20%20target_id%3A%20AssetId%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0AAssetFilter%20..%20AssetEvent%0AAssetEvent%20..%20AssetChanged%0AAssetEvent%20..%20AssetMetadataChanged%0AAssetFilter%20..%20AssetEventFilter%0A%0A%0A%0Aclass%20DomainFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20DomainEvent%0A%20%20%20%20type%20EventFilter%20%3D%20DomainEventFilter%0A%7D%0A%0Aclass%20DomainEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByAccount(FilterOpt~AccountFilter~)%0A%20%20%20%20ByAssetDefinition(FilterOpt~AssetDefinitionFilter~)%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByMetadataInserted%0A%20%20%20%20ByMetadataRemoved%0A%7D%0A%0Aclass%20DomainEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Account(AccountEvent)%0A%20%20%20%20AssetDefinition(AssetDefinitionEvent)%0A%20%20%20%20Created(DomainId)%0A%20%20%20%20Deleted(DomainId)%0A%20%20%20%20MetadataInserted(DomainMetadataChanged)%0A%20%20%20%20MetadataRemoved(DomainMetadataChanged)%0A%7D%0A%0Aclass%20DomainMetadataChanged%20%7B%0A%20%20%20%20target_id%3A%20DomainId%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0ADomainFilter%20..%20DomainEvent%0ADomainEvent%20..%20DomainMetadataChanged%0ADomainFilter%20..%20DomainEventFilter%0A%0A%0Aclass%20AccountFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20AccountEvent%0A%20%20%20%20type%20EventFilter%20%3D%20AccountEventFilter%0A%7D%0A%0Aclass%20AccountEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByAsset(FilterOpt~AssetFilter~)%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByAuthenticationAdded%0A%20%20%20%20ByAuthenticationRemoved%0A%20%20%20%20ByPermissionAdded%0A%20%20%20%20ByPermissionRemoved%0A%20%20%20%20ByRoleRevoked%0A%20%20%20%20ByRoleGranted%0A%20%20%20%20ByMetadataInserted%0A%20%20%20%20ByMetadataRemoved%0A%7D%0A%0Aclass%20AccountEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Asset(AssetEvent)%0A%20%20%20%20Created(AccountId)%0A%20%20%20%20Deleted(AccountId)%0A%20%20%20%20AuthenticationAdded(AccountId)%0A%20%20%20%20AuthenticationRemoved(AccountId)%0A%20%20%20%20PermissionAdded(AccountPermissionChanged)%0A%20%20%20%20PermissionRemoved(AccountPermissionChanged)%0A%20%20%20%20RoleRevoked(AccountRoleChanged)%0A%20%20%20%20RoleGranted(AccountRoleChanged)%0A%20%20%20%20MetadataInserted(AccountMetadataChanged)%0A%20%20%20%20MetadataRemoved(AccountMetadataChanged)%0A%7D%0A%0Aclass%20AccountPermissionChanged%20%7B%0A%20%20%20%20account_id%3A%20AccountId%0A%20%20%20%20permission_id%3A%20PermissionTokenId%0A%7D%0A%0Aclass%20AccountRoleChanged%20%7B%0A%20%20%20%20account_id%3A%20AccountId%0A%20%20%20%20role_id%3A%20RoleId%0A%7D%0A%0Aclass%20AccountMetadataChanged%20%7B%0A%20%20%20%20target_id%3A%20AccountId%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0AAccountFilter%20..%20AccountEvent%0AAccountEvent%20..%20AccountPermissionChanged%0AAccountEvent%20..%20AccountRoleChanged%0AAccountEvent%20..%20AccountMetadataChanged%0AAccountFilter%20..%20AccountEventFilter"})])}const y=i(r,[["render",l]]);export{g as __pageData,y as default}; diff --git a/assets/guide_blockchain_filters.md.3bc7d228.lean.js b/assets/guide_blockchain_filters.md.3bc7d228.lean.js new file mode 100644 index 000000000..32bb690bb --- /dev/null +++ b/assets/guide_blockchain_filters.md.3bc7d228.lean.js @@ -0,0 +1 @@ +import{_ as i,C as a,o as n,c as s,H as t,Q as A}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Filters","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/filters.md","filePath":"guide/blockchain/filters.md","lastUpdated":1700563625000}'),r={name:"guide/blockchain/filters.md"},d=A("",4),o=A("",2);function l(c,E,m,D,v,u){const e=a("MermaidRenderWrap");return n(),s("div",null,[d,t(e,{id:"mermaid_dc1d04f60b4f5b960fd5a56516975b14aceeaba39d2a33822de629ad938f1941161b4864b3165a36ba4c243c4c8184cc4b0f28204cd375175550d0e0ddbbb2a4",text:"classDiagram%0A%0Aclass%20EntityFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByPeer(FilterOpt~PeerFilter~)%0A%20%20%20%20ByDomain(FilterOpt~DomainFilter~)%0A%20%20%20%20ByAccount(FilterOpt~AccountFilter~)%0A%20%20%20%20ByAssetDefinition(FilterOpt~AssetDefinitionFilter~)%0A%20%20%20%20ByAsset(FilterOpt~AssetFilter~)%0A%20%20%20%20ByTrigger(FilterOpt~TriggerFilter~)%0A%20%20%20%20ByRole(FilterOpt~RoleFilter~)%0A%7D%0A%0Aclass%20FilterOpt~F%3A%20Filter~%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20AcceptAll%0A%20%20%20%20BySome(F)%0A%7D%0A%0Aclass%20EventFilter%20%7B%0A%20%20%20%20FilterOpt~EntityFilter~%0A%7D%0A%0AFilterOpt%20..%20EventFilter%0AFilterOpt%20..%20EntityFilter%0AEntityFilter%20..%20EventFilter"}),o,t(e,{id:"mermaid_c6f7dcaf44bbc0f986880725c420ae3c8e4ace31ddb708d70e3328e746856ebb2310b7adc671fa5c17d4afff9df55b5e88522ae8fe9021dabb61682f4bfc5e08",text:"classDiagram%0A%0Adirection%20LR%0A%0Aclass%20TriggerFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20TriggerEvent%0A%20%20%20%20type%20EventFilter%20%3D%20TriggerEventFilter%0A%7D%0A%0Aclass%20TriggerEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByExtended%0A%20%20%20%20ByShortened%0A%7D%0A%0Aclass%20TriggerEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Created(TriggerId)%0A%20%20%20%20Deleted(TriggerId)%0A%20%20%20%20Extended(TriggerNumberOfExecutionsChanged)%0A%20%20%20%20Shortened(TriggerNumberOfExecutionsChanged)%0A%7D%0A%0Aclass%20TriggerNumberOfExecutionsChanged%20%7B%0A%20%20%20%20trigger_id%3A%20TriggerId%0A%20%20%20%20by%3A%20u32%0A%7D%0A%0ATriggerFilter%20..%20TriggerEvent%0ATriggerEvent%20..%20TriggerNumberOfExecutionsChanged%0ATriggerFilter%20..%20TriggerEventFilter%0A%0A%0Aclass%20RoleFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20RoleEvent%0A%20%20%20%20type%20EventFilter%20%3D%20RoleEventFilter%0A%7D%0A%0Aclass%20RoleEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%7D%0A%0Aclass%20RoleEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Created(RoleId)%0A%20%20%20%20Deleted(RoleId)%0A%20%20%20%20PermissionRemoved(PermissionRemoved)%0A%7D%0A%0Aclass%20PermissionRemoved%20%7B%0A%20%20%20%20role_id%3A%20RoleId%0A%20%20%20%20permission_definition_id%3A%20PermissionTokenDefinitionId%0A%7D%0A%0ARoleFilter%20..%20RoleEvent%0ARoleEvent%20..%20PermissionRemoved%0ARoleFilter%20..%20RoleEventFilter%0A%0A%0Aclass%20PeerFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20PeerEvent%0A%20%20%20%20type%20EventFilter%20%3D%20PeerEventFilter%0A%7D%0A%0Aclass%20PeerEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByAdded%0A%20%20%20%20ByRemoved%0A%7D%0A%0Aclass%20PeerEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Added(PeerId)%0A%20%20%20%20Removed(PeerId)%0A%7D%0A%0APeerFilter%20..%20PeerEvent%0APeerFilter%20..%20PeerEventFilter%0A%0A%0A%0A%0Aclass%20AssetDefinitionFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20AssetDefinitionEvent%0A%20%20%20%20type%20EventFilter%20%3D%20AssetDefinitionEventFilter%0A%7D%0A%0Aclass%20AssetDefinitionEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByMintabilityChanged%0A%20%20%20%20ByMetadataInserted%0A%20%20%20%20ByMetadataRemoved%0A%7D%0A%0Aclass%20AssetDefinitionEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Created(AssetDefinitionId)%0A%20%20%20%20MintabilityChanged(AssetDefinitionId)%0A%20%20%20%20Deleted(AssetDefinitionId)%0A%20%20%20%20MetadataInserted(AssetDefinitionMetadataChanged)%0A%20%20%20%20MetadataRemoved(AssetDefinitionMetadataChanged)%0A%7D%0A%0Aclass%20AssetDefinitionMetadataChanged%20%7B%0A%20%20%20%20target_id%3A%20AssetDefinitionId%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0AAssetDefinitionFilter%20..%20AssetDefinitionEvent%0AAssetDefinitionEvent%20..%20AssetDefinitionMetadataChanged%0AAssetDefinitionFilter%20..%20AssetDefinitionEventFilter%0A%0A%0A%0Aclass%20AssetFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20AssetEvent%0A%20%20%20%20type%20EventFilter%20%3D%20AssetEventFilter%0A%7D%0A%0Aclass%20AssetEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByAdded%0A%20%20%20%20ByRemoved%0A%20%20%20%20ByMetadataInserted%0A%20%20%20%20ByMetadataRemoved%0A%7D%0A%0Aclass%20AssetEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Created(AssetId)%0A%20%20%20%20Deleted(AssetId)%0A%20%20%20%20Added(AssetChanged)%0A%20%20%20%20Removed(AssetChanged)%0A%20%20%20%20MetadataInserted(AssetMetadataChanged)%0A%20%20%20%20MetadataRemoved(AssetMetadataChanged)%0A%7D%0A%0Aclass%20AssetChanged%20%7B%0A%20%20%20%20asset_id%3A%20AssetId%0A%20%20%20%20amount%3A%20AssetValue%0A%7D%0A%0Aclass%20AssetMetadataChanged%20%7B%0A%20%20%20%20target_id%3A%20AssetId%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0AAssetFilter%20..%20AssetEvent%0AAssetEvent%20..%20AssetChanged%0AAssetEvent%20..%20AssetMetadataChanged%0AAssetFilter%20..%20AssetEventFilter%0A%0A%0A%0Aclass%20DomainFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20DomainEvent%0A%20%20%20%20type%20EventFilter%20%3D%20DomainEventFilter%0A%7D%0A%0Aclass%20DomainEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByAccount(FilterOpt~AccountFilter~)%0A%20%20%20%20ByAssetDefinition(FilterOpt~AssetDefinitionFilter~)%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByMetadataInserted%0A%20%20%20%20ByMetadataRemoved%0A%7D%0A%0Aclass%20DomainEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Account(AccountEvent)%0A%20%20%20%20AssetDefinition(AssetDefinitionEvent)%0A%20%20%20%20Created(DomainId)%0A%20%20%20%20Deleted(DomainId)%0A%20%20%20%20MetadataInserted(DomainMetadataChanged)%0A%20%20%20%20MetadataRemoved(DomainMetadataChanged)%0A%7D%0A%0Aclass%20DomainMetadataChanged%20%7B%0A%20%20%20%20target_id%3A%20DomainId%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0ADomainFilter%20..%20DomainEvent%0ADomainEvent%20..%20DomainMetadataChanged%0ADomainFilter%20..%20DomainEventFilter%0A%0A%0Aclass%20AccountFilter%20%7B%0A%20%20%20%20type%20EventType%20%3D%20AccountEvent%0A%20%20%20%20type%20EventFilter%20%3D%20AccountEventFilter%0A%7D%0A%0Aclass%20AccountEventFilter%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20ByAsset(FilterOpt~AssetFilter~)%0A%20%20%20%20ByCreated%0A%20%20%20%20ByDeleted%0A%20%20%20%20ByAuthenticationAdded%0A%20%20%20%20ByAuthenticationRemoved%0A%20%20%20%20ByPermissionAdded%0A%20%20%20%20ByPermissionRemoved%0A%20%20%20%20ByRoleRevoked%0A%20%20%20%20ByRoleGranted%0A%20%20%20%20ByMetadataInserted%0A%20%20%20%20ByMetadataRemoved%0A%7D%0A%0Aclass%20AccountEvent%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Asset(AssetEvent)%0A%20%20%20%20Created(AccountId)%0A%20%20%20%20Deleted(AccountId)%0A%20%20%20%20AuthenticationAdded(AccountId)%0A%20%20%20%20AuthenticationRemoved(AccountId)%0A%20%20%20%20PermissionAdded(AccountPermissionChanged)%0A%20%20%20%20PermissionRemoved(AccountPermissionChanged)%0A%20%20%20%20RoleRevoked(AccountRoleChanged)%0A%20%20%20%20RoleGranted(AccountRoleChanged)%0A%20%20%20%20MetadataInserted(AccountMetadataChanged)%0A%20%20%20%20MetadataRemoved(AccountMetadataChanged)%0A%7D%0A%0Aclass%20AccountPermissionChanged%20%7B%0A%20%20%20%20account_id%3A%20AccountId%0A%20%20%20%20permission_id%3A%20PermissionTokenId%0A%7D%0A%0Aclass%20AccountRoleChanged%20%7B%0A%20%20%20%20account_id%3A%20AccountId%0A%20%20%20%20role_id%3A%20RoleId%0A%7D%0A%0Aclass%20AccountMetadataChanged%20%7B%0A%20%20%20%20target_id%3A%20AccountId%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0AAccountFilter%20..%20AccountEvent%0AAccountEvent%20..%20AccountPermissionChanged%0AAccountEvent%20..%20AccountRoleChanged%0AAccountEvent%20..%20AccountMetadataChanged%0AAccountFilter%20..%20AccountEventFilter"})])}const y=i(r,[["render",l]]);export{g as __pageData,y as default}; diff --git a/assets/guide_blockchain_how-iroha-works.md.0b0aa977.js b/assets/guide_blockchain_how-iroha-works.md.0b0aa977.js new file mode 100644 index 000000000..55df39076 --- /dev/null +++ b/assets/guide_blockchain_how-iroha-works.md.0b0aa977.js @@ -0,0 +1 @@ +import{_ as e,o,c as t,Q as a}from"./chunks/framework.1293becd.js";const w=JSON.parse('{"title":"How Iroha works","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/how-iroha-works.md","filePath":"guide/blockchain/how-iroha-works.md","lastUpdated":1700563625000}'),r={name:"guide/blockchain/how-iroha-works.md"},i=a('

How Iroha works

To understand how Iroha operates, let's draw parallels between a blockchain and a computer. If the blockchain is the computer, then in this metaphor of ours the client binary (for example: iroha_client_cli) is the keyboard, the blockchain is the hard drive, and the Iroha peer software is the processor. Like a processor, Iroha accepts portable instructions that modify what's written to the blockchain, allow certain users to use the network, and lock others out.

Any operation that is run on-chain is written in terms of Iroha Special Instructions (ISI), and there is no other way of modifying the world state.

Each interaction with the blockchain is done via a transaction. A transaction is a collection of instructions, which are either glued together in sequence or compiled into what we affectionately call a WASM blob. You need these instructions to register an account, remove an account, add X amount of Y currency, and so on.

To read the information encoded in the blocks of a blockchain (the current world state), you use queries. Queries are submitted like instructions, but they're not tracked and recorded in blocks, so they're much more lightweight. If you use queries as part of complicated logic (e.g. inside WASM), they have a non-negligible impact on the size of the blocks. Queries that are only used to get information leave no trace in the blockchain.

',5),n=[i];function h(s,c,l,d,u,p){return o(),t("div",null,n)}const k=e(r,[["render",h]]);export{w as __pageData,k as default}; diff --git a/assets/guide_blockchain_how-iroha-works.md.0b0aa977.lean.js b/assets/guide_blockchain_how-iroha-works.md.0b0aa977.lean.js new file mode 100644 index 000000000..eae4a5049 --- /dev/null +++ b/assets/guide_blockchain_how-iroha-works.md.0b0aa977.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as t,Q as a}from"./chunks/framework.1293becd.js";const w=JSON.parse('{"title":"How Iroha works","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/how-iroha-works.md","filePath":"guide/blockchain/how-iroha-works.md","lastUpdated":1700563625000}'),r={name:"guide/blockchain/how-iroha-works.md"},i=a("",5),n=[i];function h(s,c,l,d,u,p){return o(),t("div",null,n)}const k=e(r,[["render",h]]);export{w as __pageData,k as default}; diff --git a/assets/guide_blockchain_instructions.md.fb8ab4c5.js b/assets/guide_blockchain_instructions.md.fb8ab4c5.js new file mode 100644 index 000000000..bd94ebcff --- /dev/null +++ b/assets/guide_blockchain_instructions.md.fb8ab4c5.js @@ -0,0 +1 @@ +import{_ as l,C as t,o as g,c as m,k as s,a as e,H as a,w as r,Q as i}from"./chunks/framework.1293becd.js";const j=JSON.parse('{"title":"Iroha Special Instructions","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/instructions.md","filePath":"guide/blockchain/instructions.md","lastUpdated":1700563625000}'),p={name:"guide/blockchain/instructions.md"},f=i('

Iroha Special Instructions

When we spoke about how Iroha operates, we said that Iroha Special Instructions are the only way to modify the world state. So, what kind of special instructions do we have? If you've read the language-specific guides in this tutorial, you've already seen a couple of instructions: Register<Account> and Mint<Quantity>.

Here is the full list of Iroha Special Instructions:

InstructionDescriptions
Register/UnregisterGive an ID to a new entity on the blockchain.
Mint/BurnMint/burn assets, triggers, or permission tokens.
SetKeyValue/RemoveKeyValueUpdate blockchain object metadata.
NewParameter/SetParameterCreate/set a chain-wide config parameter.
Grant/RevokeGive or remove certain permissions from accounts.
TransferTransfer assets between accounts.
ExecuteTriggerExecute triggers.
If, Pair, SequenceUse to create composite instructions.

Let's start with a summary of Iroha Special Instructions; what objects each instruction can be called for and what instructions are available for each object.

Summary

For each instruction, there is a list of objects on which this instruction can be run on. For example, only assets can be transferred, while minting can refer to assets, triggers, and permission tokens.

Some instructions require a destination to be specified. For example, if you transfer assets, you always need to specify to which account you are transferring them. On the other hand, when you are registering something, all you need is the object that you want to register.

InstructionObjectsDestination
Register/Unregisteraccounts, domains, asset definitions, triggers, roles, peers
Mint/Burnassets, triggers (trigger repetitions), permission tokensaccounts
SetKeyValue/RemoveKeyValueany objects that have metadata: accounts, domains, assets, asset definitions, triggers, transactions
NewParameter/SetParameterIroha configuration parameters
Grant/Revokeroles, permission tokensaccounts
Transferassetsaccounts
ExecuteTriggertriggers
If, Pair, Sequenceany instructions

There is also another way of looking at ISI, i.e. in terms of the target of each instruction. For example, when you register an account, you do so within a certain domain. This means that the target of the Register<Account> instruction would be the domain within which it is being registered.

TargetInstructions
Account(un)register assets, mint/burn account public key, mint/burn account signature condition check, update account metadata, grant/revoke a permission token, grant/revoke role
Domain(un)register accounts, (un)register asset definitions, update asset metadata, update domain metadata
Assetupdate metadata, mint/burn, transfer
Trigger(un)register, mint/burn trigger repetitions, execute trigger
World(un)register domains, peers, roles

(Un)Register

Registering and unregistering are the instructions used to give an ID to a new entity on the blockchain.

Everything that can be registered is both Registrable and Identifiable, but not everything that's Identifiable is Registrable. Most things are registered directly, like Peers, but in some cases the representation in the blockchain has considerably more data. For security and performance reasons, we use builders for such data structures (e.g. NewAccount). As a rule, everything that can be registered, can also be un-registered, but that is not a hard and fast rule.

You can register domains, accounts, asset definitions, peers, roles, and triggers. Check our naming conventions to learn about the restrictions put on entity names.

INFO

Note that depending on how you decide to set up your genesis block in genesis.json (specifically, whether or not you include registration of permission tokens), the process for registering an account can be very different. In general, we can summarise it like this:

  • In a public blockchain, anyone should be able to register an account.
  • In a private blockchain, there can be a unique process for registering accounts. In a typical private blockchain, i.e. a blockchain without any unique processes for registering accounts, you need an account to register another account.

We discuss these differences in great detail when we compare private and public blockchains.

INFO

Registering a peer is currently the only way of adding peers that were not part of the original TRUSTED_PEERS array to the network.

Refer to one of the language-specific guides to walk you through the process of registering objects in a blockchain:

LanguageGuide
BashRegister a domain, an account, an asset
RustRegister a domain, an account, an asset
Kotlin/JavaRegister a domain, an account, an asset
PythonRegister a domain, an account, an asset
JavaScript/TypeScriptRegister a domain, an account, an asset

Mint/Burn

Minting and burning can refer to assets, triggers (if the trigger has a limited number of repetitions), and temporary permission tokens. Some assets can be declared as non-mintable, meaning that they can be minted only once after registration.

',21),b={class:"katex"},_={class:"katex-mathml"},y=i('',1),k=s("code",null,"time",-1),v=s("code",null,"Burn",-1),w=s("code",null,"Mint",-1),S=i('

Refer to one of the language-specific guides to walk you through the process of minting assets in a blockchain:

Here are examples of burning assets:

Transfer

Similar to mint and burn instructions, transferring refers to assets. You can transfer assets between different accounts.

To do this, an account have to be granted the permission to transfer assets. Refer to an example on how to transfer assets in Bash.

Grant/Revoke

Grant and revoke instructions are used for account permissions and roles.

Grant is used to permanently grant a user either a single permission, or a group of permissions (a "role"). Granted roles and permissions can only be removed via the Revoke instruction. As such, these instructions should be used carefully.

SetKeyValue/RemoveKeyValue

These instructions are used with the key/value Store asset type. This use case has not received much attention so far, because storing data in the blockchain is a rather advanced topic that we shall cover separately.

NewParameter/SetParameter

With these instructions, you can create (NewParameter) and change (SetParameter) chain-wide configuration parameters for Iroha.

ExecuteTrigger

This instruction is used to execute triggers.

Composite instructions

Iroha also offers composite instructions (If, Pair, Sequence) to execute instructions in a certain way:

  • If: execute one of the two given instructions based on a given condition
  • Sequence: execute a provided vector of instructions in a given order
  • Pair: execute both provided instructions in a specified order
',19);function I(x,T,P,R,q,A){const n=t("mo"),o=t("mn"),d=t("mrow"),c=t("annotation"),h=t("semantics"),u=t("math");return g(),m("div",null,[f,s("p",null,[e("Assets and permission tokens need to be minted to a specific account, usually the one that registered the asset in the first place. All assets are assumed to be non-negative as well, so you can never have "),s("span",b,[s("span",_,[a(u,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:r(()=>[a(h,null,{default:r(()=>[a(d,null,{default:r(()=>[a(n,null,{default:r(()=>[e("−")]),_:1}),a(o,null,{default:r(()=>[e("1.0")]),_:1})]),_:1}),a(c,{encoding:"application/x-tex"},{default:r(()=>[e("-1.0")]),_:1})]),_:1})]),_:1})]),y]),e(" of "),k,e(" or "),v,e(" a negative amount and get a "),w,e(".")]),S])}const N=l(p,[["render",I]]);export{j as __pageData,N as default}; diff --git a/assets/guide_blockchain_instructions.md.fb8ab4c5.lean.js b/assets/guide_blockchain_instructions.md.fb8ab4c5.lean.js new file mode 100644 index 000000000..4fe477415 --- /dev/null +++ b/assets/guide_blockchain_instructions.md.fb8ab4c5.lean.js @@ -0,0 +1 @@ +import{_ as l,C as t,o as g,c as m,k as s,a as e,H as a,w as r,Q as i}from"./chunks/framework.1293becd.js";const j=JSON.parse('{"title":"Iroha Special Instructions","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/instructions.md","filePath":"guide/blockchain/instructions.md","lastUpdated":1700563625000}'),p={name:"guide/blockchain/instructions.md"},f=i("",21),b={class:"katex"},_={class:"katex-mathml"},y=i("",1),k=s("code",null,"time",-1),v=s("code",null,"Burn",-1),w=s("code",null,"Mint",-1),S=i("",19);function I(x,T,P,R,q,A){const n=t("mo"),o=t("mn"),d=t("mrow"),c=t("annotation"),h=t("semantics"),u=t("math");return g(),m("div",null,[f,s("p",null,[e("Assets and permission tokens need to be minted to a specific account, usually the one that registered the asset in the first place. All assets are assumed to be non-negative as well, so you can never have "),s("span",b,[s("span",_,[a(u,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:r(()=>[a(h,null,{default:r(()=>[a(d,null,{default:r(()=>[a(n,null,{default:r(()=>[e("−")]),_:1}),a(o,null,{default:r(()=>[e("1.0")]),_:1})]),_:1}),a(c,{encoding:"application/x-tex"},{default:r(()=>[e("-1.0")]),_:1})]),_:1})]),_:1})]),y]),e(" of "),k,e(" or "),v,e(" a negative amount and get a "),w,e(".")]),S])}const N=l(p,[["render",I]]);export{j as __pageData,N as default}; diff --git a/assets/guide_blockchain_metadata.md.318a1743.js b/assets/guide_blockchain_metadata.md.318a1743.js new file mode 100644 index 000000000..e2c2288b7 --- /dev/null +++ b/assets/guide_blockchain_metadata.md.318a1743.js @@ -0,0 +1,119 @@ +import{_ as n,C as l,o,c as e,H as p,Q as s}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Metadata","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/metadata.md","filePath":"guide/blockchain/metadata.md","lastUpdated":1700563625000}'),t={name:"guide/blockchain/metadata.md"},c=s('

Metadata

Metadata are key-value pairs that are attached to objects in the blockchain. The following can contain metadata:

  • domains
  • accounts
  • assets
  • asset definitions
  • triggers
  • transactions

The metadata can be of very different types, such as:

  • structures with named or unnamed fields
  • enums
  • integers
  • numbers with fixed decimal precision
  • strings
  • Boolean values
  • arrays
  • associative arrays
  • vectors
  • request results

The object's metadata can be transferred one by one, or in bulk via a WASM transaction. The Store asset type is used for working with metadata. Let's take a closer look at this asset type.

MetadataChanged

MetadataInserted or MetadataRemoved events are emitted when metadata is inserted or removed from accounts, domains, assets, or asset definitions. The emitted event also contains the data that was inserted or removed from the object. This data is stored in MetadataChanged in the form of a (key, value) pair.

',8),r=s(`

Check data filters for details.

Store Asset

In Iroha 2 there is an asset called Store that was designed to be a package of data. You can use Store when you require a storage of key-value pairs. The SetKeyValue and RemoveKeyValue instructions are used with the Store asset type. Here is an example of SetKeyValue instruction:

rust
// Mouse's account
+let mouse_id: AccountId = "mouse@wonderland".parse();
+
+// Registering \`Store\` asset definition
+let hat_definition_id: AssetDefinitionId =
+    "hat#wonderland".parse();
+let new_hat_definition = AssetDefinition::store(hat_definition_id);
+let register_hat = RegisterBox::new(new_hat_definition);
+
+let mouse_hat_id = AssetId::new(hat_definition_id, mouse_id);
+
+// New Iroha Special Instruction for setting key-value pairs for Mouse's hats:
+let set_hat_color = SetKeyValueBox::new(
+    mouse_hat_id,
+    Name::from_str("color"),
+    "yellow".to_owned(),
+);
// Mouse's account
+let mouse_id: AccountId = "mouse@wonderland".parse();
+
+// Registering \`Store\` asset definition
+let hat_definition_id: AssetDefinitionId =
+    "hat#wonderland".parse();
+let new_hat_definition = AssetDefinition::store(hat_definition_id);
+let register_hat = RegisterBox::new(new_hat_definition);
+
+let mouse_hat_id = AssetId::new(hat_definition_id, mouse_id);
+
+// New Iroha Special Instruction for setting key-value pairs for Mouse's hats:
+let set_hat_color = SetKeyValueBox::new(
+    mouse_hat_id,
+    Name::from_str("color"),
+    "yellow".to_owned(),
+);

Working with metadata

The following example showcases how to register and grant a role to access another account's metadata.

Example
rust
#[test]
+fn register_and_grant_role_for_metadata_access() -> Result<()> {
+    let (_rt, _peer, test_client) = <PeerBuilder>::new().start_with_runtime();
+    wait_for_genesis_committed(&vec![test_client.clone()], 0);
+
+    let alice_id = AccountId::from_str("alice@wonderland")?;
+    let mouse_id = AccountId::from_str("mouse@wonderland")?;
+
+    // Registering Mouse
+    let mouse_key_pair = KeyPair::generate()?;
+    let register_mouse = RegisterBox::new(Account::new(
+        mouse_id.clone(),
+        [mouse_key_pair.public_key().clone()],
+    ));
+    test_client.submit_blocking(register_mouse)?;
+
+    // Registering role
+    let role_id = <Role as Identifiable>::Id::from_str("ACCESS_TO_MOUSE_METADATA")?;
+    let role = iroha_data_model::role::Role::new(role_id.clone())
+        .add_permission(CanSetKeyValueInUserMetadata::new(mouse_id.clone()))
+        .add_permission(CanRemoveKeyValueInUserMetadata::new(mouse_id.clone()));
+    let register_role = RegisterBox::new(role);
+    test_client.submit_blocking(register_role)?;
+
+    // Mouse grants role to Alice
+    let grant_role = GrantBox::new(role_id.clone(), alice_id.clone());
+    let grant_role_tx = Transaction::new(mouse_id.clone(), vec![grant_role.into()].into(), 100_000)
+        .sign(mouse_key_pair)?;
+    test_client.submit_transaction_blocking(grant_role_tx)?;
+
+    // Alice modifies Mouse's metadata
+    let set_key_value = SetKeyValueBox::new(
+        mouse_id,
+        Name::from_str("key").expect("Valid"),
+        Value::String("value".to_owned()),
+    );
+    test_client.submit_blocking(set_key_value)?;
+
+    // Making request to find Alice's roles
+    let found_role_ids = test_client.request(client::role::by_account_id(alice_id))?;
+    assert!(found_role_ids.contains(&role_id));
+
+    Ok(())
+}
#[test]
+fn register_and_grant_role_for_metadata_access() -> Result<()> {
+    let (_rt, _peer, test_client) = <PeerBuilder>::new().start_with_runtime();
+    wait_for_genesis_committed(&vec![test_client.clone()], 0);
+
+    let alice_id = AccountId::from_str("alice@wonderland")?;
+    let mouse_id = AccountId::from_str("mouse@wonderland")?;
+
+    // Registering Mouse
+    let mouse_key_pair = KeyPair::generate()?;
+    let register_mouse = RegisterBox::new(Account::new(
+        mouse_id.clone(),
+        [mouse_key_pair.public_key().clone()],
+    ));
+    test_client.submit_blocking(register_mouse)?;
+
+    // Registering role
+    let role_id = <Role as Identifiable>::Id::from_str("ACCESS_TO_MOUSE_METADATA")?;
+    let role = iroha_data_model::role::Role::new(role_id.clone())
+        .add_permission(CanSetKeyValueInUserMetadata::new(mouse_id.clone()))
+        .add_permission(CanRemoveKeyValueInUserMetadata::new(mouse_id.clone()));
+    let register_role = RegisterBox::new(role);
+    test_client.submit_blocking(register_role)?;
+
+    // Mouse grants role to Alice
+    let grant_role = GrantBox::new(role_id.clone(), alice_id.clone());
+    let grant_role_tx = Transaction::new(mouse_id.clone(), vec![grant_role.into()].into(), 100_000)
+        .sign(mouse_key_pair)?;
+    test_client.submit_transaction_blocking(grant_role_tx)?;
+
+    // Alice modifies Mouse's metadata
+    let set_key_value = SetKeyValueBox::new(
+        mouse_id,
+        Name::from_str("key").expect("Valid"),
+        Value::String("value".to_owned()),
+    );
+    test_client.submit_blocking(set_key_value)?;
+
+    // Making request to find Alice's roles
+    let found_role_ids = test_client.request(client::role::by_account_id(alice_id))?;
+    assert!(found_role_ids.contains(&role_id));
+
+    Ok(())
+}

Queries

You can get the key value of an object metadata using queries:

Permissions

Pre-configured tokens in Iroha 2 LTS version that allow to set or remove key-values in accounts, assets, or asset definitions:

`,13);function y(i,E,d,F,u,_){const a=l("MermaidRenderWrap");return o(),e("div",null,[c,p(a,{id:"mermaid_2d3f0b7624b6a36380f036629a9452425dc05d6c4a61988862ccf28002cdef5ae656eb9b068819f89cb73fb8fd390d7e0965c8b8866f9ed83002216af5c6db75",text:"classDiagram%0A%0Adirection%20LR%0A%0Aclass%20MetadataChanged~ID~%20%7B%0A%20%20%20%20target_id%3A%20ID%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0Aclass%20AccountMetadataChanged~AccountId~%0Aclass%20AssetMetadataChanged~AssetId~%0Aclass%20AssetDefinitionMetadataChanged~AssetDefinitionId~%0Aclass%20DomainMetadataChanged~DomainId~%0A%0AMetadataChanged%20--%3E%20AccountMetadataChanged%0AMetadataChanged%20--%3E%20AssetMetadataChanged%0AMetadataChanged%20--%3E%20AssetDefinitionMetadataChanged%0AMetadataChanged%20--%3E%20DomainMetadataChanged"}),r])}const h=n(t,[["render",y]]);export{m as __pageData,h as default}; diff --git a/assets/guide_blockchain_metadata.md.318a1743.lean.js b/assets/guide_blockchain_metadata.md.318a1743.lean.js new file mode 100644 index 000000000..ff22353f2 --- /dev/null +++ b/assets/guide_blockchain_metadata.md.318a1743.lean.js @@ -0,0 +1 @@ +import{_ as n,C as l,o,c as e,H as p,Q as s}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Metadata","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/metadata.md","filePath":"guide/blockchain/metadata.md","lastUpdated":1700563625000}'),t={name:"guide/blockchain/metadata.md"},c=s("",8),r=s("",13);function y(i,E,d,F,u,_){const a=l("MermaidRenderWrap");return o(),e("div",null,[c,p(a,{id:"mermaid_2d3f0b7624b6a36380f036629a9452425dc05d6c4a61988862ccf28002cdef5ae656eb9b068819f89cb73fb8fd390d7e0965c8b8866f9ed83002216af5c6db75",text:"classDiagram%0A%0Adirection%20LR%0A%0Aclass%20MetadataChanged~ID~%20%7B%0A%20%20%20%20target_id%3A%20ID%0A%20%20%20%20key%3A%20Name%0A%20%20%20%20value%3A%20Box~Value~%0A%7D%0A%0Aclass%20AccountMetadataChanged~AccountId~%0Aclass%20AssetMetadataChanged~AssetId~%0Aclass%20AssetDefinitionMetadataChanged~AssetDefinitionId~%0Aclass%20DomainMetadataChanged~DomainId~%0A%0AMetadataChanged%20--%3E%20AccountMetadataChanged%0AMetadataChanged%20--%3E%20AssetMetadataChanged%0AMetadataChanged%20--%3E%20AssetDefinitionMetadataChanged%0AMetadataChanged%20--%3E%20DomainMetadataChanged"}),r])}const h=n(t,[["render",y]]);export{m as __pageData,h as default}; diff --git a/assets/guide_blockchain_permissions.md.81c20431.js b/assets/guide_blockchain_permissions.md.81c20431.js new file mode 100644 index 000000000..1fa77b868 --- /dev/null +++ b/assets/guide_blockchain_permissions.md.81c20431.js @@ -0,0 +1,29 @@ +import{_ as s,o as e,c as o,Q as a}from"./chunks/framework.1293becd.js";const E=JSON.parse('{"title":"Permissions","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/permissions.md","filePath":"guide/blockchain/permissions.md","lastUpdated":1700563625000}'),n={name:"guide/blockchain/permissions.md"},t=a(`

Permissions

Accounts need permission tokens for various actions on a blockchain, e.g. to mint or burn assets.

There is a difference between a public and a private blockchain in terms of permissions granted to users. In a public blockchain, most accounts have the same set of permissions. In a private blockchain, most accounts are assumed not to be able to do anything outside of their own account or domain unless explicitly granted said permission.

Having a permission to do something means having a PermissionToken to do so. There are two ways for users to receive permission tokens: they can be granted directly or as a part of a Role (a set of permission tokens). Permissions are granted via Grant special instruction. Permission tokens and roles do not expire, they can only be removed using Revoke instruction.

Permission Tokens

Permission token definitions have parameters. When a new permission token is registered, the names of the parameters and their types are checked against their names and types in the token definition. The token registration fails if there are too few parameters, if the parameter types don't match the definition, or parameters with unrecognised names.

Here are some examples of parameters used for various permission tokens:

  • A token that grants permission to change the values associated to keys in a Store asset needs the asset_definition_id parameter of type Id:

    json
    "params": {
    +     "asset_definition_id": "Id"
    +}
    "params": {
    +     "asset_definition_id": "Id"
    +}
  • By contrast, the permission token that grants the permission to set keys to values in user metadata needs the account_id parameter of type Id:

    json
    "params": {
    +  "account_id": "Id"
    +}
    "params": {
    +  "account_id": "Id"
    +}
  • The permission token that grants the permission to transfer assets only a fixed number of times per some time period, needs these two parameters:

    json
    "params": {
    +  "count": "U32",
    +  "period": "U128"
    +}
    "params": {
    +  "count": "U32",
    +  "period": "U128"
    +}

    Where the period is specified as a standard Duration since the UNIX epoch in milliseconds (more details about time in Rust).

Pre-configured Permission Tokens

You can find the list of pre-configured permission tokens in the Reference chapter.

Permission Groups (Roles)

A set of permissions is called a role. Similarly to permission tokens, roles can be granted using the Grant instruction and revoked using the Revoke instruction.

Before granting a role to an account, the role should be registered first.

Register a new role

Let's register a new role that, when granted, will allow another account access to the metadata in Mouse's account:

rust
let role_id = RoleId::from_str("ACCESS_TO_MOUSE_METADATA")?;
+let role = iroha_data_model::role::Role::new(role_id)
+    .add_permission(CanSetKeyValueInUserMetadata::new(mouse_id))
+    .add_permission(CanRemoveKeyValueInUserMetadata::new(mouse_id));
+let register_role = RegisterBox::new(role);
let role_id = RoleId::from_str("ACCESS_TO_MOUSE_METADATA")?;
+let role = iroha_data_model::role::Role::new(role_id)
+    .add_permission(CanSetKeyValueInUserMetadata::new(mouse_id))
+    .add_permission(CanRemoveKeyValueInUserMetadata::new(mouse_id));
+let register_role = RegisterBox::new(role);

Grant a role

After the role is registered, Mouse can grant it to Alice:

rust
let grant_role = GrantBox::new(role_id, alice_id);
+let grant_role_tx =
+    Transaction::new(mouse_id, vec![grant_role.into()].into(), 100_000)
+    .sign(mouse_key_pair)?;
let grant_role = GrantBox::new(role_id, alice_id);
+let grant_role_tx =
+    Transaction::new(mouse_id, vec![grant_role.into()].into(), 100_000)
+    .sign(mouse_key_pair)?;

Permission Validators

Permissions exist so that only those accounts that have a required permission token to perform a certain action could do so.

The Judge trait is used to check permissions. The Judge decides whether a certain operation (instruction, query, or expression) could be performed based on the verdicts of multiple validators.

Each validator returns one of the following verdicts: Deny (with the exact reason to deny an operation), Skip (if an operation is not supported or has no meaning in a given context), or Allow.

There are several implementations of the Judge trait in Iroha 2, such as:

JudgeDescription
AtLeastOneAllowThe judge that succeeds only if there is at least one Allow verdict. The execution is stopped once there is a first Allow verdict.
NoDeniesThe judge that succeeds only if there is no Deny verdict. All validators are checked.
NoDeniesAndAtLeastOneAllowThe judge that succeeds only if there is no Deny verdict and at least one Allow verdict. The execution is stopped once there is a Deny verdict or all validators were checked.
AllowAllFor tests and simple cases. All operations are allowed to be executed for all possible values.
DenyAllFor tests and simple cases. All operations are disallowed to be executed for all possible for all possible values.

You can also build a custom permission validator by combining multiple validators, all of which should be of the same type (for checking instructions, queries, or expressions).

Runtime Validators

Currently Iroha 2 has only built-in validators. In the future, built-in validators will be completely replaced with runtime validators that use WASM.

The chain of runtime validators is used to validate operations that require permissions. It works similarly to the Chain of responsibility.

All runtime validators return validation verdict. By default, all operations are considered valid unless proven otherwise. Validators check whether or not an operation is not allowed: each validator either allows an operation and passes it to the following validator, or denies the operation. The validation stops at the first Deny verdict.

Supported Queries

Permission tokens and roles can be queried.

Queries for roles:

Queries for permission tokens:

`,36),l=[t];function r(p,i,c,d,y,u){return e(),o("div",null,l)}const m=s(n,[["render",r]]);export{E as __pageData,m as default}; diff --git a/assets/guide_blockchain_permissions.md.81c20431.lean.js b/assets/guide_blockchain_permissions.md.81c20431.lean.js new file mode 100644 index 000000000..2eaf8c215 --- /dev/null +++ b/assets/guide_blockchain_permissions.md.81c20431.lean.js @@ -0,0 +1 @@ +import{_ as s,o as e,c as o,Q as a}from"./chunks/framework.1293becd.js";const E=JSON.parse('{"title":"Permissions","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/permissions.md","filePath":"guide/blockchain/permissions.md","lastUpdated":1700563625000}'),n={name:"guide/blockchain/permissions.md"},t=a("",36),l=[t];function r(p,i,c,d,y,u){return e(),o("div",null,l)}const m=s(n,[["render",r]]);export{E as __pageData,m as default}; diff --git a/assets/guide_blockchain_queries.md.b6c7f793.js b/assets/guide_blockchain_queries.md.b6c7f793.js new file mode 100644 index 000000000..d98b48797 --- /dev/null +++ b/assets/guide_blockchain_queries.md.b6c7f793.js @@ -0,0 +1,13 @@ +import{o as s,c as a,Q as e}from"./chunks/framework.1293becd.js";const n=e(`

Queries

Although much of the information about the state of the blockchain can be obtained, as we've shown before, using an event subscriber and a filter to narrow the scope of the events to those of interest, sometimes you need to take a more direct approach. Enter queries.

Queries are small instruction-like objects that, when sent to an Iroha peer, prompt a response with details from the current world state view.

This is not necessarily the only kind of information that is available on the network, but it's the only kind of information that is guaranteed to be accessible on all networks.

For each deployment of Iroha, there might be other available information. For example, the availability of telemetry data is up to the network administrators. It's entirely their decision whether or not they want to allocate processing power to track the work instead of using it to do the actual work. By contrast, some functions are always required, e.g. having access to your account balance.

The results of queries can be sorted, paginated and filtered peer-side all at once. Sorting is done lexicographically on metadata keys. Filtering can be done on a variety of principles, from domain-specific (individual IP address filter masks) to sub-string methods like begins_with combined using logical operations.

Create a query

Use QueryBox to construct a query. For example, a query to find all accounts would be created like this:

rust
let query = QueryBox::FindAllAccounts(FindAllAccounts {});
let query = QueryBox::FindAllAccounts(FindAllAccounts {});

Here is an example of a query that finds Alice's assets:

rust
let alice_id =
+    AccountId::from_str("alice@wonderland")?;
+let query = QueryBox::FindAssetsByAccountId(
+    FindAssetsByAccountId::new(alice_id)
+  );
let alice_id =
+    AccountId::from_str("alice@wonderland")?;
+let query = QueryBox::FindAssetsByAccountId(
+    FindAssetsByAccountId::new(alice_id)
+  );

Pagination

For both a Vec<Z> and just Z as the return type, you can use client.request(query) to submit a query and get the full result in one go.

However, some queries, particularly the ones with "All" in their names, can return exorbitant amounts of data. As such, we highly recommend you consider pagination to reduce the load on the system.

To construct a Pagination, you need to call client.request_with_pagination(query, pagination), where the pagination is constructed as follows:

rust
let starting_result: u32 = _;
+let limit: u32 = _;
+let pagination = Pagination::new(Some(starting_result), Some(limit));
let starting_result: u32 = _;
+let limit: u32 = _;
+let pagination = Pagination::new(Some(starting_result), Some(limit));

Filters

When you create a query, you can use a filter to only return the results that match the specified filter.

Sorting

Iroha 2 can sort items with metadata lexicographically if you provide a key to sort by during the construction of the query. A typical use case is for accounts to have a registered-on metadata entry, which, when sorted, allows you to view the account registration history.

Sorting only applies to entities that have metadata, as the metadata key is used to sort query results.

You can combine sorting with pagination and filters. Note that sorting is an optional feature, most queries with pagination won't need it.

Reference

Check the list of existing queries for detailed information about them.

`,24),o=[n],i=JSON.parse('{"title":"Queries","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/queries.md","filePath":"guide/blockchain/queries.md","lastUpdated":1700563625000}'),t={name:"guide/blockchain/queries.md"},y=Object.assign(t,{setup(l){return(p,r)=>(s(),a("div",null,o))}});export{i as __pageData,y as default}; diff --git a/assets/guide_blockchain_queries.md.b6c7f793.lean.js b/assets/guide_blockchain_queries.md.b6c7f793.lean.js new file mode 100644 index 000000000..c4308a847 --- /dev/null +++ b/assets/guide_blockchain_queries.md.b6c7f793.lean.js @@ -0,0 +1 @@ +import{o as s,c as a,Q as e}from"./chunks/framework.1293becd.js";const n=e("",24),o=[n],i=JSON.parse('{"title":"Queries","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/queries.md","filePath":"guide/blockchain/queries.md","lastUpdated":1700563625000}'),t={name:"guide/blockchain/queries.md"},y=Object.assign(t,{setup(l){return(p,r)=>(s(),a("div",null,o))}});export{i as __pageData,y as default}; diff --git a/assets/guide_blockchain_transactions.md.f5c57974.js b/assets/guide_blockchain_transactions.md.f5c57974.js new file mode 100644 index 000000000..6ef637dd5 --- /dev/null +++ b/assets/guide_blockchain_transactions.md.f5c57974.js @@ -0,0 +1,7 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Transactions","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/transactions.md","filePath":"guide/blockchain/transactions.md","lastUpdated":1700563625000}'),l={name:"guide/blockchain/transactions.md"},e=o(`

Transactions

A transaction is a collection of instructions. The instructions within a transaction can be executed in a sequence or compiled into a WASM blob.

All interactions in the blockchain are done via transactions.

Here is an example of creating a new transaction with the Grant instruction. In this transaction, Mouse is granting Alice the specified role (role_id). Check the full example.

rust
let grant_role = GrantBox::new(role_id, alice_id);
+let grant_role_tx =
+    Transaction::new(mouse_id, vec![grant_role.into()].into(), 100_000)
+    .sign(mouse_key_pair)?;
let grant_role = GrantBox::new(role_id, alice_id);
+let grant_role_tx =
+    Transaction::new(mouse_id, vec![grant_role.into()].into(), 100_000)
+    .sign(mouse_key_pair)?;
`,5),t=[e];function p(c,r,i,y,E,d){return a(),n("div",null,t)}const u=s(l,[["render",p]]);export{h as __pageData,u as default}; diff --git a/assets/guide_blockchain_transactions.md.f5c57974.lean.js b/assets/guide_blockchain_transactions.md.f5c57974.lean.js new file mode 100644 index 000000000..4d4021880 --- /dev/null +++ b/assets/guide_blockchain_transactions.md.f5c57974.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Transactions","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/transactions.md","filePath":"guide/blockchain/transactions.md","lastUpdated":1700563625000}'),l={name:"guide/blockchain/transactions.md"},e=o("",5),t=[e];function p(c,r,i,y,E,d){return a(),n("div",null,t)}const u=s(l,[["render",p]]);export{h as __pageData,u as default}; diff --git a/assets/guide_blockchain_triggers.md.f630788f.js b/assets/guide_blockchain_triggers.md.f630788f.js new file mode 100644 index 000000000..4ba13d254 --- /dev/null +++ b/assets/guide_blockchain_triggers.md.f630788f.js @@ -0,0 +1,91 @@ +import{_ as a,C as n,o,c as t,H as l,Q as s}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Triggers","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/triggers.md","filePath":"guide/blockchain/triggers.md","lastUpdated":1700563625000}'),p={name:"guide/blockchain/triggers.md"},r=s(`

Triggers

Certain things, such as changing the state of an entity, committing a block or executing an Iroha Special Instruction (ISI), can emit events, and you can attach triggers to these events.

A trigger is a fairly basic entity that can be registered. Just like with Accounts, to register a trigger, you submit a RegisterBox::Trigger, which contains the necessary information:

  • an account ID, which should ideally be a brand new account that you register in the same transaction
  • an executable, which itself is either a Vec<Instruction> or a WASM blob
  • an EventFilter[1], which is something that combs through all[2] events and returns true when it finds the matching event to start the execution

Let's take a closer look at how triggers work.

The Anatomy of a Trigger

A trigger has roughly the following form:

rust
struct Trigger {
+  id: TriggerId,
+  action: Action,
+}
struct Trigger {
+  id: TriggerId,
+  action: Action,
+}

Trigger.id

The TriggerId is a simple wrapper around a single Name, a string with no whitespaces and no reserved characters (@, #, $).

A typical domain-scoped trigger looks like trigger_id$domain_name, while a bare trigger looks like @@trigger_id, which makes these names easy to parse.

Trigger.action

An Action is the heart of the trigger. It is defined like this:

rust
struct Action {
+  executable: Executable,
+  repeats: Repeats,
+  technical_account: AccountId,
+  filter: EventFilter,
+  metadata: Metadata,
+}
struct Action {
+  executable: Executable,
+  repeats: Repeats,
+  technical_account: AccountId,
+  filter: EventFilter,
+  metadata: Metadata,
+}

Action.executable

The executable linked to this action, either a Vec<Instruction> or a WASM binary.

Action.repeats

The Repeats is a universal enumeration of all possible repetition schemes.

rust
enum Repeats {
+  Indefinitely,
+  Exactly(u32),
+}
enum Repeats {
+  Indefinitely,
+  Exactly(u32),
+}

Action.technical_account

A technical account is the account that would (in theory) be responsible for the execution environment and be the authority for Instruction execution.

For now, you can leave this as the account that registered the trigger. If you have been following the tutorial, this is alice@wonderland. However, later on we will show you why you'd want to create a brand new account for those purposes.

INFO

Note that you can only use the account that already exists in order to be able to register a new trigger.

Action.filter

A filter is what determines what kind of trigger you're dealing with. All triggers respond to events, but the precise type of event that activates a trigger depends on which EventFilter was used.

The reason why we chose this architecture is simple; front end code has an abundance of event filters, and so, your knowledge of filters is transferable to writing smart contracts.

Action.metadata

This Metadata is the same kind of Metadata that can be attached to accounts, domains, assets, or transactions. This is the storage for trigger data.

You can learn more about metadata in a dedicated section.

How Triggers Work

As we already said, the filter that is used to register a trigger determines what kind of trigger this is. It is, of course, also determines how the trigger works, e.g. when it is executed. We will go into more details about the types of triggers in just a moment.

First, we shall point out that there two other characteristics of a trigger that determine how this trigger works: its scope and repetition schema.

Scope

Triggers can be scoped and un-scoped. Iroha supports both un-scoped system-wide triggers as well as domain-scoped triggers. Since system-wide triggers scan all events, and domain-scoped triggers only scan events emitted in a certain domain, it is highly recommended to use domain-scoped triggers where possible.

INFO

Be mindful of the limitations. Currently triggers don't check for permissions , so they can do things your account wouldn't be allowed to. Un-scoped triggers process every event, and the amount of work grows quadratically.

Domain-scoped Triggers

While un-scoped triggers check all events of a specified type, domain-scoped triggers only look for events in a given domain. These triggers are more optimised compared to un-scoped triggers.

You can use FindTriggersByDomainId query to find triggers for the given domain.

When you register a domain-scoped trigger, you need to add the domain id to the trigger id using $ symbol: my_trigger$my_domain.

Repetition Schema

Each such trigger can be set to repeat either Indefinitely or Exactly(n) times, where n is a 32-bit integer. Once the number of repetitions reaches zero, the trigger is gone. That means that if your trigger got repeated exactly n times, you can't Mint new repetitions, you have to Register it again, with the same name.

After a trigger is repeated for the last time, i.e. the execution count reaches 0, the trigger should be un-registered.

Types of Triggers

We shall cover the following basic types of triggers and provide you with the detailed information on how to use each of them:

All triggers are essentially event triggers. The type of a trigger is determined by the type of an event that trigger is associated with. This, in turn, is determined by the filter used to register a trigger.

`,46),c=s(`

Data Triggers

This category includes the largest variety of triggers. The events that are associated with this trigger type account for the vast majority of events in Ethereum. These are data-related events, such as: an account got registered, an asset got transferred, the Queen of Hearts decided to burn all of her assets.

Time Triggers

Time triggers behave slightly differently compared to data triggers. There are two sub-types of this type: scheduled triggers and pre-commit triggers.

Instead of processing all the events generated by normal transactions, all time triggers process one event: the block formation event.

The filters of scheduled triggers are only interested in the timestamp provided in that event, but not the block height, and not the current time. They are executed according to a certain schedule. Pre-commit triggers, on the other hand, are executed right before a block is committed.

Scheduled Triggers

When going through consensus, all peers must agree on which triggers got executed. Scheduled triggers can't use real time, because then you can easily create a situation when they would never agree: e.g. by giving the Repeats::Indefinitely trigger a period that is smaller than the time it takes to pass consensus. It's really that simple.

So instead of using the actual current time at each peer, we use the time when the block got started plus a small offset. All triggers before that point in time get executed. All triggers that would be executed after that time wait for the next block.

Why we use the offset

The reason why we add this offset has to do with Iroha being best effort.

Imagine if we didn't have the offset... Normally, triggers would be set to nice round numbers; e.g. 12:00, 12:05, 11:55, etc. (as opposed to e.g. 11:59). However, the consensus can start at any point in time and could last a while.

Suppose that the block started to form at 11:56 and consensus finished at 12:03 (which is optimistically quick). Let's consider different scenarios:

  • If your trigger was supposed to run at 11:55, you'd be happy, since your trigger got executed just 1 minute late.
  • If your trigger was supposed to run at 12:05, it will run in the next block, not the one that was formed at 11:56. If you're the author and you're looking at the time stamp of 12:03, it makes sense, your trigger wasn't supposed to run yet.
  • For the trigger scheduled for 12:00, the situation is different. You look at the clock, you see 12:03, which is when the blockchain explorer shows you the block data was committed, but you don't see your trigger. It was supposed to run, but didn't.

So, the offset is meant to anticipate when the block would get added to the chain, so that people who were just 4 minutes early don't have to be potentially several hours late.

Because more triggers get executed sooner, your throughput is also infinitesimally smaller.

We could also say "you should aim to execute your trigger slightly earlier than consensus starts", but people writing smart contracts already have too much to worry about.

Pre-commit Triggers

This is a variant of timed triggers that gets run before blocks with transactions get committed. It leaves a special event to be triggered in the next block. Effectively, it's a delayed pre-commit that can track the behaviour of transactions in the pipeline.

INFO

These triggers are not meant for restricting the execution of transactions.

If you want to stop your users from transferring more than X amount of Y to user Z, you really want a permission. While you could hack the pre-commit triggers to emulate the desired behaviour, this is not economical neither in terms of gas fees nor computation.

Until Iroha 2 supports WASM-based permissions validators, however, your only choice is pre-commit triggers.

By-call Triggers

These triggers only get executed once the CallTrigger(trigger_name) instruction is executed. They can be useful if you want to achieve dynamic linkage between different smart contract modules.

Space is precious, so you want to use as little of it as you can. Thus, you follow the UNIX design philosophy, and instead of creating one large smart contract, you create many smaller ones, and re-use as much logic as you can.

INFO

Of course, this is a rather exotic use case, so it shall be implemented last.

Event Triggers by Example

Now that we've gotten the theory out of the way, we want to sit down with the Mad Hatter, the March Hare, and the Dormouse and see if we can spin. Let's start with an event trigger that shows the basics.

1. Register accounts

We have mad_hatter@wonderland, dormouse@wonderland, march_hare@wonderland all of which share the fixed-point asset of tea#wonderland. The Mad Hatter has the tea pot, while the rest have a single cup of tea. When alice@wonderland had arrived, she got a nice cup of tea as well.

The way we get them in Rust code looks like this:

rust
let tea = AssetDefinitionId::new("tea", "wonderland")?;
+let mad_hatter = AccountId::new("mad_hatter", "wonderland")?;
+let dormouse = AccountId::new("dormouse", "wonderland")?;
+let march_hare = AccountId::new("march_hare", "wonderland")?;
+vec![
+  RegisterBox::new(IdentifiableBox::from(NewAccount::new(mad_hatter.clone()))),
+  RegisterBox::new(IdentifiableBox::from(NewAccount::new(march_hare.clone()))),
+  RegisterBox::new(IdentifiableBox::from(NewAccount::new(dormouse.clone()))),
+  RegisterBox::new(IdentifiableBox::from(AssetDefinition::new_fixed(tea.clone()))),
+  MintBox::new(Value::Fixed(100.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), mad_hatter.clone())))
+  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), march_hare.clone())))
+  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), dormouse.clone())))
+  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), alice.clone())))
+]
let tea = AssetDefinitionId::new("tea", "wonderland")?;
+let mad_hatter = AccountId::new("mad_hatter", "wonderland")?;
+let dormouse = AccountId::new("dormouse", "wonderland")?;
+let march_hare = AccountId::new("march_hare", "wonderland")?;
+vec![
+  RegisterBox::new(IdentifiableBox::from(NewAccount::new(mad_hatter.clone()))),
+  RegisterBox::new(IdentifiableBox::from(NewAccount::new(march_hare.clone()))),
+  RegisterBox::new(IdentifiableBox::from(NewAccount::new(dormouse.clone()))),
+  RegisterBox::new(IdentifiableBox::from(AssetDefinition::new_fixed(tea.clone()))),
+  MintBox::new(Value::Fixed(100.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), mad_hatter.clone())))
+  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), march_hare.clone())))
+  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), dormouse.clone())))
+  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), alice.clone())))
+]

2. Register a trigger

We want a smart contract that transfers some tea from mad_hatter@wonderland to alice@wonderland when her tea reduces by a single cup.

For that we need to register a trigger. The boilerplate is straightforward:

rust
let id = TriggerId::new(Name::new("refresh_tea"));
+
+let metadata = Metadata::new();
+
+let executable = vec![
+    TransferBox::new(
+      IdBox::AssetId(AssetId::new(tea.clone(), mad_hatter.clone())),
+      Value::Fixed(1_f64.try_into()?),
+      IdBox::AssetId(AssetId::new(alice.clone(), mad_hatter.clone())),
+    )
+];
+
+let repeats = Repeats::Indefinitely;
+
+let technical_account = mad_hatter.clone();
+
+let filter = _ // ...
let id = TriggerId::new(Name::new("refresh_tea"));
+
+let metadata = Metadata::new();
+
+let executable = vec![
+    TransferBox::new(
+      IdBox::AssetId(AssetId::new(tea.clone(), mad_hatter.clone())),
+      Value::Fixed(1_f64.try_into()?),
+      IdBox::AssetId(AssetId::new(alice.clone(), mad_hatter.clone())),
+    )
+];
+
+let repeats = Repeats::Indefinitely;
+
+let technical_account = mad_hatter.clone();
+
+let filter = _ // ...

3. Define an event filter

The event filter is where we need to spend some time and think. So far we've seen the Pipeline variety of filters. This time around, the filter is a Data kind. This type of filter is a tuple with a single variant, which is a FilterOpt of an EntityFilter:

  • FilterOpt stands for Optional Filter. It can either AcceptAll or accept BySome of another Filter.
  • An EntityFilter is a filter that matches ByAccount in our case, but can match by many other means. It wraps an AccountFilter, which matches various events produced on accounts.

What we want to do is create an event filter for when alice@wonderland drinks some of her tea, or, in other words, reduces the tea asset by any amount. To do this with the current API, we need to work bottom up.

An IdFilter is a filter that .matches(event) == true if and only if the identities are exactly the same. Everything that has an Id has a corresponding IdFilter.

INFO

An IdFilter is a parametric structure, an IdFilter that works on Peers has the type IdFilter<PeerId> and is not the same type as an IdFilter that works with AccountId; IdFilter<AccountId.

Now if we wanted a filter that will match whenever tea gets reduced, either through a Transfer or a Burn instruction, we need an AssetFilter. It needs to look at what the Id of the asset is, hence IdFilter<AssetDefinitionId> and ByRemoved.

rust
use FilterOpt::{BySome, AcceptAll};
+
+let asset_filter = AssetFilter::new(BySome(IdFilter(tea.clone())), BySome(AssetEventFilter::ByRemoved));
use FilterOpt::{BySome, AcceptAll};
+
+let asset_filter = AssetFilter::new(BySome(IdFilter(tea.clone())), BySome(AssetEventFilter::ByRemoved));

So far so good?

Next, we want a filter that looks for changes in an asset for an account. Specifically:

rust
let account_filter = AccountFilter::new(BySome(IdFilter(alice.clone())), BySome(asset_filter));
let account_filter = AccountFilter::new(BySome(IdFilter(alice.clone())), BySome(asset_filter));

Now, because of the way that parity_scale_codec works, we need to wrap this in various boxes.

rust
let filter = EventFilter::Data(BySome(EntityFilter::ByAccount(account_filter)));
let filter = EventFilter::Data(BySome(EntityFilter::ByAccount(account_filter)));

4. Create a Trigger instance

After this somewhat laborious filter combination, we can create an Action

rust
let action = Action {
+    executable, repeats, technical_account, filter, metadata
+}
let action = Action {
+    executable, repeats, technical_account, filter, metadata
+}

Which allows us to create an instance of a Trigger.

rust
let trigger = Trigger::new(id, action);
let trigger = Trigger::new(id, action);

5. Create a transaction

Finally, in order to get said trigger onto the blockchain, we create a transaction with the following single instruction:

rust
Instruction::Register(RegisterBox::new(IdentifiableBox::Trigger(Box::new(trigger))));
Instruction::Register(RegisterBox::new(IdentifiableBox::Trigger(Box::new(trigger))));

How it works

The technical details of the created transaction are summarised as follows:

  • The (normal) instructions that either got submitted from WASM or directly from the client get executed. If there were any triggers that should have been registered, they get registered.
  • Using the total set of events that got generated during the execution of instructions, the triggers (including some that got registered just this round) get executed.
  • The events produced in the previous step get scheduled for execution in the next block.

INFO

The reason why the events caused by the execution of triggers get processed in the next block is that we don't want two triggers to inadvertently cause an infinite loop of instruction execution and break consensus.

Now each time Alice drinks some tea, the Mad Hatter pours in a whole new cup. The keen eyed among you will have noticed that the amount that Alice drinks is irrelevant to how much tea will be transferred. Alice may take a tiny sip and still be poured a whole new cup's worth.

INFO

We intend to address this issue in the future so that an emitted event also has an attached Value. We also intend to provide more event filter types. For example, we will have filters that match when the asset:

  • Decreases by any amount (current behaviour)
  • Decreases by more than (or exactly) the specific amount in one instruction
  • Decreases to below a certain threshold

Only the first type of event filter is implemented now, and the other two can be emulated using a WASM smart contract as the Executable.

Why not WASM

The above observation can be generalised. WASM can do any logic that a Turing complete machine could, using the data available via queries. So in theory for event-based triggers, you could create an AcceptAll event filter and do all of the processing using the key-value store as persistent storage, and then, determining if you want to execute using easy-to-understand Rust code, and not our admittedly cumbersome, EventFilters.

We don't want that. WASM takes up significantly more space, and takes longer to execute compared to plain ISI, which are slower than EventFilters. We want you to want to use the EventFilters because they would make the process much more efficient, and we are working tirelessly to make the experience of using event filters much less gruelling.

However, as was mentioned previously on several occasions, implementing a feature properly takes time and effort. Ergonomics must be balanced against safety and reliability, so we cannot just make things easier to use. We want them to retain many of the advantages of strong typing.

This is all a work in progress. Our code is in flux. We need time to play around with a particular implementation to optimise it.

Supported ISI

All Iroha Special Instructions work with triggers, specifically:

  • Register<Trigger>: Create a trigger object and subscribe it to global events.

  • Unregister<Trigger>: Remove a trigger from the World State View and stop passing events through it.

  • Mint<Trigger, u32>: For triggers that repeat a certain number of times, increase the number of times that the trigger gets executed. Can be done from inside the executable of the trigger.

  • Burn<Trigger, u32>: For triggers that repeat a certain number of times, decrease the number of times that the trigger gets executed.

    WARNING

    If the number provided is greater than the remaining number of repetitions, the instruction will fail to execute, and the transaction that it is part of will be rejected.

You can learn more about Iroha Special Instructions in the dedicated section.

Supported Queries

We list supported queries for triggers when we talk in more detail about queries in the next chapter.


  1. The documentation on the EventFilter types is under construction, as we are likely to make major changes to that particular architecture. For now, suffice it to say that you can look at the source code in iroha_data_model and see a few particularly interesting applications. ↩︎

  2. This behaviour is likely to change in future releases.

    ↩︎
`,63);function i(y,d,h,E,g,u){const e=n("MermaidRenderWrap");return o(),t("div",null,[r,l(e,{id:"mermaid_79eaa517dfee336607034fec2fd701367be409be118356c9efd26a71dab658d37df3c8bb95a16a92b1cabc6118ec2e6ba2fda13bc753c4dd54aeaee6076b6256",text:"classDiagram%0A%0Aclass%20Trigger~F%3A%20Filter~%0A%0Aclass%20time_trigger~TimeEventFilter~%0Aclass%20data_trigger~DataEventFilter~%0Aclass%20by_call_trigger~ExecuteTriggerEventFilter~%0Aclass%20pipeline_trigger~PipelineEventFilter~%0A%0Aclass%20precommit_trigger~TimeEventFilter(ExecutionTime%3A%3APreCommit)~%0Aclass%20scheduled_trigger~TimeEventFilter(ExecutionTime%3A%3ASchedule(schedule))~%0A%0ATrigger%20--%3E%20time_trigger%0ATrigger%20--%3E%20by_call_trigger%0ATrigger%20--%3E%20data_trigger%0ATrigger%20--%3E%20pipeline_trigger%0A%0Atime_trigger%20--%3E%20precommit_trigger%20%0Atime_trigger%20--%3E%20scheduled_trigger"}),c])}const f=a(p,[["render",i]]);export{m as __pageData,f as default}; diff --git a/assets/guide_blockchain_triggers.md.f630788f.lean.js b/assets/guide_blockchain_triggers.md.f630788f.lean.js new file mode 100644 index 000000000..e1781ec84 --- /dev/null +++ b/assets/guide_blockchain_triggers.md.f630788f.lean.js @@ -0,0 +1 @@ +import{_ as a,C as n,o,c as t,H as l,Q as s}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Triggers","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/triggers.md","filePath":"guide/blockchain/triggers.md","lastUpdated":1700563625000}'),p={name:"guide/blockchain/triggers.md"},r=s("",46),c=s("",63);function i(y,d,h,E,g,u){const e=n("MermaidRenderWrap");return o(),t("div",null,[r,l(e,{id:"mermaid_79eaa517dfee336607034fec2fd701367be409be118356c9efd26a71dab658d37df3c8bb95a16a92b1cabc6118ec2e6ba2fda13bc753c4dd54aeaee6076b6256",text:"classDiagram%0A%0Aclass%20Trigger~F%3A%20Filter~%0A%0Aclass%20time_trigger~TimeEventFilter~%0Aclass%20data_trigger~DataEventFilter~%0Aclass%20by_call_trigger~ExecuteTriggerEventFilter~%0Aclass%20pipeline_trigger~PipelineEventFilter~%0A%0Aclass%20precommit_trigger~TimeEventFilter(ExecutionTime%3A%3APreCommit)~%0Aclass%20scheduled_trigger~TimeEventFilter(ExecutionTime%3A%3ASchedule(schedule))~%0A%0ATrigger%20--%3E%20time_trigger%0ATrigger%20--%3E%20by_call_trigger%0ATrigger%20--%3E%20data_trigger%0ATrigger%20--%3E%20pipeline_trigger%0A%0Atime_trigger%20--%3E%20precommit_trigger%20%0Atime_trigger%20--%3E%20scheduled_trigger"}),c])}const f=a(p,[["render",i]]);export{m as __pageData,f as default}; diff --git a/assets/guide_blockchain_wasm.md.e977d18a.js b/assets/guide_blockchain_wasm.md.e977d18a.js new file mode 100644 index 000000000..491c6c909 --- /dev/null +++ b/assets/guide_blockchain_wasm.md.e977d18a.js @@ -0,0 +1,73 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"WASM","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/wasm.md","filePath":"guide/blockchain/wasm.md","lastUpdated":1700563625000}'),e={name:"guide/blockchain/wasm.md"},l=o(`

WASM

While we had initially assumed that all operations within Iroha will be handled with instructions and conditionals, however, there are a few problems with this approach.

  • The ISI syntax is verbose and ugly.
  • The ISI syntax is not familiar for most programmers.
  • While simple ISI smart contracts are compact (usually a few bytes), they need different kinds of manual optimisations.

In the long run, all of these problems are taken care of by using a domain-specific language, which gets optimised and compiled into a sequence of instructions that executes fast and takes very little space in the blocks, but is also easy to understand. Something that looks like your traditional if statements and for loops.

However, in the interim, we have decided to use another portable binary standard called Web assembly, or WASM.

Working with WASM

The main advantage of using the WASM format is that you can use any language you like (as long as it links statically against our helper library), and produce a 32-bit portable executable. The compilers take care of the optimisation, and you don't have to learn a new language (ahem... solidity... ahem), just to operate on the blockchain.

You'd still need to use ISI from inside your WASM binary to do anything on-chain, as we explained earlier.

In theory, you can do anything you want just using ISI as it is a Turing-complete language. However, it'll be less convenient and efficient since you'd need to use metadata as memory and write complex conditionals using just the tools that we've provided in the Expression and ISI infrastructure. We highly recommend choosing a well-known programming language, such as Rust, to build the necessary logic out of simple instructions. This is much easier than trying to reinvent the wheel using ISI.

Moreover, as long as you fit within the limits of WASM runtime and the provided libraries, you can do anything (and everything) you want. The drawback is that this process is a tad more involved than just writing the ISI using the client libraries.

Simple Rust Smart Contract Example

WASM projects, just like any other binary in Rust, need to be separate crates. Don't worry, it doesn't have to be big.

1. Create a new project

To get started, create a new project:

bash
$ cargo new --lib
$ cargo new --lib

Yes! We need the lib type; more on that later.

The Cargo.toml of your project should look something like this:

toml
[package]
+name  = "smartcontract"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ['cdylib']
+
+[dependencies]
+iroha_wasm = { git = "https://github.com/hyperledger/iroha/", branch = "iroha2-dev" }
[package]
+name  = "smartcontract"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ['cdylib']
+
+[dependencies]
+iroha_wasm = { git = "https://github.com/hyperledger/iroha/", branch = "iroha2-dev" }

Note that the crate type is cdylib. Most Rust code is linked in a non-portable architecture and OS-specific static manner, but WASM is a portable format. Since C ABI is the lingua franca of the programming world and there is no other stable Rust ABI (yet), Iroha relies on the C-linkage to generate WASM bindings. Thankfully, iroha_wasm takes care of everything related to foreign function interfaces (FFI), so you don't have to worry about things like unsafe, repr(C), padding, alignment, and others.

The iroha_wasm crate contains all of the bindings, macros, and trait implementations that you'd need to write the program, most notably the iroha_wasm attribute macro. The crate also exposes our data_model, which contains all of the basic ISI and types. The chosen serialisation format is parity-scale-codec, though there is a strong possibility it'll get replaced with a different (custom) serialisation format in the near future, as it seems to dominate the binary size[1].

2. Write a smart contract

Now that we have the preliminaries nailed down, we get to write some code for our smart contract. In the src/lib.rs you should write the following:

rust
#![no_std]
+#![no_main]
+
+extern crate alloc;
+
+use alloc::vec::Vec;
+
+use iroha_wasm::data_model::prelude::*;
+
+#[iroha_wasm::iroha_wasm]
+fn smartcontract_entry_point(_account_id: AccountId) {
+    let query = QueryBox::FindAllDomains(FindAllDomains {});
+    let domains: Vec<Domain> = query.execute().try_into().unwrap();
+
+    for domain in domains {
+        let new_account_id = AccountId {
+            name: Name::new("mad_hatter").unwrap(),
+            domain_id: domain.id,
+        };
+
+        Instruction::Register(RegisterBox::new(NewAccount::new(new_account_id))).execute();
+    }
+}
#![no_std]
+#![no_main]
+
+extern crate alloc;
+
+use alloc::vec::Vec;
+
+use iroha_wasm::data_model::prelude::*;
+
+#[iroha_wasm::iroha_wasm]
+fn smartcontract_entry_point(_account_id: AccountId) {
+    let query = QueryBox::FindAllDomains(FindAllDomains {});
+    let domains: Vec<Domain> = query.execute().try_into().unwrap();
+
+    for domain in domains {
+        let new_account_id = AccountId {
+            name: Name::new("mad_hatter").unwrap(),
+            domain_id: domain.id,
+        };
+
+        Instruction::Register(RegisterBox::new(NewAccount::new(new_account_id))).execute();
+    }
+}

To submit the instruction and run it, execute the following command (be sure to have a peer up):

cargo run --release
cargo run --release

What does this smart contract do? Let's see. It queries all of the currently existing domains and puts the results into a std::vec::Vec. In this case, std::vec::Vec has to be imported from alloc, as we use no_std (more on that later). It is then used to add the user named mad_hatter to all of the existing domains.

Building the same logic out of Expression and If and Sequence would be significantly harder. Moreover, the actual low-level instructions that would run are very likely not going to be as well-optimised as what the compiler produces.

Advanced Smart Contracts: Optimising for Size

WASM smart contracts can get big. So big, in fact, that we might not let you store them in the blockchain. So how do you reduce the size? The most important modifications are done in Cargo.toml:

toml
[profile.release]
+strip = "debuginfo" # Remove debugging info from the binary
+panic = "abort"     # Abort panics as they are transcribed to Traps when compiling for WASM anyways
+lto = true          # Use link-time-optimisation (it produces a notable decrease in binary size)
+opt-level = "z"     # Optimise for size vs speed with "s"/"z"(removes vectorization)
+codegen-units = 1   # Use one code generation unit (it further reduces the binary size but increases compilation time)
[profile.release]
+strip = "debuginfo" # Remove debugging info from the binary
+panic = "abort"     # Abort panics as they are transcribed to Traps when compiling for WASM anyways
+lto = true          # Use link-time-optimisation (it produces a notable decrease in binary size)
+opt-level = "z"     # Optimise for size vs speed with "s"/"z"(removes vectorization)
+codegen-units = 1   # Use one code generation unit (it further reduces the binary size but increases compilation time)

Let's take a closer look at what you can do to reduce the size of the WASM binary.

Remove debugging info

Rust stores a lot of debug information (even when compiled in release mode), which is (as the name suggests) used for debugging a panic in your Rust application. As you would expect, this information increases the size of the compiled WASM significantly.

Normally, this would be a worthwhile trade-off, but not in our case. Firstly, since the WASM is stored on-chain, it will be permanently recorded in some block and take space on every full node of an Iroha network. Iroha stores a lot of its information in RAM, so storage space for WASM is at a premium.

Secondly, once the WASM smart contract is stored on-chain, the debug information is no longer accessible. Indeed, you shouldn't debug on a peer. Instead, you should replicate the conditions that caused the panic locally and debug on your personal machine.

Work under a no_std environment

Another step that we've already taken involves working under a no_std environment. All of our size-related woes stem from Rust being predominantly statically linked. As such, breaking the standard library into more manageable crates, like using alloc::vec instead of std::vec, can help us reduce the size and compilation time[2].

Re-compile libcore

Next, you're advised to re-compile libcore and any other standard library crate (e.g. alloc) to exclude the leftover panic-related code that comes with the prebuilt core library[3]:

bash
$ cargo +nightly build -Z build-std -Z build-std-features=panic_immediate_abort --target wasm32-unknown-unknown
$ cargo +nightly build -Z build-std -Z build-std-features=panic_immediate_abort --target wasm32-unknown-unknown

Unfortunately, this is an unstable feature. In other words, the developers of the Rust programming language can decide to change how this works, or remove this option entirely.

Use tools to optimise WASM size

Finally, you can use an automated tool to optimise the size of the WASM binary. You could use wasm-opt or twiggy to guide your manual optimisation efforts.

We highly advise using wasm-opt because it will often significantly reduce your binary size:

bash
$ wasm-opt -Os -o output.wasm input.wasm
$ wasm-opt -Os -o output.wasm input.wasm

Conclusion

At some point, unfortunately, the smallest size of your WASM blob is going to be determined by the libraries that you need to use. Using all of the above steps on the provided smart contract can reduce it down to a manageable (for the blockchain) size.


  1. Size is an important metric. We shall cover size-optimisation strategies as we go. ↩︎

  2. It should be noted that excluding the standard library is necessary for compiling to the wasm32 target, and is thus mandatory. ↩︎

  3. wasm-opt can also be used to remove the debug sections. ↩︎

`,49),t=[l];function p(r,c,i,y,d,E){return a(),n("div",null,t)}const m=s(e,[["render",p]]);export{u as __pageData,m as default}; diff --git a/assets/guide_blockchain_wasm.md.e977d18a.lean.js b/assets/guide_blockchain_wasm.md.e977d18a.lean.js new file mode 100644 index 000000000..d33cbc922 --- /dev/null +++ b/assets/guide_blockchain_wasm.md.e977d18a.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"WASM","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/wasm.md","filePath":"guide/blockchain/wasm.md","lastUpdated":1700563625000}'),e={name:"guide/blockchain/wasm.md"},l=o("",49),t=[l];function p(r,c,i,y,d,E){return a(),n("div",null,t)}const m=s(e,[["render",p]]);export{u as __pageData,m as default}; diff --git a/assets/guide_blockchain_world.md.e5785fd9.js b/assets/guide_blockchain_world.md.e5785fd9.js new file mode 100644 index 000000000..ea821cc5b --- /dev/null +++ b/assets/guide_blockchain_world.md.e5785fd9.js @@ -0,0 +1 @@ +import{_ as e,o as i,c as r,Q as o}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"World","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/world.md","filePath":"guide/blockchain/world.md","lastUpdated":1700563625000}'),t={name:"guide/blockchain/world.md"},a=o('

World

World is the global entity that contains other entities. The World consists of:

When domains, peers, or roles are registered or unregistered, the World is the target of the (un)register instruction.

World State View (WSV)

World State View is the in-memory representation of the current blockchain state. This includes all currently loaded blocks, with all of their contents, as well as peers elected for the current epoch.

',6),s=[a];function l(n,d,c,h,u,p){return i(),r("div",null,s)}const g=e(t,[["render",l]]);export{f as __pageData,g as default}; diff --git a/assets/guide_blockchain_world.md.e5785fd9.lean.js b/assets/guide_blockchain_world.md.e5785fd9.lean.js new file mode 100644 index 000000000..613325cb9 --- /dev/null +++ b/assets/guide_blockchain_world.md.e5785fd9.lean.js @@ -0,0 +1 @@ +import{_ as e,o as i,c as r,Q as o}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"World","description":"","frontmatter":{},"headers":[],"relativePath":"guide/blockchain/world.md","filePath":"guide/blockchain/world.md","lastUpdated":1700563625000}'),t={name:"guide/blockchain/world.md"},a=o("",6),s=[a];function l(n,d,c,h,u,p){return i(),r("div",null,s)}const g=e(t,[["render",l]]);export{f as __pageData,g as default}; diff --git a/assets/guide_configure_client-configuration.md.913412a8.js b/assets/guide_configure_client-configuration.md.913412a8.js new file mode 100644 index 000000000..aa719541a --- /dev/null +++ b/assets/guide_configure_client-configuration.md.913412a8.js @@ -0,0 +1,55 @@ +import{_ as s,o as a,c as o,Q as n}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Client Configuration","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/client-configuration.md","filePath":"guide/configure/client-configuration.md","lastUpdated":1700563625000}'),e={name:"guide/configure/client-configuration.md"},l=n(`

Client Configuration

Let's look at the client configuration options.

Client configuration example
json
{
+  "PUBLIC_KEY": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
+  "PRIVATE_KEY": {
+    "digest_function": "ed25519",
+    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
+  },
+  "ACCOUNT_ID": "alice@wonderland",
+  "BASIC_AUTH": {
+    "web_login": "mad_hatter",
+    "password": "ilovetea"
+  },
+  "TORII_API_URL": "http://127.0.0.1:8080/",
+  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180/",
+  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
+  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
+  "TRANSACTION_LIMITS": {
+    "max_instruction_number": 4096,
+    "max_wasm_size_bytes": 4194304
+  },
+  "ADD_TRANSACTION_NONCE": false
+}
{
+  "PUBLIC_KEY": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
+  "PRIVATE_KEY": {
+    "digest_function": "ed25519",
+    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
+  },
+  "ACCOUNT_ID": "alice@wonderland",
+  "BASIC_AUTH": {
+    "web_login": "mad_hatter",
+    "password": "ilovetea"
+  },
+  "TORII_API_URL": "http://127.0.0.1:8080/",
+  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180/",
+  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
+  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
+  "TRANSACTION_LIMITS": {
+    "max_instruction_number": 4096,
+    "max_wasm_size_bytes": 4194304
+  },
+  "ADD_TRANSACTION_NONCE": false
+}

Generation

You can use kagami to generate the default client configuration:

bash
$ kagami config client > client-config.json
$ kagami config client > client-config.json

Public and Private Keys

The configs/client_cli/config.json client configuration file should contain a pair of the user's public PUBLIC_KEY and private PRIVATE_KEY cryptographic keys for their account's ACCOUNT_ID.

For details on cryptographic keys, see Public Key Cryptography.

User account

The ACCOUNT_ID should be self-explanatory. The only thing you need to worry about is that the account must already exist in the blockchain. In other words, the account you provide here should already be registered.

Note

Iroha is case-sensitive, meaning that Alice@wonderland is different from alice@wonderland. It should go without saying that alice@wonderland is not the same as alice@looking_glass either, since these accounts belong to different domains, wonderland and looking_glass.

Basic Authentication Credentials

The idea of basic authentication credentials is to provide the access control using a web server with a reverse proxy like Nginx while these credentials are ignored by the Iroha peers.

The login and password will be filled by the client and used for the Authorization HTTP header.

Use this style of configuration to provide the basic authentication credentials (login and password):

json
"BASIC_AUTH": {
+    "web_login": "mad_hatter",
+    "password": "ilovetea"
+  },
"BASIC_AUTH": {
+    "web_login": "mad_hatter",
+    "password": "ilovetea"
+  },

Iroha Public Addresses

TORII is the module in charge of handling incoming and outgoing connections. For client configuration, you can set up two addresses: TORII_API_URL and TORII_TELEMETRY_URL.

TORII_API_URL

First, the TORII_API_URL is the same as TORII API_URL in the peer configuration. This is the module responsible for handling incoming and outgoing connections. You should also add the prefix http:// or (preferably) https:// to the address. For example:

json
"TORII_API_URL": "http://127.0.0.1:8080"
"TORII_API_URL": "http://127.0.0.1:8080"

TORII_TELEMETRY_URL

The TORII_TELEMETRY_URL is used to specify the prometheus endpoint address. You can set TORII_TELEMETRY_URL like this:

json
"TORII_TELEMETRY_URL": "http://127.0.0.1:8180"
"TORII_TELEMETRY_URL": "http://127.0.0.1:8180"

A GET request to the 127.0.0.1:8180/status will give you a JSON-encoded representation of the top-level metrics, while a GET request to 127.0.0.1:8180/metrics will give you a (somewhat verbose) list of all available metrics gathered in Iroha. You might want to change this if you're having trouble gathering metrics using prometheus.

INFO

Learn how to monitor Iroha performance using prometheus.

Transaction Limits

You can specify the transaction limits that each transaction must adhere to: the maximum number of instructions and the maximum size of a WASM blob (in bytes). For example:

json
{
+  "max_instruction_number": 4096,
+  "max_wasm_size_bytes": 4194304
+}
{
+  "max_instruction_number": 4096,
+  "max_wasm_size_bytes": 4194304
+}

Transaction TTL and Timeout

Configure the time-to-live (TTL) for transactions and the timeout to wait for the status. Both values have to be provided in milliseconds. For example:

json
"TRANSACTION_TIME_TO_LIVE_MS": 100000,
+"TRANSACTION_STATUS_TIMEOUT_MS": 15000,
"TRANSACTION_TIME_TO_LIVE_MS": 100000,
+"TRANSACTION_STATUS_TIMEOUT_MS": 15000,

Transaction Nonce

If you set ADD_TRANSACTION_NONCE to true, Iroha will create different hashes for transactions that occur repeatedly and simultaneously.

`,35),t=[l];function p(c,r,i,E,u,d){return a(),o("div",null,t)}const q=s(e,[["render",p]]);export{h as __pageData,q as default}; diff --git a/assets/guide_configure_client-configuration.md.913412a8.lean.js b/assets/guide_configure_client-configuration.md.913412a8.lean.js new file mode 100644 index 000000000..ab2e1a2d8 --- /dev/null +++ b/assets/guide_configure_client-configuration.md.913412a8.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as o,Q as n}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Client Configuration","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/client-configuration.md","filePath":"guide/configure/client-configuration.md","lastUpdated":1700563625000}'),e={name:"guide/configure/client-configuration.md"},l=n("",35),t=[l];function p(c,r,i,E,u,d){return a(),o("div",null,t)}const q=s(e,[["render",p]]);export{h as __pageData,q as default}; diff --git a/assets/guide_configure_configuration-types.md.f5f5b88c.js b/assets/guide_configure_configuration-types.md.f5f5b88c.js new file mode 100644 index 000000000..9721eee12 --- /dev/null +++ b/assets/guide_configure_configuration-types.md.f5f5b88c.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as o,Q as i}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Configuration Types","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/configuration-types.md","filePath":"guide/configure/configuration-types.md","lastUpdated":1700563625000}'),a={name:"guide/configure/configuration-types.md"},n=i('

Configuration Types

Configuration options have different underlying types and default values, which are denoted in code as types wrapped in a single Option<..> or in a double Option<Option<..>>.

In this section we explain the difference between Option<..> and Option<Option<..>> used for configuration types. You can find more about available configuration options in peer configuration. The full list of available options is in Iroha Configuration Reference.

Option<..>

A type wrapped in a single Option<..> signifies that in the corresponding json block there is a fallback value for this type, and that it only serves as a reference.

If a default for such a type has a null value, it means that

there is no meaningful fallback available for this particular value. It doesn't mean that you can omit the value. Quite the opposite, it must be set manually, either in the configuration file, or via the environment variables.

All the default values can be freely obtained from a provided sample configuration file, but it is only a starting point. **If left unchanged, the sample configuration file will not work. All null values in place of public and private keys as well as endpoint URLs should be provided either by modifying the sample config file or as environment variables. No other overloading of configuration values happens besides reading them from a file and capturing the environment variables, and environment variables take precedence.

For both types of configuration options wrapped in a single Option<..> (i.e. both those that have meaningful defaults and those that have null), failure to provide them in any of the above two ways results in an error.

Option<Option<..>>

Option<Option<..>> types should be distinguished from types wrapped in a single Option<..>. Only the double option ones are allowed to stay null, meaning that not providing them in an environment variable or a file will not cause an error.

Thus, only these types are truly optional in the common sense of the word. An example of this distinction is genesis public and private keys. While the first one is a single Option<..> wrapped type, the latter is wrapped in Option<Option<..>>. This means that the genesis public key should always be provided by the user, be it via a file config or an environment variable, whereas the private key is only needed for the peer that submits the genesis block, and can be omitted for all others. The same logic goes for other double option fields such as logger file path.

Sumeragi: default null values

A special note about Sumeragi fields with null as default: only the trusted_peers field out of the three can be initialized via a provided file or an environment variable.

The other two fields, namely key_pair and peer_id, go through a process of finalization where their values are derived from the corresponding ones in the uppermost Iroha config (using its public_key and private_key fields) or the Torii config (via its p2p_addr). This ensures that these linked fields stay in sync, and prevents the programmer error when different values are provided to these field pairs. Providing either sumeragi.key_pair or sumeragi.peer_id by hand will result in an error, as it should never be done directly. In later versions these configuration options shall be hidden completely.

',14),r=[n];function l(s,d,p,c,h,u){return t(),o("div",null,r)}const m=e(a,[["render",l]]);export{g as __pageData,m as default}; diff --git a/assets/guide_configure_configuration-types.md.f5f5b88c.lean.js b/assets/guide_configure_configuration-types.md.f5f5b88c.lean.js new file mode 100644 index 000000000..05b1b47af --- /dev/null +++ b/assets/guide_configure_configuration-types.md.f5f5b88c.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as o,Q as i}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Configuration Types","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/configuration-types.md","filePath":"guide/configure/configuration-types.md","lastUpdated":1700563625000}'),a={name:"guide/configure/configuration-types.md"},n=i("",14),r=[n];function l(s,d,p,c,h,u){return t(),o("div",null,r)}const m=e(a,[["render",l]]);export{g as __pageData,m as default}; diff --git a/assets/guide_configure_genesis.md.584065bb.js b/assets/guide_configure_genesis.md.584065bb.js new file mode 100644 index 000000000..645afeea4 --- /dev/null +++ b/assets/guide_configure_genesis.md.584065bb.js @@ -0,0 +1,401 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.1293becd.js";const F=JSON.parse('{"title":"Genesis Block","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/genesis.md","filePath":"guide/configure/genesis.md","lastUpdated":1700563625000}'),o={name:"guide/configure/genesis.md"},p=l(`

Genesis Block

The genesis block is the first block in your blockchain. It's never empty, even if configs/peer/genesis.json is. Here's an example:

Genesis Block Example: alice@wonderland
json
{
+  "transactions": [
+    [
+      {
+        "Register": {
+          "NewDomain": {
+            "id": "wonderland",
+            "logo": null,
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "alice@wonderland",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "bob@wonderland",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAssetDefinition": {
+            "id": "rose#wonderland",
+            "value_type": "Quantity",
+            "mintable": "Infinitely",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewDomain": {
+            "id": "garden_of_live_flowers",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "carpenter@garden_of_live_flowers",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAssetDefinition": {
+            "id": "cabbage#garden_of_live_flowers",
+            "value_type": "Quantity",
+            "mintable": "Infinitely",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Mint": {
+          "object": "13_u32",
+          "destination_id": {
+            "AssetId": "rose##alice@wonderland"
+          }
+        }
+      },
+      {
+        "Mint": {
+          "object": "44_u32",
+          "destination_id": {
+            "AssetId": "cabbage#garden_of_live_flowers#alice@wonderland"
+          }
+        }
+      },
+      {
+        "Grant": {
+          "object": {
+            "PermissionToken": {
+              "definition_id": "CanSetParameters",
+              "payload": null
+            }
+          },
+          "destination_id": {
+            "AccountId": "alice@wonderland"
+          }
+        }
+      },
+      {
+        "Sequence": [
+          {
+            "NewParameter": {
+              "Parameter": "?MaxTransactionsInBlock=512"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?BlockTime=2000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?CommitTimeLimit=4000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?TransactionLimits=4096,4194304_TL"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAssetMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAccountMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVDomainMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVIdentLengthLimits=1,128_LL"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WASMFuelLimit=23000000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WASMMaxMemory=524288000"
+            }
+          }
+        ]
+      },
+      {
+        "Register": {
+          "NewRole": {
+            "id": "ALICE_METADATA_ACCESS",
+            "permissions": [
+              {
+                "definition_id": "CanRemoveKeyValueInUserAccount",
+                "payload": {
+                  "account_id": "alice@wonderland"
+                }
+              },
+              {
+                "definition_id": "CanSetKeyValueInUserAccount",
+                "payload": {
+                  "account_id": "alice@wonderland"
+                }
+              }
+            ]
+          }
+        }
+      }
+    ]
+  ],
+  "validator": "./validator.wasm"
+}
{
+  "transactions": [
+    [
+      {
+        "Register": {
+          "NewDomain": {
+            "id": "wonderland",
+            "logo": null,
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "alice@wonderland",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "bob@wonderland",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAssetDefinition": {
+            "id": "rose#wonderland",
+            "value_type": "Quantity",
+            "mintable": "Infinitely",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewDomain": {
+            "id": "garden_of_live_flowers",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "carpenter@garden_of_live_flowers",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAssetDefinition": {
+            "id": "cabbage#garden_of_live_flowers",
+            "value_type": "Quantity",
+            "mintable": "Infinitely",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Mint": {
+          "object": "13_u32",
+          "destination_id": {
+            "AssetId": "rose##alice@wonderland"
+          }
+        }
+      },
+      {
+        "Mint": {
+          "object": "44_u32",
+          "destination_id": {
+            "AssetId": "cabbage#garden_of_live_flowers#alice@wonderland"
+          }
+        }
+      },
+      {
+        "Grant": {
+          "object": {
+            "PermissionToken": {
+              "definition_id": "CanSetParameters",
+              "payload": null
+            }
+          },
+          "destination_id": {
+            "AccountId": "alice@wonderland"
+          }
+        }
+      },
+      {
+        "Sequence": [
+          {
+            "NewParameter": {
+              "Parameter": "?MaxTransactionsInBlock=512"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?BlockTime=2000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?CommitTimeLimit=4000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?TransactionLimits=4096,4194304_TL"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAssetMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAccountMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVDomainMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVIdentLengthLimits=1,128_LL"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WASMFuelLimit=23000000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WASMMaxMemory=524288000"
+            }
+          }
+        ]
+      },
+      {
+        "Register": {
+          "NewRole": {
+            "id": "ALICE_METADATA_ACCESS",
+            "permissions": [
+              {
+                "definition_id": "CanRemoveKeyValueInUserAccount",
+                "payload": {
+                  "account_id": "alice@wonderland"
+                }
+              },
+              {
+                "definition_id": "CanSetKeyValueInUserAccount",
+                "payload": {
+                  "account_id": "alice@wonderland"
+                }
+              }
+            ]
+          }
+        }
+      }
+    ]
+  ],
+  "validator": "./validator.wasm"
+}

The genesis account is specified in the peer configuration file, configs/peer/config.json. This is the account that will submit the genesis block. The genesis account is like a super user account that has elevated privileges, but only during the genesis round. The genesis account should be signed by one of the peers, or, in other words, it should have the public key of this peer.

If you look at the example of a genesis block above, you will see that it contains instructions for registering a new domain (wonderland), two new accounts (alice@wonderland and bob@wonderland), a new asset (rose#wonderland) and a Mint instruction for this asset, as well as several permission tokens and roles. Both new accounts are signed with the ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0 public key.

Note

Iroha is case-sensitive, meaning that Alice@wonderland is different from alice@wonderland. It should go without saying that alice@wonderland is not the same as alice@looking_glass either, since these accounts belong to different domains, wonderland and looking_glass.

The accounts registered in the genesis block are just new accounts. As we said above, the genesis account is determined in the peer configuration. However, you can use the matching signature for the genesis account and for a new account in the genesis block. Since the genesis account only has privileges during the genesis round, it won't be a security issue.

You can generate the default genesis block or create a custom one.

If you need to recommit a genesis block, remove the previously stored blocks, then restart the Docker container. The new genesis block will be automatically recommited upon container restart.

Generation

You can add various instructions to the genesis block, such as registering new accounts or assets, as well as minting assets. You can also register permission tokens and roles, as well as grant them to the registered accounts.

Generate default genesis block

You can use kagami to generate the default genesis block:

  • Generate a genesis block in JSON format:

    bash
    $ kagami genesis
    $ kagami genesis
  • Generate a genesis block in JSON format and write the output to the specified file:

    bash
    $ kagami genesis > genesis.json
    $ kagami genesis > genesis.json
  • Generate a synthetic genesis block in JSON format and write the n domains, m accounts per domain and p assets per domain:

    bash
    $ kagami genesis --synthetic --domains n --accounts-per-domain m --assets-per-domain p
    $ kagami genesis --synthetic --domains n --accounts-per-domain m --assets-per-domain p

The genesis block should be located in configs/peer/genesis.json.

Configuration

As we already explained, genesis account is specified in the peer configuration file, configs/peer/config.json. You can use the same configuration file to fine-tune other genesis block configurations.

`,17),e=[p];function t(c,E,r,y,i,u){return n(),a("div",null,e)}const C=s(o,[["render",t]]);export{F as __pageData,C as default}; diff --git a/assets/guide_configure_genesis.md.584065bb.lean.js b/assets/guide_configure_genesis.md.584065bb.lean.js new file mode 100644 index 000000000..f4a480a2c --- /dev/null +++ b/assets/guide_configure_genesis.md.584065bb.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.1293becd.js";const F=JSON.parse('{"title":"Genesis Block","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/genesis.md","filePath":"guide/configure/genesis.md","lastUpdated":1700563625000}'),o={name:"guide/configure/genesis.md"},p=l("",17),e=[p];function t(c,E,r,y,i,u){return n(),a("div",null,e)}const C=s(o,[["render",t]]);export{F as __pageData,C as default}; diff --git a/assets/guide_configure_keys-for-network-deployment.md.02deb3dc.js b/assets/guide_configure_keys-for-network-deployment.md.02deb3dc.js new file mode 100644 index 000000000..1effce33c --- /dev/null +++ b/assets/guide_configure_keys-for-network-deployment.md.02deb3dc.js @@ -0,0 +1 @@ +import{_ as e,o,c as a,Q as t}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Keys for Network Deployment","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/keys-for-network-deployment.md","filePath":"guide/configure/keys-for-network-deployment.md","lastUpdated":1700563625000}'),r={name:"guide/configure/keys-for-network-deployment.md"},s=t('

Keys for Network Deployment

If you're deploying your own Iroha 2 network, your unique cryptographic keys must be specified in all three of the configuration files:

  1. Peer configuration file: configs/peer/config.json
  2. Client configuration file: configs/client_cli/config.json
  3. Genesis block file: configs/peer/genesis.json

To learn more about cryptographic keys and their role, see Security > Public Key Cryptography.

Setting Keys For a New Network

1. Generate New Key Pairs

To generate new key pairs for the peers, a wide variety of methods can be used. However, within the Iroha 2 framework, you can conveniently use the built-in kagami tool for generating cryptographic keys.

To generate a new key pair run the following command from the project's root directory:

bash
$ cargo run --bin kagami --release -- crypto --json
$ cargo run --bin kagami --release -- crypto --json

Note

The output cryptographic keys generated by kagami are customizable by using preferences. Note that in the example above the --json parameter is specified to generate a key pair in the JSON format.

To learn more about generating cryptographic keys with kagami, available algorithms, and other parameters, see Generating Cryptographic Keys with Kagami.

If you plan to use the generated private_key with one of our SDKs, note that even though cryptographic keys are commonly encoded using ASCII characters, both the payload value of the private_key and the string representation of the public_key are encoded as Hex.

2. Update Keys For Peers

If you want to set up your own network, you should change the keys for all your peers: in peer/config.json change PUBLIC_KEY and PRIVATE_KEY to the fresh pair. When you've done that, you should add the keys to the TRUSTED_PEERS array in the same configuration file. Every peer that wants to connect to the given peer from the outside must know its PRIVATE_KEY specified in the TRUSTED_PEERS section.

To create a minimum BFT network one needs four peers, which means four different private keys split across four different configuration files (or environment variables).

Each peer must have their own PUBLIC_KEY and PRIVATE_KEY variables specified. All four of the public keys—including the peer that is being configured—must be added to the TRUSTED_PEERS array. The same TRUSTED_PEERS array must be copied across all four of the configuration files. If either one of the peers is missing, or there's an extraneous peer or one of the peers has the incorrect key, the network will fail to start.

After that, make sure that the peers agree on the GENESIS_ACCOUNT key pairs. Failure to do so will result in a network which cannot accept any transactions.

Note

Even though the private key for the genesis account is known to all peers, the account itself loses all privileges after the first block is committed.

3. Register a Non-Genesis Account

Finally, while the first client could use the genesis account to register new users, it's not a great idea for most networks. You should, instead, register a non-genesis account (for example, alice@wonderland).

WARNING

iroha_client_cli currently processes all of its instructions in the JSON format, it also provides a dedicated instruction to unregister accounts.

If you plan on creating a private blockchain, you should consider writing your own client based on the client Rust crate, or any of the provided client libraries:

',20),n=[s];function i(c,l,p,h,d,y){return o(),a("div",null,n)}const g=e(r,[["render",i]]);export{f as __pageData,g as default}; diff --git a/assets/guide_configure_keys-for-network-deployment.md.02deb3dc.lean.js b/assets/guide_configure_keys-for-network-deployment.md.02deb3dc.lean.js new file mode 100644 index 000000000..db4c6e147 --- /dev/null +++ b/assets/guide_configure_keys-for-network-deployment.md.02deb3dc.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as a,Q as t}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Keys for Network Deployment","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/keys-for-network-deployment.md","filePath":"guide/configure/keys-for-network-deployment.md","lastUpdated":1700563625000}'),r={name:"guide/configure/keys-for-network-deployment.md"},s=t("",20),n=[s];function i(c,l,p,h,d,y){return o(),a("div",null,n)}const g=e(r,[["render",i]]);export{f as __pageData,g as default}; diff --git a/assets/guide_configure_metadata-and-store-assets.md.06d6b458.js b/assets/guide_configure_metadata-and-store-assets.md.06d6b458.js new file mode 100644 index 000000000..fbff4f86c --- /dev/null +++ b/assets/guide_configure_metadata-and-store-assets.md.06d6b458.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as a,Q as s}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Choosing Between the Store and Metadata Assets","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/metadata-and-store-assets.md","filePath":"guide/configure/metadata-and-store-assets.md","lastUpdated":1700563625000}'),o={name:"guide/configure/metadata-and-store-assets.md"},n=s('

Choosing Between the Store and Metadata Assets

The Store and metadata assets allow for the storage of several parameters with different types and string keys. Despite the similarity, their use cases differ.

In most cases, you should only use metadata if you want something to use specific quantities and not be editable by other users. There are a few other practical considerations we'll discuss below.

You must use a Store asset if the data exists in the blockchain and a default number type is not applicable. It lets you record data sequences and works as a dictionary, where the keys are strings, while the values are Value type instances that assume many types from String to Ipv6Addr.

Let’s discuss some use cases to make it more clear.

Let’s say, for example, you have a clinic. Here, generic user data, such as birthdays, can be added to metadata. The Store asset instances record the appointments, treatments suggested by the doctors, and medical test results.

Suppose you’re using Iroha for a large-scale IoT network to monitor the machines in your factory. In this case, you want to use the Store asset instances to record each manufacturing process on each manufacturing device so you can analyze the factors involved in the manufacturing process to understand the “health” of your machines and reduce the possibility of downtime by making repairs when needed.

Finally, imagine a network of organizations that trade specific goods or resources. Here, the Store assets would record every trade agreement, including the trade cost and a signature. This approach applies to many cases, starting with someone’s services and ending with NFTs.

There are also some pitfalls in choosing between metadata and Store assets.

Let's look at what happens when we define something in the metadata at the system level, such as the number of queries users can perform at a given time. Theoretically, this is correct since metadata is a key-value store. However, the default settings allow users to edit their metadata. In the development phase, this isn't a significant problem. However, once deployed, users could change the number of queries they can perform in this configuration, rendering any imposed restrictions ineffective.

With a new account schema, the user's metadata becomes global information: you cannot restrict access to it and say that this is related to a domain. On the other hand, the storage asset can belong to a single user. Some optimizations prevent domain-specific triggers from acting only on domain-specific data. Using metadata would cause these optimizations not to work. Instead, each user within a domain can get a copy of the metadata. This approach works like metadata, except that you'd give them memory access but not necessarily allow them to create new key-value pairs. They can see how many queries they have left, but they can't easily change that number with a simple statement.

Instead of thinking about data ownership, think about the location of the data and the function that data serves and the flow of information. So instead of storing in the user's metadata the number of queries they're allowed to make, the number of tokens they have created, or their NFTs, you should put each category in its own Store asset.

Think about what the smartcontract is supposed to do and how much extra data you must load and ignore. If your service participates using the metadata and another one does, and so on, the metadata size becomes enormous. Whenever you have queries related to it, you'll copy a lot of excess information and slow your code down. There's another catch: when the metadata belongs to an account, it essentially depends on another entity. When the said entity is removed or replaced, it requires glue code to handle the metadata transfer.

',13),i=[n];function r(d,c,h,u,m,l){return t(),a("div",null,i)}const y=e(o,[["render",r]]);export{f as __pageData,y as default}; diff --git a/assets/guide_configure_metadata-and-store-assets.md.06d6b458.lean.js b/assets/guide_configure_metadata-and-store-assets.md.06d6b458.lean.js new file mode 100644 index 000000000..2d7d6e42a --- /dev/null +++ b/assets/guide_configure_metadata-and-store-assets.md.06d6b458.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as a,Q as s}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Choosing Between the Store and Metadata Assets","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/metadata-and-store-assets.md","filePath":"guide/configure/metadata-and-store-assets.md","lastUpdated":1700563625000}'),o={name:"guide/configure/metadata-and-store-assets.md"},n=s("",13),i=[n];function r(d,c,h,u,m,l){return t(),a("div",null,i)}const y=e(o,[["render",r]]);export{f as __pageData,y as default}; diff --git a/assets/guide_configure_modes.md.854101fa.js b/assets/guide_configure_modes.md.854101fa.js new file mode 100644 index 000000000..3313d0c9f --- /dev/null +++ b/assets/guide_configure_modes.md.854101fa.js @@ -0,0 +1 @@ +import{_ as e,o,c as t,Q as i}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Public and Private Blockchains","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/modes.md","filePath":"guide/configure/modes.md","lastUpdated":1700563625000}'),a={name:"guide/configure/modes.md"},s=i('

Public and Private Blockchains

Iroha can be ran in a variety of configurations. As the administrator of your own network, you can use different permission sets to decide what criteria must be met in order for some transaction to be accepted.

We provide two major sets of permissions: called a private and public permission sets. These need to be added into the genesis.json before you start an Iroha peer.

Below we outline the major differences in these two use cases.

Permissions

In a public blockchain, most accounts have the same set of permissions. In a private blockchain, most accounts are assumed not to be able to do anything outside of their own account or domain unless explicitly granted said permission.

INFO

Refer to the dedicated section on permissions for more details.

Peers

Any peer can join a public blockchain. For a private blockchain, automatic discovery of peers is turned off.

INFO

Refer to peer management for more details.

Registering accounts

Depending on how you decide to set up your genesis block (genesis.json), the process for registering an account might go one of two ways. To understand why, let's talk about permission first.

By default, Iroha allows all instructions to go through, until a permission validator that can restrict instruction execution has been registered. You can add permission validators to your genesis block by registering built-in permission tokens that we thought would be useful for private and public blockchain use-cases. However, in that case, the process of registering accounts is different.

When it comes to registering accounts, public and private blockchain have the following differences:

  • In a public blockchain, anyone should be able to register an account[1]. So, in theory, all that you need is a suitable client, a way to generate a private key of a suitable type (ED25519), and that's it.

  • In a private blockchain, you can have any process for setting up an account: it could be that the registering instruction has to be submitted by a specific account, or by a smart contract that asks for other details. It could be that in a private blockchain registering new accounts is only possible on specific dates, or limited by a non-mintable (finite) token.

  • In a typical private blockchain, i.e. a blockchain without any unique processes for registering accounts, you need an account to register another account.

Built-in permission validators for private blockchains cover the `typical private blockchain use-case.

INFO

As of writing, the set of public blockchain permissions is incomplete, and as such Iroha source code needs to be modified to run it in the public mode.

Refer to the section on instructions for more details about Register<Account> instructions.


  1. In fact, once we have finished with our key-centric address scheme for accounts, you don't register an account as much as claim it. ↩︎

',20),n=[s];function c(r,l,d,u,h,p){return o(),t("div",null,n)}const b=e(a,[["render",c]]);export{f as __pageData,b as default}; diff --git a/assets/guide_configure_modes.md.854101fa.lean.js b/assets/guide_configure_modes.md.854101fa.lean.js new file mode 100644 index 000000000..4d9aa0a67 --- /dev/null +++ b/assets/guide_configure_modes.md.854101fa.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as t,Q as i}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Public and Private Blockchains","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/modes.md","filePath":"guide/configure/modes.md","lastUpdated":1700563625000}'),a={name:"guide/configure/modes.md"},s=i("",20),n=[s];function c(r,l,d,u,h,p){return o(),t("div",null,n)}const b=e(a,[["render",c]]);export{f as __pageData,b as default}; diff --git a/assets/guide_configure_overview.md.abcd2531.js b/assets/guide_configure_overview.md.abcd2531.js new file mode 100644 index 000000000..f8f277e7e --- /dev/null +++ b/assets/guide_configure_overview.md.abcd2531.js @@ -0,0 +1 @@ +import{_ as a,o as n,c as t,k as e,a as o}from"./chunks/framework.1293becd.js";const p=JSON.parse('{"title":"Configuration and Management","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/overview.md","filePath":"guide/configure/overview.md","lastUpdated":1700563625000}'),r={name:"guide/configure/overview.md"},i=e("h1",{id:"configuration-and-management",tabindex:"-1"},[o("Configuration and Management "),e("a",{class:"header-anchor",href:"#configuration-and-management","aria-label":'Permalink to "Configuration and Management"'},"​")],-1),d=[i];function c(s,f,g,m,u,_){return n(),t("div",null,d)}const h=a(r,[["render",c]]);export{p as __pageData,h as default}; diff --git a/assets/guide_configure_overview.md.abcd2531.lean.js b/assets/guide_configure_overview.md.abcd2531.lean.js new file mode 100644 index 000000000..f8f277e7e --- /dev/null +++ b/assets/guide_configure_overview.md.abcd2531.lean.js @@ -0,0 +1 @@ +import{_ as a,o as n,c as t,k as e,a as o}from"./chunks/framework.1293becd.js";const p=JSON.parse('{"title":"Configuration and Management","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/overview.md","filePath":"guide/configure/overview.md","lastUpdated":1700563625000}'),r={name:"guide/configure/overview.md"},i=e("h1",{id:"configuration-and-management",tabindex:"-1"},[o("Configuration and Management "),e("a",{class:"header-anchor",href:"#configuration-and-management","aria-label":'Permalink to "Configuration and Management"'},"​")],-1),d=[i];function c(s,f,g,m,u,_){return n(),t("div",null,d)}const h=a(r,[["render",c]]);export{p as __pageData,h as default}; diff --git a/assets/guide_configure_peer-configuration.md.f1ef1883.js b/assets/guide_configure_peer-configuration.md.f1ef1883.js new file mode 100644 index 000000000..1aeabd7c2 --- /dev/null +++ b/assets/guide_configure_peer-configuration.md.f1ef1883.js @@ -0,0 +1,197 @@ +import{_ as s,o as n,c as a,Q as o}from"./chunks/framework.1293becd.js";const q=JSON.parse('{"title":"Peer Configuration","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/peer-configuration.md","filePath":"guide/configure/peer-configuration.md","lastUpdated":1700563625000}'),l={name:"guide/configure/peer-configuration.md"},p=o(`

Peer Configuration

The peer configuration file (configs/peer/config.json) determines how your blockchain operates.

Here's an example of how peer configuration file looks like:

Peer configuration example
json
{
+  "PUBLIC_KEY": null,
+  "PRIVATE_KEY": null,
+  "DISABLE_PANIC_TERMINAL_COLORS": false,
+  "KURA": {
+    "INIT_MODE": "strict",
+    "BLOCK_STORE_PATH": "./storage",
+    "BLOCKS_PER_STORAGE_FILE": 1000,
+    "ACTOR_CHANNEL_CAPACITY": 100,
+    "DEBUG_OUTPUT_NEW_BLOCKS": false
+  },
+  "SUMERAGI": {
+    "KEY_PAIR": null,
+    "PEER_ID": null,
+    "BLOCK_TIME_MS": 2000,
+    "TRUSTED_PEERS": null,
+    "COMMIT_TIME_LIMIT_MS": 4000,
+    "MAX_TRANSACTIONS_IN_BLOCK": 512,
+    "ACTOR_CHANNEL_CAPACITY": 100,
+    "GOSSIP_BATCH_SIZE": 500,
+    "GOSSIP_PERIOD_MS": 1000
+  },
+  "TORII": {
+    "P2P_ADDR": null,
+    "API_URL": null,
+    "TELEMETRY_URL": null,
+    "MAX_TRANSACTION_SIZE": 32768,
+    "MAX_CONTENT_LEN": 16384000,
+    "FETCH_SIZE": 10,
+    "QUERY_IDLE_TIME_MS": 30000
+  },
+  "BLOCK_SYNC": {
+    "GOSSIP_PERIOD_MS": 10000,
+    "BLOCK_BATCH_SIZE": 4,
+    "ACTOR_CHANNEL_CAPACITY": 100
+  },
+  "QUEUE": {
+    "MAX_TRANSACTIONS_IN_QUEUE": 65536,
+    "MAX_TRANSACTIONS_IN_QUEUE_PER_USER": 65536,
+    "TRANSACTION_TIME_TO_LIVE_MS": 86400000,
+    "FUTURE_THRESHOLD_MS": 1000
+  },
+  "LOGGER": {
+    "MAX_LOG_LEVEL": "INFO",
+    "TELEMETRY_CAPACITY": 1000,
+    "COMPACT_MODE": false,
+    "LOG_FILE_PATH": null,
+    "TERMINAL_COLORS": true
+  },
+  "GENESIS": {
+    "ACCOUNT_PUBLIC_KEY": null,
+    "ACCOUNT_PRIVATE_KEY": null
+  },
+  "WSV": {
+    "ASSET_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "ASSET_DEFINITION_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "ACCOUNT_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "DOMAIN_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "IDENT_LENGTH_LIMITS": {
+      "min": 1,
+      "max": 128
+    },
+    "TRANSACTION_LIMITS": {
+      "max_instruction_number": 4096,
+      "max_wasm_size_bytes": 4194304
+    },
+    "WASM_RUNTIME_CONFIG": {
+      "FUEL_LIMIT": 23000000,
+      "MAX_MEMORY": 524288000
+    }
+  },
+  "NETWORK": {
+    "ACTOR_CHANNEL_CAPACITY": 100
+  },
+  "TELEMETRY": {
+    "NAME": null,
+    "URL": null,
+    "MIN_RETRY_PERIOD": 1,
+    "MAX_RETRY_DELAY_EXPONENT": 4,
+    "FILE": null
+  },
+  "SNAPSHOT": {
+    "CREATE_EVERY_MS": 60000,
+    "DIR_PATH": "./storage",
+    "CREATION_ENABLED": true
+  }
+}
{
+  "PUBLIC_KEY": null,
+  "PRIVATE_KEY": null,
+  "DISABLE_PANIC_TERMINAL_COLORS": false,
+  "KURA": {
+    "INIT_MODE": "strict",
+    "BLOCK_STORE_PATH": "./storage",
+    "BLOCKS_PER_STORAGE_FILE": 1000,
+    "ACTOR_CHANNEL_CAPACITY": 100,
+    "DEBUG_OUTPUT_NEW_BLOCKS": false
+  },
+  "SUMERAGI": {
+    "KEY_PAIR": null,
+    "PEER_ID": null,
+    "BLOCK_TIME_MS": 2000,
+    "TRUSTED_PEERS": null,
+    "COMMIT_TIME_LIMIT_MS": 4000,
+    "MAX_TRANSACTIONS_IN_BLOCK": 512,
+    "ACTOR_CHANNEL_CAPACITY": 100,
+    "GOSSIP_BATCH_SIZE": 500,
+    "GOSSIP_PERIOD_MS": 1000
+  },
+  "TORII": {
+    "P2P_ADDR": null,
+    "API_URL": null,
+    "TELEMETRY_URL": null,
+    "MAX_TRANSACTION_SIZE": 32768,
+    "MAX_CONTENT_LEN": 16384000,
+    "FETCH_SIZE": 10,
+    "QUERY_IDLE_TIME_MS": 30000
+  },
+  "BLOCK_SYNC": {
+    "GOSSIP_PERIOD_MS": 10000,
+    "BLOCK_BATCH_SIZE": 4,
+    "ACTOR_CHANNEL_CAPACITY": 100
+  },
+  "QUEUE": {
+    "MAX_TRANSACTIONS_IN_QUEUE": 65536,
+    "MAX_TRANSACTIONS_IN_QUEUE_PER_USER": 65536,
+    "TRANSACTION_TIME_TO_LIVE_MS": 86400000,
+    "FUTURE_THRESHOLD_MS": 1000
+  },
+  "LOGGER": {
+    "MAX_LOG_LEVEL": "INFO",
+    "TELEMETRY_CAPACITY": 1000,
+    "COMPACT_MODE": false,
+    "LOG_FILE_PATH": null,
+    "TERMINAL_COLORS": true
+  },
+  "GENESIS": {
+    "ACCOUNT_PUBLIC_KEY": null,
+    "ACCOUNT_PRIVATE_KEY": null
+  },
+  "WSV": {
+    "ASSET_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "ASSET_DEFINITION_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "ACCOUNT_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "DOMAIN_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "IDENT_LENGTH_LIMITS": {
+      "min": 1,
+      "max": 128
+    },
+    "TRANSACTION_LIMITS": {
+      "max_instruction_number": 4096,
+      "max_wasm_size_bytes": 4194304
+    },
+    "WASM_RUNTIME_CONFIG": {
+      "FUEL_LIMIT": 23000000,
+      "MAX_MEMORY": 524288000
+    }
+  },
+  "NETWORK": {
+    "ACTOR_CHANNEL_CAPACITY": 100
+  },
+  "TELEMETRY": {
+    "NAME": null,
+    "URL": null,
+    "MIN_RETRY_PERIOD": 1,
+    "MAX_RETRY_DELAY_EXPONENT": 4,
+    "FILE": null
+  },
+  "SNAPSHOT": {
+    "CREATE_EVERY_MS": 60000,
+    "DIR_PATH": "./storage",
+    "CREATION_ENABLED": true
+  }
+}

Some of the configuration options are required, while others are used for fine-tuning. When you create a new peer, you are required to provide the following:

For the full list of configuration options, refer to Iroha Configuration Reference.

INFO

Configuration options have different underlying types and default values, which are denoted in code as types wrapped in a single Option<..> or in a double Option<Option<..>>. Refer to configuration types for details.

Generation

You can use kagami to generate the default peer configuration:

bash
$ kagami config peer > peer-config.json
$ kagami config peer > peer-config.json

Public and private keys

The configs/peer/config.json peer configuration file should contain a pair of the user's public PUBLIC_KEY and private PRIVATE_KEY cryptographic keys for their account's ACCOUNT_ID.

For details on cryptographic keys, see Public Key Cryptography.

Trusted Peers

Iroha is a blockchain ledger. In order for it to work optimally and be Byzantine-fault tolerant with the maximum number of faults allowed, it needs to be started with a set number of peers: 4, 7, 10, ... 3f+1, where f is the allowed number of faults.

So usually, when you want to start an Iroha deployment, you should already know a number of peers that you can trust and join their blockchain. The way it works in the examples is that you just specify in four config.json files four peers with their public keys and API addresses.

Since Iroha has no automatic peer discovery, the only other way to make peers known to each other is to use the iroha_client_cli to register new peers). This is not too difficult with the provided client libraries. With Python's Beautiful Soup, the curated list of peers can be updated, registered, and un-registered on its own.

The list of trusted peers is a part of SUMERAGI configuration. Here's an example of SUMERAGI_TRUSTED_PEERS environment variable to configure trusted peers:

'[{"address":"iroha0:1337", "public_key": "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"}, {"address":"iroha1:1338", "public_key": "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"}, {"address": "iroha2:1339", "public_key": "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"}, {"address": "iroha3:1340", "public_key": "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
'[{"address":"iroha0:1337", "public_key": "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"}, {"address":"iroha1:1338", "public_key": "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"}, {"address": "iroha2:1339", "public_key": "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"}, {"address": "iroha3:1340", "public_key": "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'

Iroha Public Addresses

TORII is the module in charge of handling incoming and outgoing connections.

Here we only cover the required configurations: API_URL, P2P_ADDR, and TELEMETRY_URL. Check TORII configuration reference for all available options.

API_URL

The API_URL is the location to which the client(s) make their requests. You can also use it to change some peer-specific configuration options.

Most of the time, the only reason to change the API_URL is to change the port, in case 8080 is either closed, or if you want to randomise ports to avoid certain kinds of attacks.

P2P_ADDR

The P2P_ADDR is the internal address used for communication between peers. This address should be included in the TRUSTED_PEERS section of the configuration file.

TELEMETRY_URL

The TELEMETRY_URL is used to specify the prometheus endpoint address. It's set by adding "TELEMETRY_URL": "127.0.0.1:8180" to the TORII section of the configuration file.

It's not meant to be human-readable. However, a GET request to the 127.0.0.1:8180/status will give you a JSON-encoded representation of the top-level metrics, while a GET request to 127.0.0.1:8180/metrics will give you a (somewhat verbose) list of all available metrics gathered in Iroha. You might want to change this if you're having trouble gathering metrics using prometheus.

INFO

Learn how to monitor Iroha performance using prometheus.

Genesis

When you configure a peer, you have to provide private and public keys for the genesis account.

You can do this via the configuration file (ACCOUNT_PUBLIC_KEY, ACCOUNT_PRIVATE_KEY) or environment variables (IROHA_GENESIS_ACCOUNT_PUBLIC_KEY, IROHA_GENESIS_ACCOUNT_PRIVATE_KEY).

To learn more about genesis block, genesis account, and cryptographic keys, see the following:

Aside from the public and private keys for the genesis account, which are required configuration options, you can also fine-tune other genesis block configurations, such as:

  • WAIT_FOR_PEERS_RETRY_COUNT_LIMIT: the number of attempts to connect to peers before genesis block is submitted
  • WAIT_FOR_PEERS_RETRY_PERIOD_MS: how long to wait before retrying a connection to peers
  • GENESIS_SUBMISSION_DELAY_MS: the delay before the genesis block submission after the minimum number of peers were discovered.

You can find more details in GENESIS Iroha Configuration Reference.

Logger

Let's cover the most useful LOGGER configurations, MAX_LOG_LEVEL and LOG_FILE_PATH.

MAX_LOG_LEVEL

The MAX_LOG_LEVEL is used to determine which messages are logged.

With "MAX_LOG_LEVEL": "WARN" you won't get any messages unless they are either a warning or an error. Beside WARN, other available options are:

  • TRACE (log every time you enter a function)
  • DEBUG (use when you know something went wrong)
  • INFO (the default)
  • WARN (log everything that could be an error)
  • ERROR (to silence any logging except for error messages)

LOG_FILE_PATH

Another useful option is "LOG_FILE_PATH": bunyan.json. It creates (if it didn't already exist) a file called bunyan.json that contains the message log in a structured format.

This is extremely useful for two reasons. Firstly, you can use the bunyan log viewer to filter information more precisely than Iroha would allow you to do. Do you only want messages from a specific module or package? You can do that with bunyan. Secondly, while copying logs is not too big of a problem if your instance is just a small setup, for bigger setups the log will be larger. Having it saved to a file makes much more sense in that case.

INFO

You can also set LOG_FILE_PATH to /dev/stdout if you want to use bunyan's logging facilities directly without saving the output.

Kura

Kura is the persistent storage engine of Iroha (Japanese for warehouse). The BLOCK_STORE_PATH specifies where the blocks are stored. You can change it to a custom location if for some reason the default location (./storage) is not available or desirable.

For more details, check KURA configuration reference.

`,53),e=[p];function t(c,r,E,y,i,u){return n(),a("div",null,e)}const C=s(l,[["render",t]]);export{q as __pageData,C as default}; diff --git a/assets/guide_configure_peer-configuration.md.f1ef1883.lean.js b/assets/guide_configure_peer-configuration.md.f1ef1883.lean.js new file mode 100644 index 000000000..f6679092d --- /dev/null +++ b/assets/guide_configure_peer-configuration.md.f1ef1883.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as o}from"./chunks/framework.1293becd.js";const q=JSON.parse('{"title":"Peer Configuration","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/peer-configuration.md","filePath":"guide/configure/peer-configuration.md","lastUpdated":1700563625000}'),l={name:"guide/configure/peer-configuration.md"},p=o("",53),e=[p];function t(c,r,E,y,i,u){return n(),a("div",null,e)}const C=s(l,[["render",t]]);export{q as __pageData,C as default}; diff --git a/assets/guide_configure_peer-management.md.e212b1c4.js b/assets/guide_configure_peer-management.md.e212b1c4.js new file mode 100644 index 000000000..ea17f57af --- /dev/null +++ b/assets/guide_configure_peer-management.md.e212b1c4.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as a,Q as r}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Peer Management","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/peer-management.md","filePath":"guide/configure/peer-management.md","lastUpdated":1700563625000}'),i={name:"guide/configure/peer-management.md"},o=r('

Peer Management

If you followed any of the language-specific guides, you now have a well-functioning network that people will want to join.

Public Blockchain

Naturally, in a public blockchain, joining is a matter of installing the correct software and waiting until your node gets discovered.

INFO

Peer discovery is under construction.

Private Blockchain

In a bank setting, allowing everyone to join at their leisure is a security nightmare. For safety, automatic discovery of peers is turned off for Iroha 2 in the private blockchain configuration.

Registering peers

To add a peer to the network, it must be manually registered. Let's discuss the steps that should be taken in order to complete this process.

1. Grant the user permissions

The user that registers the peer must have the appropriate PermissionToken. This could be granted as part of a role, or as part of a one-time allowance.

How to decide if you need to grant a role? Granting roles makes sense if a user is to serve as an administrator of sorts, where it's their responsibility to maintain the peers in the network long-term. A one-time permission grant is useful when the party registering the peer isn't responsible for registering peers in general, but the network administrator doesn't need to (or want to) spend time setting up a new peer.

INFO

Permissions for registering a peer are under construction.

We discuss permissions and roles with more detail in a separate chapter.

2. Set up a peer

After a new peer was granted permissions, it must be set up.

It's a good idea to request information about the peers' configuration in the network. Your best friend is the configuration endpoint of the API socket. Thus far querying is done manually. Until the bootstrapping procedure is implemented, you'll have to manually check that the timeouts and batch sizes match.

To simplify the process, you can ask the network administrator for a redacted version of config.json, which excludes privileged information, such as PRIVATE_KEYs.

3. Submit the instruction

After your peer is running, you should submit the register peer instruction. The peer will go through the handshake process and start chatting with the network.

TIP

Submitting a Register<Peer> instruction does not (and cannot) instantiate a new peer process.

Unregistering peers

What about unregistering peers? For security reasons this process is one-sided. The network reaches consensus that it wants to remove a peer, but the peer itself doesn't know much about why nobody's talking to it.

In most circumstances, if you want to unregister a peer, you want to do so because it is a Byzantine fault. Just "ghosting" this peer makes the life of the malicious actor on the network harder.

',24),s=[o];function n(c,h,p,l,u,d){return t(),a("div",null,s)}const f=e(i,[["render",n]]);export{g as __pageData,f as default}; diff --git a/assets/guide_configure_peer-management.md.e212b1c4.lean.js b/assets/guide_configure_peer-management.md.e212b1c4.lean.js new file mode 100644 index 000000000..abbaffb6b --- /dev/null +++ b/assets/guide_configure_peer-management.md.e212b1c4.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as a,Q as r}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Peer Management","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/peer-management.md","filePath":"guide/configure/peer-management.md","lastUpdated":1700563625000}'),i={name:"guide/configure/peer-management.md"},o=r("",24),s=[o];function n(c,h,p,l,u,d){return t(),a("div",null,s)}const f=e(i,[["render",n]]);export{g as __pageData,f as default}; diff --git a/assets/guide_configure_sample-configuration.md.94949a44.js b/assets/guide_configure_sample-configuration.md.94949a44.js new file mode 100644 index 000000000..8121b79e1 --- /dev/null +++ b/assets/guide_configure_sample-configuration.md.94949a44.js @@ -0,0 +1,637 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.1293becd.js";const C=JSON.parse('{"title":"Sample Configuration Files","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/sample-configuration.md","filePath":"guide/configure/sample-configuration.md","lastUpdated":1700563625000}'),p={name:"guide/configure/sample-configuration.md"},o=l(`

Sample Configuration Files

Here you can find sample configuration files for Iroha 2:

  • Peer Configuration (configs/peer/config.json). Refer to peer configuration for details.
  • Genesis Block (configs/peer/genesis.json). Refer to genesis block for details.
  • Client Configuration (configs/client_cli/config.json). Refer to client configuration for details.
json
{
+  "PUBLIC_KEY": null,
+  "PRIVATE_KEY": null,
+  "DISABLE_PANIC_TERMINAL_COLORS": false,
+  "KURA": {
+    "INIT_MODE": "strict",
+    "BLOCK_STORE_PATH": "./storage",
+    "BLOCKS_PER_STORAGE_FILE": 1000,
+    "ACTOR_CHANNEL_CAPACITY": 100,
+    "DEBUG_OUTPUT_NEW_BLOCKS": false
+  },
+  "SUMERAGI": {
+    "KEY_PAIR": null,
+    "PEER_ID": null,
+    "BLOCK_TIME_MS": 2000,
+    "TRUSTED_PEERS": null,
+    "COMMIT_TIME_LIMIT_MS": 4000,
+    "MAX_TRANSACTIONS_IN_BLOCK": 512,
+    "ACTOR_CHANNEL_CAPACITY": 100,
+    "GOSSIP_BATCH_SIZE": 500,
+    "GOSSIP_PERIOD_MS": 1000
+  },
+  "TORII": {
+    "P2P_ADDR": null,
+    "API_URL": null,
+    "TELEMETRY_URL": null,
+    "MAX_TRANSACTION_SIZE": 32768,
+    "MAX_CONTENT_LEN": 16384000,
+    "FETCH_SIZE": 10,
+    "QUERY_IDLE_TIME_MS": 30000
+  },
+  "BLOCK_SYNC": {
+    "GOSSIP_PERIOD_MS": 10000,
+    "BLOCK_BATCH_SIZE": 4,
+    "ACTOR_CHANNEL_CAPACITY": 100
+  },
+  "QUEUE": {
+    "MAX_TRANSACTIONS_IN_QUEUE": 65536,
+    "MAX_TRANSACTIONS_IN_QUEUE_PER_USER": 65536,
+    "TRANSACTION_TIME_TO_LIVE_MS": 86400000,
+    "FUTURE_THRESHOLD_MS": 1000
+  },
+  "LOGGER": {
+    "MAX_LOG_LEVEL": "INFO",
+    "TELEMETRY_CAPACITY": 1000,
+    "COMPACT_MODE": false,
+    "LOG_FILE_PATH": null,
+    "TERMINAL_COLORS": true
+  },
+  "GENESIS": {
+    "ACCOUNT_PUBLIC_KEY": null,
+    "ACCOUNT_PRIVATE_KEY": null
+  },
+  "WSV": {
+    "ASSET_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "ASSET_DEFINITION_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "ACCOUNT_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "DOMAIN_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "IDENT_LENGTH_LIMITS": {
+      "min": 1,
+      "max": 128
+    },
+    "TRANSACTION_LIMITS": {
+      "max_instruction_number": 4096,
+      "max_wasm_size_bytes": 4194304
+    },
+    "WASM_RUNTIME_CONFIG": {
+      "FUEL_LIMIT": 23000000,
+      "MAX_MEMORY": 524288000
+    }
+  },
+  "NETWORK": {
+    "ACTOR_CHANNEL_CAPACITY": 100
+  },
+  "TELEMETRY": {
+    "NAME": null,
+    "URL": null,
+    "MIN_RETRY_PERIOD": 1,
+    "MAX_RETRY_DELAY_EXPONENT": 4,
+    "FILE": null
+  },
+  "SNAPSHOT": {
+    "CREATE_EVERY_MS": 60000,
+    "DIR_PATH": "./storage",
+    "CREATION_ENABLED": true
+  }
+}
{
+  "PUBLIC_KEY": null,
+  "PRIVATE_KEY": null,
+  "DISABLE_PANIC_TERMINAL_COLORS": false,
+  "KURA": {
+    "INIT_MODE": "strict",
+    "BLOCK_STORE_PATH": "./storage",
+    "BLOCKS_PER_STORAGE_FILE": 1000,
+    "ACTOR_CHANNEL_CAPACITY": 100,
+    "DEBUG_OUTPUT_NEW_BLOCKS": false
+  },
+  "SUMERAGI": {
+    "KEY_PAIR": null,
+    "PEER_ID": null,
+    "BLOCK_TIME_MS": 2000,
+    "TRUSTED_PEERS": null,
+    "COMMIT_TIME_LIMIT_MS": 4000,
+    "MAX_TRANSACTIONS_IN_BLOCK": 512,
+    "ACTOR_CHANNEL_CAPACITY": 100,
+    "GOSSIP_BATCH_SIZE": 500,
+    "GOSSIP_PERIOD_MS": 1000
+  },
+  "TORII": {
+    "P2P_ADDR": null,
+    "API_URL": null,
+    "TELEMETRY_URL": null,
+    "MAX_TRANSACTION_SIZE": 32768,
+    "MAX_CONTENT_LEN": 16384000,
+    "FETCH_SIZE": 10,
+    "QUERY_IDLE_TIME_MS": 30000
+  },
+  "BLOCK_SYNC": {
+    "GOSSIP_PERIOD_MS": 10000,
+    "BLOCK_BATCH_SIZE": 4,
+    "ACTOR_CHANNEL_CAPACITY": 100
+  },
+  "QUEUE": {
+    "MAX_TRANSACTIONS_IN_QUEUE": 65536,
+    "MAX_TRANSACTIONS_IN_QUEUE_PER_USER": 65536,
+    "TRANSACTION_TIME_TO_LIVE_MS": 86400000,
+    "FUTURE_THRESHOLD_MS": 1000
+  },
+  "LOGGER": {
+    "MAX_LOG_LEVEL": "INFO",
+    "TELEMETRY_CAPACITY": 1000,
+    "COMPACT_MODE": false,
+    "LOG_FILE_PATH": null,
+    "TERMINAL_COLORS": true
+  },
+  "GENESIS": {
+    "ACCOUNT_PUBLIC_KEY": null,
+    "ACCOUNT_PRIVATE_KEY": null
+  },
+  "WSV": {
+    "ASSET_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "ASSET_DEFINITION_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "ACCOUNT_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "DOMAIN_METADATA_LIMITS": {
+      "max_len": 1048576,
+      "max_entry_byte_size": 4096
+    },
+    "IDENT_LENGTH_LIMITS": {
+      "min": 1,
+      "max": 128
+    },
+    "TRANSACTION_LIMITS": {
+      "max_instruction_number": 4096,
+      "max_wasm_size_bytes": 4194304
+    },
+    "WASM_RUNTIME_CONFIG": {
+      "FUEL_LIMIT": 23000000,
+      "MAX_MEMORY": 524288000
+    }
+  },
+  "NETWORK": {
+    "ACTOR_CHANNEL_CAPACITY": 100
+  },
+  "TELEMETRY": {
+    "NAME": null,
+    "URL": null,
+    "MIN_RETRY_PERIOD": 1,
+    "MAX_RETRY_DELAY_EXPONENT": 4,
+    "FILE": null
+  },
+  "SNAPSHOT": {
+    "CREATE_EVERY_MS": 60000,
+    "DIR_PATH": "./storage",
+    "CREATION_ENABLED": true
+  }
+}
json
{
+  "transactions": [
+    [
+      {
+        "Register": {
+          "NewDomain": {
+            "id": "wonderland",
+            "logo": null,
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "alice@wonderland",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "bob@wonderland",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAssetDefinition": {
+            "id": "rose#wonderland",
+            "value_type": "Quantity",
+            "mintable": "Infinitely",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewDomain": {
+            "id": "garden_of_live_flowers",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "carpenter@garden_of_live_flowers",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAssetDefinition": {
+            "id": "cabbage#garden_of_live_flowers",
+            "value_type": "Quantity",
+            "mintable": "Infinitely",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Mint": {
+          "object": "13_u32",
+          "destination_id": {
+            "AssetId": "rose##alice@wonderland"
+          }
+        }
+      },
+      {
+        "Mint": {
+          "object": "44_u32",
+          "destination_id": {
+            "AssetId": "cabbage#garden_of_live_flowers#alice@wonderland"
+          }
+        }
+      },
+      {
+        "Grant": {
+          "object": {
+            "PermissionToken": {
+              "definition_id": "CanSetParameters",
+              "payload": null
+            }
+          },
+          "destination_id": {
+            "AccountId": "alice@wonderland"
+          }
+        }
+      },
+      {
+        "Sequence": [
+          {
+            "NewParameter": {
+              "Parameter": "?MaxTransactionsInBlock=512"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?BlockTime=2000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?CommitTimeLimit=4000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?TransactionLimits=4096,4194304_TL"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAssetMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAccountMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVDomainMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVIdentLengthLimits=1,128_LL"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WASMFuelLimit=23000000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WASMMaxMemory=524288000"
+            }
+          }
+        ]
+      },
+      {
+        "Register": {
+          "NewRole": {
+            "id": "ALICE_METADATA_ACCESS",
+            "permissions": [
+              {
+                "definition_id": "CanRemoveKeyValueInUserAccount",
+                "payload": {
+                  "account_id": "alice@wonderland"
+                }
+              },
+              {
+                "definition_id": "CanSetKeyValueInUserAccount",
+                "payload": {
+                  "account_id": "alice@wonderland"
+                }
+              }
+            ]
+          }
+        }
+      }
+    ]
+  ],
+  "validator": "./validator.wasm"
+}
{
+  "transactions": [
+    [
+      {
+        "Register": {
+          "NewDomain": {
+            "id": "wonderland",
+            "logo": null,
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "alice@wonderland",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "bob@wonderland",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {
+              "key": {
+                "String": "value"
+              }
+            }
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAssetDefinition": {
+            "id": "rose#wonderland",
+            "value_type": "Quantity",
+            "mintable": "Infinitely",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewDomain": {
+            "id": "garden_of_live_flowers",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAccount": {
+            "id": "carpenter@garden_of_live_flowers",
+            "signatories": [
+              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
+            ],
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Register": {
+          "NewAssetDefinition": {
+            "id": "cabbage#garden_of_live_flowers",
+            "value_type": "Quantity",
+            "mintable": "Infinitely",
+            "logo": null,
+            "metadata": {}
+          }
+        }
+      },
+      {
+        "Mint": {
+          "object": "13_u32",
+          "destination_id": {
+            "AssetId": "rose##alice@wonderland"
+          }
+        }
+      },
+      {
+        "Mint": {
+          "object": "44_u32",
+          "destination_id": {
+            "AssetId": "cabbage#garden_of_live_flowers#alice@wonderland"
+          }
+        }
+      },
+      {
+        "Grant": {
+          "object": {
+            "PermissionToken": {
+              "definition_id": "CanSetParameters",
+              "payload": null
+            }
+          },
+          "destination_id": {
+            "AccountId": "alice@wonderland"
+          }
+        }
+      },
+      {
+        "Sequence": [
+          {
+            "NewParameter": {
+              "Parameter": "?MaxTransactionsInBlock=512"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?BlockTime=2000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?CommitTimeLimit=4000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?TransactionLimits=4096,4194304_TL"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAssetMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVAccountMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVDomainMetadataLimits=1048576,4096_ML"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WSVIdentLengthLimits=1,128_LL"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WASMFuelLimit=23000000"
+            }
+          },
+          {
+            "NewParameter": {
+              "Parameter": "?WASMMaxMemory=524288000"
+            }
+          }
+        ]
+      },
+      {
+        "Register": {
+          "NewRole": {
+            "id": "ALICE_METADATA_ACCESS",
+            "permissions": [
+              {
+                "definition_id": "CanRemoveKeyValueInUserAccount",
+                "payload": {
+                  "account_id": "alice@wonderland"
+                }
+              },
+              {
+                "definition_id": "CanSetKeyValueInUserAccount",
+                "payload": {
+                  "account_id": "alice@wonderland"
+                }
+              }
+            ]
+          }
+        }
+      }
+    ]
+  ],
+  "validator": "./validator.wasm"
+}
json
{
+  "PUBLIC_KEY": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
+  "PRIVATE_KEY": {
+    "digest_function": "ed25519",
+    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
+  },
+  "ACCOUNT_ID": "alice@wonderland",
+  "BASIC_AUTH": {
+    "web_login": "mad_hatter",
+    "password": "ilovetea"
+  },
+  "TORII_API_URL": "http://127.0.0.1:8080/",
+  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180/",
+  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
+  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
+  "TRANSACTION_LIMITS": {
+    "max_instruction_number": 4096,
+    "max_wasm_size_bytes": 4194304
+  },
+  "ADD_TRANSACTION_NONCE": false
+}
{
+  "PUBLIC_KEY": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
+  "PRIVATE_KEY": {
+    "digest_function": "ed25519",
+    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
+  },
+  "ACCOUNT_ID": "alice@wonderland",
+  "BASIC_AUTH": {
+    "web_login": "mad_hatter",
+    "password": "ilovetea"
+  },
+  "TORII_API_URL": "http://127.0.0.1:8080/",
+  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180/",
+  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
+  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
+  "TRANSACTION_LIMITS": {
+    "max_instruction_number": 4096,
+    "max_wasm_size_bytes": 4194304
+  },
+  "ADD_TRANSACTION_NONCE": false
+}
`,4),t=[o];function e(E,c,r,y,u,i){return n(),a("div",null,t)}const F=s(p,[["render",e]]);export{C as __pageData,F as default}; diff --git a/assets/guide_configure_sample-configuration.md.94949a44.lean.js b/assets/guide_configure_sample-configuration.md.94949a44.lean.js new file mode 100644 index 000000000..9732eb780 --- /dev/null +++ b/assets/guide_configure_sample-configuration.md.94949a44.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.1293becd.js";const C=JSON.parse('{"title":"Sample Configuration Files","description":"","frontmatter":{},"headers":[],"relativePath":"guide/configure/sample-configuration.md","filePath":"guide/configure/sample-configuration.md","lastUpdated":1700563625000}'),p={name:"guide/configure/sample-configuration.md"},o=l("",4),t=[o];function e(E,c,r,y,u,i){return n(),a("div",null,t)}const F=s(p,[["render",e]]);export{C as __pageData,F as default}; diff --git a/assets/guide_get-started_bash.md.b307a61c.js b/assets/guide_get-started_bash.md.b307a61c.js new file mode 100644 index 000000000..8f5cf82f4 --- /dev/null +++ b/assets/guide_get-started_bash.md.b307a61c.js @@ -0,0 +1,409 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.1293becd.js";const F=JSON.parse('{"title":"Bash Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/bash.md","filePath":"guide/get-started/bash.md","lastUpdated":1700563625000}'),o={name:"guide/get-started/bash.md"},p=l(`

Bash Guide

0. A brief primer on CLI applications

This is as good a place as any to discuss what iroha_client_cli is and what you should expect it to be able to do.

Most users think that everything that's run inside the terminal is a CLI program. This is not the case. A Command-line Interface is a glorified scripting language that you interact with using the shell.

CLI programs are run multiple times and given different arguments: --flag1 file2 --help, etc., depending on what you want to do. A single session doesn't begin with you opening the program and end with you closing it. When you've stopped interacting, the iroha_client_cli session is finished.

You also don't configure a CLI program in the usual sense of the word. Most people expect that it's possible to change some settings of a program from inside that same program, but most CLI programs are configured differently.

First of all, if you want to make a certain --flag part of the way you invoke iroha_client_cli, you should create a shell alias. Some programs, but not iroha_client_cli, also use something called Environment variables.

Finally, most programs store persistent information that is too big for either a shell alias or an environment variable in a separate file. iroha_client_cli does not yet follow the XDG_CONFIG_HOME specification. It only looks for a configuration file in one of two places:

  1. If the -c or --config command line flag is specified, the next argument interpreted as a path.

    For example: -c ~/Git/iroha/configs/peer/config.json. If that file doesn't exist, you will see an error, and iroha_client_cli won't look for a configuration file anywhere else.

  2. If neither -c nor --config were given, it will look in the current working directory.

These defaults are not very ergonomic. They are artifacts of the way in which Iroha is being deployed, and the fact that a CLI interface is used exclusively for testing purposes.

INFO

This might change in the future, but likely not by fixing iroha_client_cli but rather by replacing it entirely with Iroha Python. The only thing stopping us from that today is that Iroha Python has not gotten the attention it deserves.

It is possible that a user might be expecting iroha_client_cli to behave like a graphical program using terminal graphics: a Terminal User Interface. While we'd like to provide you with such a program, we don't think that it offers enough convenience over the amount of effort that we'd need to put in.

INFO

It is possible that in the future, once iroha_client_cli is phased out and iroha_python is the official scripting interface, we might use the excellent Python libraries for creating a useful TUI. For now, one must make do with what one has.

1. Iroha 2 Client Setup

Note, first, that we have already created the iroha_client_cli binary executable, when we ran the build command.

Create a fresh directory for the client:

bash
$ mkdir -p test_docker
$ mkdir -p test_docker

Copy the configuration file to the client directory:

bash
$ cp ./configs/client/config.json test_docker/
$ cp ./configs/client/config.json test_docker/

TIP

You could also use a file manager (e.g. finder) to do that. We prefer providing command-line instructions, because they are easier to follow step-by-step.

To test Iroha 2 metadata capabilities, let's also create a dummy metadata.json file:

bash
$ echo '{"comment":{"String": "Hello Meta!"}}' > test_docker/metadata.json
$ echo '{"comment":{"String": "Hello Meta!"}}' > test_docker/metadata.json

To get the CLI started, copy the iroha_client_cli binary into the client directory:

bash
$ cp ./target/debug/iroha_client_cli test_docker/
$ cp ./target/debug/iroha_client_cli test_docker/

Make sure you bring up the test network as well.

2. Configuring Iroha 2

Now let's look at how to properly configure Iroha 2, and especially its Command-Line Interface client.

Make sure that you have another terminal tab or window open with a running version, using the instructions above. You can use this screen to monitor the pipeline events as they are output.

On a new terminal tab run:

bash
$ cd ~/Git/iroha/test_docker
$ cd ~/Git/iroha/test_docker

If you followed the steps correctly, this should contain the iroha_client_cli and config.json.

TIP

Use ls to make sure both files are there, and if not, return to Step 1.

Run

bash
$ ./iroha_client_cli
$ ./iroha_client_cli
Expand to see the expected output
iroha_client_cli 0.1.0
+Soramitsu Iroha2 team (https, //github.com/orgs/soramitsu/teams/iroha2)
+Iroha CLI Client provides an ability to interact with Iroha Peers Web API without direct network usage
+
+USAGE:
+    iroha_client_cli [OPTIONS] <SUBCOMMAND>
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information
+
+OPTIONS:
+    -c, --config <config>    Sets a config file path [default: config.json]
+
+SUBCOMMANDS:
+    account    The subcommand related to accounts
+    asset      The subcommand related to assets
+    blocks     The subcommand related to block streaming
+    domain     The subcommand related to domains
+    events     The subcommand related to event streaming
+    help       Prints this message or the help of the given subcommand(s)
+    json       The subcommand related to multi-instructions as Json
+    peer       The subcommand related to p2p networking
+    wasm       The subcommand related to Wasm
iroha_client_cli 0.1.0
+Soramitsu Iroha2 team (https, //github.com/orgs/soramitsu/teams/iroha2)
+Iroha CLI Client provides an ability to interact with Iroha Peers Web API without direct network usage
+
+USAGE:
+    iroha_client_cli [OPTIONS] <SUBCOMMAND>
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information
+
+OPTIONS:
+    -c, --config <config>    Sets a config file path [default: config.json]
+
+SUBCOMMANDS:
+    account    The subcommand related to accounts
+    asset      The subcommand related to assets
+    blocks     The subcommand related to block streaming
+    domain     The subcommand related to domains
+    events     The subcommand related to event streaming
+    help       Prints this message or the help of the given subcommand(s)
+    json       The subcommand related to multi-instructions as Json
+    peer       The subcommand related to p2p networking
+    wasm       The subcommand related to Wasm

To configure the Iroha client, run:

bash
$ ./iroha_client_cli --config ./test_docker/config.json
$ ./iroha_client_cli --config ./test_docker/config.json

It should be noted that this is not persistent configuration: each time you run iroha_client_cli, you must add the --config ./test_docker/config.json command-line argument.

TIP

Because the client checks the working directory for a file called config.json, it's always much easier to just copy (or link) the file into the working directory. Alternatively, you could also create a shell alias.

Feel free to edit the file and see what each option does. The only thing that you shouldn't edit at this point is the account. You see, alice has to be pre-registered in the genesis block. Only she can interact with the blockchain, and if you change the value of the user account, you should also make sure that that user exists in the blockchain.

To make sure that your configuration options worked, try to run a query, e.g.:

bash
$ ./iroha_client_cli domain list all
$ ./iroha_client_cli domain list all

If the output looks like some form of JSON (but not quite), then the configuration was successful!

3. Registering a Domain

To get started, you must register a domain:

bash
$ ./iroha_client_cli domain register --id="looking_glass"
$ ./iroha_client_cli domain register --id="looking_glass"

You will receive a confirmation of the domain creation. However, this information will not be clearly visible within the message. To confirm that the new domain looking_glass has been created successfully, run:

bash
$ ./iroha_client_cli domain list all
$ ./iroha_client_cli domain list all

The printout should contain the recently-created looking_glass domain

rust
Domain {
+    name: "looking_glass",
+    accounts: {},
+    asset_definitions: {},
+    metadata: Metadata {
+        map: {},
+    },
+},
Domain {
+    name: "looking_glass",
+    accounts: {},
+    asset_definitions: {},
+    metadata: Metadata {
+        map: {},
+    },
+},

With a domain available, it is time to register an account.

4. Registering an Account

To register a new account, you need a cryptographic key pair, a set of a public and private keys that establish a secure communication channel between a peer and the network (to learn more about cryptographic keys, see Public Key Cryptography).

There is a number of different ways to generate a cryptographic key pair. For the convenience of our users, Iroha 2 is delivered with kagami, an in-built tool for generating keys. However, any user is free to generate their keys any way they like.

To generate a new key pair with kagami, run the following command from your project's root directory:

bash
$ cargo build --bin kagami --release
+$ ./target/release/kagami crypto
$ cargo build --bin kagami --release
+$ ./target/release/kagami crypto

TIP

To customize the generated keys, you can specify a number of parameters. For instance, kagami can use of one of four available algorithms to generate cryptographic keys.

To learn more about generating cryptographic keys with kagami, available algorithms, and other parameters, see Generating Cryptographic Keys with Kagami

For the purposes of this tutorial, we will use the following key pair for mad_hatter@looking_glass:

bash
Public key (multihash): ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5
+Private key: 14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5
+Digest function: ed25519
Public key (multihash): ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5
+Private key: 14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5
+Digest function: ed25519

To register a new account called mad_hatter within the looking_glass domain, run:

bash
$ ./iroha_client_cli account register \\
+    --id="mad_hatter@looking_glass" \\
+    --key="ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5"
$ ./iroha_client_cli account register \\
+    --id="mad_hatter@looking_glass" \\
+    --key="ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5"

The --id argument in the above code snippet specifies the account id, the unique name assigned to that account in the blockchain. This name includes the domain, which is a group of things like asset definitions, account ids, and other objects that we'll talk about later. The --key argument specifies the public key, which is the one we generated above using kagami.

If the account registration is successful, you will receive a confirmation message. Like before, it is necessary to query the accounts to verify that mad_hatter has been registered.

Now, let's switch to the newly created account, mad_hatter, and continue experimenting with it. For this, we need to modify the PUBLIC_KEY, PRIVATE_KEY, and ACCOUNT_ID in the config.json file with the ones we registered earlier, which is located in the same directory as iroha_client_cli.

TIP

Your updated config.json should look like this

json
{
+  "PUBLIC_KEY": "ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5",
+  "PRIVATE_KEY": {
+    "digest_function": "ed25519",
+    "payload": "14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5"
+  },
+  "ACCOUNT_ID": "mad_hatter@looking_glass",
+  "BASIC_AUTH": {
+    "web_login": "mad_hatter",
+    "password": "ilovetea"
+  },
+  "TORII_API_URL": "http://127.0.0.1:8080",
+  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180",
+  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
+  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
+  "TRANSACTION_LIMITS": {
+    "max_instruction_number": 4096,
+    "max_wasm_size_bytes": 4194304
+  },
+  "ADD_TRANSACTION_NONCE": false
+}
{
+  "PUBLIC_KEY": "ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5",
+  "PRIVATE_KEY": {
+    "digest_function": "ed25519",
+    "payload": "14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5"
+  },
+  "ACCOUNT_ID": "mad_hatter@looking_glass",
+  "BASIC_AUTH": {
+    "web_login": "mad_hatter",
+    "password": "ilovetea"
+  },
+  "TORII_API_URL": "http://127.0.0.1:8080",
+  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180",
+  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
+  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
+  "TRANSACTION_LIMITS": {
+    "max_instruction_number": 4096,
+    "max_wasm_size_bytes": 4194304
+  },
+  "ADD_TRANSACTION_NONCE": false
+}

To see all the accounts on the network as mad_hatter, run:

bash
$ ./iroha_client_cli account list all
$ ./iroha_client_cli account list all

This will list the active accounts on the network, along with their assets. It will look like this:

Expand to see the expected output
rust
Account {
+    id: Id {
+        name: "mad_hatter",
+        domain_name: Id {
+            name: "looking_glass",
+        },
+    },
+    assets: {},
+    signatories: {
+        { digest: "ed25519", payload: "A753146E75B910AE5E2994DC8ADEA9E7D87E5D53024CFA310CE992F17106F92C",
+        },
+    },
+    permission_tokens: {},
+    signature_check_condition: SignatureCheckCondition(
+        EvaluatesTo {
+            expression: ContainsAny(
+                ContainsAny {
+                    collection: EvaluatesTo {
+                        expression: ContextValue(
+                            ContextValue {
+                                value_name: "transaction_signatories",
+                            },
+                        ),
+                        _value_type: PhantomData,
+                    },
+                    elements: EvaluatesTo {
+                        expression: ContextValue(
+                            ContextValue {
+                                value_name: "account_signatories",
+                            },
+                        ),
+                        _value_type: PhantomData,
+                    },
+                },
+            ),
+            _value_type: PhantomData,
+        },
+    ),
+    metadata: Metadata {
+        map: {},
+    },
+    roles: {},
+}
Account {
+    id: Id {
+        name: "mad_hatter",
+        domain_name: Id {
+            name: "looking_glass",
+        },
+    },
+    assets: {},
+    signatories: {
+        { digest: "ed25519", payload: "A753146E75B910AE5E2994DC8ADEA9E7D87E5D53024CFA310CE992F17106F92C",
+        },
+    },
+    permission_tokens: {},
+    signature_check_condition: SignatureCheckCondition(
+        EvaluatesTo {
+            expression: ContainsAny(
+                ContainsAny {
+                    collection: EvaluatesTo {
+                        expression: ContextValue(
+                            ContextValue {
+                                value_name: "transaction_signatories",
+                            },
+                        ),
+                        _value_type: PhantomData,
+                    },
+                    elements: EvaluatesTo {
+                        expression: ContextValue(
+                            ContextValue {
+                                value_name: "account_signatories",
+                            },
+                        ),
+                        _value_type: PhantomData,
+                    },
+                },
+            ),
+            _value_type: PhantomData,
+        },
+    ),
+    metadata: Metadata {
+        map: {},
+    },
+    roles: {},
+}

Another way to create a user (and the user's keys) is as follows:

  1. Open a new tab and navigate to the /iroha directory, then run:

    bash
    $ ./target/debug/iroha_crypto_cli
    $ ./target/debug/iroha_crypto_cli
  2. Copy the public key and repeat the instructions to register a new account. Every time you run this command, you will generate a new key-pair.

In this case, we will create an account for white_rabbit within the looking_glass domain, so we will run:

bash
$ ./iroha_client_cli account register \\
+    --id="white_rabbit@looking_glass" \\
+    --key="ed0120a4c4dadd9f18b0f63d6a420151fe0748d785475dec63034a15fcf999ceda1e65"
$ ./iroha_client_cli account register \\
+    --id="white_rabbit@looking_glass" \\
+    --key="ed0120a4c4dadd9f18b0f63d6a420151fe0748d785475dec63034a15fcf999ceda1e65"

And like before, the new active user will be listed on the network:

Expand to see the expected output
rust
Account {
+    id: Id {
+        name: "white_rabbit",
+        domain_name: Id {
+            name: "looking_glass",
+        },
+    },
+    assets: {},
+    signatories: {
+        { digest: "ed25519", payload: "A4C4DADD9F18B0F63D6A420151FE0748D785475DEC63034A15FCF999CEDA1E65",
+        },
+    },
+    permission_tokens: {},
+    signature_check_condition: SignatureCheckCondition(
+        EvaluatesTo {
+            expression: ContainsAny(
+                ContainsAny {
+                    collection: EvaluatesTo {
+                        expression: ContextValue(
+                            ContextValue {
+                                value_name: "transaction_signatories",
+                            },
+                        ),
+                        _value_type: PhantomData,
+                    },
+                    elements: EvaluatesTo {
+                        expression: ContextValue(
+                            ContextValue {
+                                value_name: "account_signatories",
+                            },
+                        ),
+                        _value_type: PhantomData,
+                    },
+                },
+            ),
+            _value_type: PhantomData,
+        },
+    ),
+    metadata: Metadata {
+        map: {},
+    },
+    roles: {},
+}
Account {
+    id: Id {
+        name: "white_rabbit",
+        domain_name: Id {
+            name: "looking_glass",
+        },
+    },
+    assets: {},
+    signatories: {
+        { digest: "ed25519", payload: "A4C4DADD9F18B0F63D6A420151FE0748D785475DEC63034A15FCF999CEDA1E65",
+        },
+    },
+    permission_tokens: {},
+    signature_check_condition: SignatureCheckCondition(
+        EvaluatesTo {
+            expression: ContainsAny(
+                ContainsAny {
+                    collection: EvaluatesTo {
+                        expression: ContextValue(
+                            ContextValue {
+                                value_name: "transaction_signatories",
+                            },
+                        ),
+                        _value_type: PhantomData,
+                    },
+                    elements: EvaluatesTo {
+                        expression: ContextValue(
+                            ContextValue {
+                                value_name: "account_signatories",
+                            },
+                        ),
+                        _value_type: PhantomData,
+                    },
+                },
+            ),
+            _value_type: PhantomData,
+        },
+    ),
+    metadata: Metadata {
+        map: {},
+    },
+    roles: {},
+}

Now that the network and users are registered, it is possible to mint assets.

5. Registering and minting assets

In order to mint assets, you need to register the asset first. We are going to register the tea token within the looking_glass network. To do that, run:

bash
$ ./iroha_client_cli asset register \\
+    --id="tea#looking_glass" \\
+    --value-type=Quantity
$ ./iroha_client_cli asset register \\
+    --id="tea#looking_glass" \\
+    --value-type=Quantity

The tea asset is now registered within the looking_glass network. The output within the CLI is the same as with other commands, you will be able to see that there are new events in the pipeline.

With the asset created, you can now mint tokens. Run:

bash
$ ./iroha_client_cli asset mint \\
+    --account="mad_hatter@looking_glass" \\
+    --asset="tea#looking_glass" \\
+    --quantity="100"
$ ./iroha_client_cli asset mint \\
+    --account="mad_hatter@looking_glass" \\
+    --asset="tea#looking_glass" \\
+    --quantity="100"

After minting one hundred tea, you will see more pipeline events in the logger, and you can also query the assets that you have just minted:

bash
$ ./iroha_client_cli asset list all
$ ./iroha_client_cli asset list all

After running this command, you will be able to see the tokens currently available on the network:

rust
[
+    Asset {
+        id: Id {
+            definition_id: DefinitionId {
+                name: "tea",
+                domain_name: "looking_glass",
+            },
+            account_id: Id {
+                name: "mad_hatter",
+                domain_name: "looking_glass",
+            },
+        },
+        value: Quantity(
+            100,
+        ),
+    },
+    Asset {
+        id: Id {
+            definition_id: DefinitionId {
+                name: "rose",
+                domain_name: "wonderland",
+            },
+            account_id: Id {
+                name: "alice",
+                domain_name: "wonderland",
+            },
+        },
+        value: Quantity(
+            13,
+        ),
+    },
+]
[
+    Asset {
+        id: Id {
+            definition_id: DefinitionId {
+                name: "tea",
+                domain_name: "looking_glass",
+            },
+            account_id: Id {
+                name: "mad_hatter",
+                domain_name: "looking_glass",
+            },
+        },
+        value: Quantity(
+            100,
+        ),
+    },
+    Asset {
+        id: Id {
+            definition_id: DefinitionId {
+                name: "rose",
+                domain_name: "wonderland",
+            },
+            account_id: Id {
+                name: "alice",
+                domain_name: "wonderland",
+            },
+        },
+        value: Quantity(
+            13,
+        ),
+    },
+]

INFO

Iroha 2 currently doesn't validate the account names, so you could (in theory) add invalid characters to the name, e.g. spaces. We recommend sticking to English alphanumeric characters and underscores.

6. Transferring assets

After minting the assets, you can transfer some of Mad Hatter's tea to White Rabbit:

bash
$ ./iroha_client_cli asset transfer --from mad_hatter@looking_glass --to white_rabbit@looking_glass --asset-id tea#looking_glass --quantity 5
$ ./iroha_client_cli asset transfer --from mad_hatter@looking_glass --to white_rabbit@looking_glass --asset-id tea#looking_glass --quantity 5

7. Burning assets

Burning assets is quite similar to minting them:

bash
$ ./iroha_client_cli asset burn \\
+    --account="mad_hatter@looking_glass" \\
+    --asset="tea#looking_glass" \\
+    --quantity="10"
$ ./iroha_client_cli asset burn \\
+    --account="mad_hatter@looking_glass" \\
+    --asset="tea#looking_glass" \\
+    --quantity="10"

8. Visualizing outputs

Although you will get a constant data feed of the network within the terminal running docker compose, you can also configure an output to listen to events on the network.

From a terminal tab/window run:

bash
$ ./iroha_client_cli events pipeline
$ ./iroha_client_cli events pipeline

This view will output all the events related to Iroha 2, such as transactions, block validations, or data events (e.g. when the in-memory representation of the blockchain gets committed to the hard disk).

The output would look like this:

rust
Iroha Client CLI: build v0.0.1 [release]
+User: alice@wonderland
+{"PUBLIC_KEY":"ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0","PRIVATE_KEY":{"digest_function":"ed25519","payload":"9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"},"ACCOUNT_ID":{"name":"alice","domain_name":"wonderland"},"TORII_API_URL":"http://127.0.0.1:8080","TORII_STATUS_URL":"127.0.0.1:8180","TRANSACTION_TIME_TO_LIVE_MS":100000,"TRANSACTION_STATUS_TIMEOUT_MS":10000,"MAX_INSTRUCTION_NUMBER":4096,"ADD_TRANSACTION_NONCE":false,"LOGGER_CONFIGURATION":{"MAX_LOG_LEVEL":"INFO","TELEMETRY_CAPACITY":1000,"COMPACT_MODE":false,"LOG_FILE_PATH":null}}
+Listening to events with filter: Pipeline(EventFilter { entity: None, hash: None })
+Pipeline(
+    Event {
+        entity_type: Transaction,
+        status: Validating,
+        hash: 10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77,
+    },
+)
+Pipeline(
+    Event {
+        entity_type: Block,
+        status: Validating,
+        hash: 944269f27e1ed8882c6c8c74bd641bc3551ef5651320f4e1e1be11a470b4e3c3,
+    },
+)
+Pipeline(
+    Event {
+        entity_type: Transaction,
+        status: Committed,
+        hash: 10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77,
+    },
+)
Iroha Client CLI: build v0.0.1 [release]
+User: alice@wonderland
+{"PUBLIC_KEY":"ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0","PRIVATE_KEY":{"digest_function":"ed25519","payload":"9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"},"ACCOUNT_ID":{"name":"alice","domain_name":"wonderland"},"TORII_API_URL":"http://127.0.0.1:8080","TORII_STATUS_URL":"127.0.0.1:8180","TRANSACTION_TIME_TO_LIVE_MS":100000,"TRANSACTION_STATUS_TIMEOUT_MS":10000,"MAX_INSTRUCTION_NUMBER":4096,"ADD_TRANSACTION_NONCE":false,"LOGGER_CONFIGURATION":{"MAX_LOG_LEVEL":"INFO","TELEMETRY_CAPACITY":1000,"COMPACT_MODE":false,"LOG_FILE_PATH":null}}
+Listening to events with filter: Pipeline(EventFilter { entity: None, hash: None })
+Pipeline(
+    Event {
+        entity_type: Transaction,
+        status: Validating,
+        hash: 10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77,
+    },
+)
+Pipeline(
+    Event {
+        entity_type: Block,
+        status: Validating,
+        hash: 944269f27e1ed8882c6c8c74bd641bc3551ef5651320f4e1e1be11a470b4e3c3,
+    },
+)
+Pipeline(
+    Event {
+        entity_type: Transaction,
+        status: Committed,
+        hash: 10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77,
+    },
+)
`,100),e=[p];function t(c,r,E,i,y,d){return a(),n("div",null,e)}const h=s(o,[["render",t]]);export{F as __pageData,h as default}; diff --git a/assets/guide_get-started_bash.md.b307a61c.lean.js b/assets/guide_get-started_bash.md.b307a61c.lean.js new file mode 100644 index 000000000..6bc704bf0 --- /dev/null +++ b/assets/guide_get-started_bash.md.b307a61c.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.1293becd.js";const F=JSON.parse('{"title":"Bash Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/bash.md","filePath":"guide/get-started/bash.md","lastUpdated":1700563625000}'),o={name:"guide/get-started/bash.md"},p=l("",100),e=[p];function t(c,r,E,i,y,d){return a(),n("div",null,e)}const h=s(o,[["render",t]]);export{F as __pageData,h as default}; diff --git a/assets/guide_get-started_build.md.a6d60f6a.js b/assets/guide_get-started_build.md.a6d60f6a.js new file mode 100644 index 000000000..8ddc9eeaf --- /dev/null +++ b/assets/guide_get-started_build.md.a6d60f6a.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as o,Q as t}from"./chunks/framework.1293becd.js";const E=JSON.parse('{"title":"Build Iroha 2 Client","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/build.md","filePath":"guide/get-started/build.md","lastUpdated":1700563625000}'),e={name:"guide/get-started/build.md"},l=t('

Build Iroha 2 Client

After you have installed Iroha from GitHub, follow these instructions to build the Iroha 2 client:

  1. Install the Rust Toolchain
  2. Build Iroha 2 Client

Install the Rust Toolchain

You need a working Rust toolchain: cargo, rustc v1.60 and up. [1]

Installing the Rust Toolchain is normally a straightforward process, but we've added troubleshooting details for each stage, in case you experience issues with the installation process.

The easiest way to get the official rustup script is to run:

bash
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Or, alternatively, you can install rustup via your operating system’s package manager.

TIP

If you know what you're doing, you can also install the Rust toolchain directly, without rustup.

If you chose to use the one-line curl script, you will be guided through the setup process. Just go with the defaults.

Build Iroha Client

  1. Navigate to the directory containing the Iroha repository. If you followed the installation instructions here, run:

    bash
    $ cd ~/Git/iroha
    $ cd ~/Git/iroha
  2. Build the Iroha 2 client using:

    bash
    $ cargo build -p iroha_client_cli --release
    $ cargo build -p iroha_client_cli --release

    Build artifacts are created in the ./target/debug/ directory.

    INFO

    We take pride in the fact that Iroha is extremely quick to compile. For reference, compiling hyperledger/substrate takes a good part of ten minutes on a modern M1 machine. Iroha, in comparison, compiles in around one minute.


  1. If you're having issues installing Rust compatible with our code (2021 edition), please consult the troubleshooting section. ↩︎

',15),n=[l];function p(r,i,c,h,d,u){return a(),o("div",null,n)}const g=s(e,[["render",p]]);export{E as __pageData,g as default}; diff --git a/assets/guide_get-started_build.md.a6d60f6a.lean.js b/assets/guide_get-started_build.md.a6d60f6a.lean.js new file mode 100644 index 000000000..8ba6ea6da --- /dev/null +++ b/assets/guide_get-started_build.md.a6d60f6a.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as o,Q as t}from"./chunks/framework.1293becd.js";const E=JSON.parse('{"title":"Build Iroha 2 Client","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/build.md","filePath":"guide/get-started/build.md","lastUpdated":1700563625000}'),e={name:"guide/get-started/build.md"},l=t("",15),n=[l];function p(r,i,c,h,d,u){return a(),o("div",null,n)}const g=s(e,[["render",p]]);export{E as __pageData,g as default}; diff --git a/assets/guide_get-started_index.md.0460923f.js b/assets/guide_get-started_index.md.0460923f.js new file mode 100644 index 000000000..9503802a6 --- /dev/null +++ b/assets/guide_get-started_index.md.0460923f.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as t,Q as i}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Get Started","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/index.md","filePath":"guide/get-started/index.md","lastUpdated":1700563625000}'),r={name:"guide/get-started/index.md"},o=i('

Get Started

  1. Install Iroha 2.
  2. Build Iroha 2
  3. Follow one of the language-specific guides to learn how to set up and configure Iroha 2, register a domain and an account, register and mind assets, and visualize outputs:

If you have previously worked with Iroha, start with our comparison of Iroha 1 and Iroha 2. That will help you understand the differences between the two versions and upgrade to the newer one.

Before you dive into tutorials and deploy Iroha 2, we suggest you read through the Security section to learn about core security principles and operational security measures that are necessary to ensure the safety and validity of data and assets. This section also covers cryptographic keys, how to generate them, and how to store them securely.

Check the tutorial where you can follow one of the available language-specific guides in Bash, Rust, Kotlin, Javascript, or Python. The guides introduce you to the basic concepts and provide code snippets that you can run yourself.

In the Blockchain chapter you can find documentation for Iroha features, such as Iroha Special Instructions, triggers, queries.

The Configuration and Management section explains Iroha 2 configuration files in great detail and covers topics such as genesis blocks and accounts, client configuration, and public and private modes.

',7),s=[o];function h(d,c,n,l,u,g){return a(),t("div",null,s)}const m=e(r,[["render",h]]);export{f as __pageData,m as default}; diff --git a/assets/guide_get-started_index.md.0460923f.lean.js b/assets/guide_get-started_index.md.0460923f.lean.js new file mode 100644 index 000000000..b09148260 --- /dev/null +++ b/assets/guide_get-started_index.md.0460923f.lean.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as t,Q as i}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Get Started","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/index.md","filePath":"guide/get-started/index.md","lastUpdated":1700563625000}'),r={name:"guide/get-started/index.md"},o=i("",7),s=[o];function h(d,c,n,l,u,g){return a(),t("div",null,s)}const m=e(r,[["render",h]]);export{f as __pageData,m as default}; diff --git a/assets/guide_get-started_install.md.471062b8.js b/assets/guide_get-started_install.md.471062b8.js new file mode 100644 index 000000000..8a8470ce9 --- /dev/null +++ b/assets/guide_get-started_install.md.471062b8.js @@ -0,0 +1 @@ +import{_ as s,o as e,c as a,Q as o}from"./chunks/framework.1293becd.js";const b=JSON.parse('{"title":"Install Iroha 2","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/install.md","filePath":"guide/get-started/install.md","lastUpdated":1700563625000}'),t={name:"guide/get-started/install.md"},l=o('

Install Iroha 2

To install Iroha, follow these instructions:

  1. Choose the Iroha 2 version to work with
  2. Install prerequisites
  3. Install Iroha from GitHub

Choose Version

You can choose to work with one of the following versions of Iroha: dev, lts, or stable:

VersionDescription
iroha2-devThis is the latest state of Iroha and it is not meant to be used in production. This is an intermediate, untested, potentially broken state, and therefore we cannot guarantee that it is usable or stable.
iroha2-stableThis is the latest released version of Iroha. Stable versions are tested and released once a month, in accordance with our release schedule. You can use the stable version in production, and we will offer you tech support.
iroha2-ltsThis is the long-term supported version. We guarantee its compatibility with SDKs, toolsets, and block stores released when it came out. We recommend using the LTS version in production since it is the version that will not change much over time. Similarly to the stable version, we offer tech support for LTS.

Install Prerequisites

To install Iroha from GitHub, you need:

Install OpenSSL

Make sure you have OpenSSL installed. Note that in most Linux setups it is already available to you.

  • Install OpenSSL on Ubuntu:

    bash
    $ sudo apt-get install libssl-dev
    $ sudo apt-get install libssl-dev
  • Install OpenSSL on macOS using brew:

    bash
    $ brew install openssl
    $ brew install openssl

Check the OpenSSL installation guide for details.

Install Iroha from GitHub

  1. If you haven’t already, you might want to create a clean folder for Iroha 2, to keep things tidy.

    bash
    $ mkdir -p ~/Git
    $ mkdir -p ~/Git

    TIP

    On macOS, if you get fatal: could not create work tree dir 'iroha': Read-only file system, that’s because the home folder is not a real file system. The fix is to create the Git folder.

  2. Enter the directory you have just created using

    bash
    $ cd ~/Git
    $ cd ~/Git
  3. Then clone the Iroha git repository into the folder ~/Git/iroha and checkout the branch you prefer to work on. You can use the iroha2-lts branch, which is the long-term support release, or the latest stable release branch (iroha2-stable). To clone the repository and checkout the stable release, run:

    bash
    $ git clone https://github.com/hyperledger/iroha.git --branch iroha2-stable
    $ git clone https://github.com/hyperledger/iroha.git --branch iroha2-stable

    This will fetch all of Iroha, including Iroha 1, and the iroha2-dev branch, which we will touch upon later.

What's next

',17),n=[l];function r(p,i,c,h,d,u){return e(),a("div",null,n)}const E=s(t,[["render",r]]);export{b as __pageData,E as default}; diff --git a/assets/guide_get-started_install.md.471062b8.lean.js b/assets/guide_get-started_install.md.471062b8.lean.js new file mode 100644 index 000000000..e59cdc681 --- /dev/null +++ b/assets/guide_get-started_install.md.471062b8.lean.js @@ -0,0 +1 @@ +import{_ as s,o as e,c as a,Q as o}from"./chunks/framework.1293becd.js";const b=JSON.parse('{"title":"Install Iroha 2","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/install.md","filePath":"guide/get-started/install.md","lastUpdated":1700563625000}'),t={name:"guide/get-started/install.md"},l=o("",17),n=[l];function r(p,i,c,h,d,u){return e(),a("div",null,n)}const E=s(t,[["render",r]]);export{b as __pageData,E as default}; diff --git a/assets/guide_get-started_javascript.md.274c1036.js b/assets/guide_get-started_javascript.md.274c1036.js new file mode 100644 index 000000000..909b35012 --- /dev/null +++ b/assets/guide_get-started_javascript.md.274c1036.js @@ -0,0 +1,1363 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.1293becd.js";const p="/iroha-2-docs/assets/sample-vue-app.4caffed6.gif",h=JSON.parse('{"title":"JavaScript/TypeScript Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/javascript.md","filePath":"guide/get-started/javascript.md","lastUpdated":1700563625000}'),o={name:"guide/get-started/javascript.md"},e=l(`

JavaScript/TypeScript Guide

INFO

This guide targets @iroha2/client and @iroha/data-model version ^5.0, which targets Iroha 2 stable (2.0.0-pre-rc.13, c4af68c4f7959b154eb5380aa93c894e2e63fe4e).

INFO

This guide assumes you are familiar with Node.js and NPM ecosystem.

1. Client Installation

The Iroha 2 JavaScript library consists of multiple packages:

PackageDescription
clientSubmits requests to Iroha Peer
data-modelProvides SCALE (Simple Concatenated Aggregate Little-Endian)-codecs for the Iroha 2 Data Model
crypto-coreContains cryptography types
crypto-target-nodeProvides compiled crypto WASM (Web Assembly) for the Node.js environment
crypto-target-webProvides compiled crypto WASM for native Web (ESM)
crypto-target-bundlerProvides compiled crypto WASM to use with bundlers such as Webpack

All of these are published under the @iroha2 scope into Iroha Nexus Registry. In the future, they will be published in the main NPM Registry.

INFO

You can also work with the sources in Iroha Javascript repository, where the active development is happening. Clone the repository and check out the iroha2 branch:

bash
$ git clone https://github.com/hyperledger/iroha-javascript.git --branch iroha2
$ git clone https://github.com/hyperledger/iroha-javascript.git --branch iroha2

Please note that this guide does not cover the details of this workflow.

While we've taken great care to decouple the packages, so you could minimise their footprint, for the purposes of this tutorial, it's better to install everything.

The installation consists of two steps: setting up a registry and then installing the packages you need.

  1. Set up a registry. In shell, run:

    bash
    $ echo "@iroha2:registry=https://nexus.iroha.tech/repository/npm-group/" > .npmrc
    $ echo "@iroha2:registry=https://nexus.iroha.tech/repository/npm-group/" > .npmrc
  2. Install Iroha 2 packages as any other NPM package. If you are following the tutorial, we recommend installing all of the following:

    bash
    $ npm i @iroha2/client
    +$ npm i @iroha2/data-model
    +$ npm i @iroha2/crypto-core
    +$ npm i @iroha2/crypto-target-node
    +$ npm i @iroha2/crypto-target-web
    +$ npm i @iroha2/crypto-target-bundler
    $ npm i @iroha2/client
    +$ npm i @iroha2/data-model
    +$ npm i @iroha2/crypto-core
    +$ npm i @iroha2/crypto-target-node
    +$ npm i @iroha2/crypto-target-web
    +$ npm i @iroha2/crypto-target-bundler

    INFO

    Note that you can use other package managers, such as yarn or pnpm, for a faster installation. For example:

    bash
    $ yarn add @iroha2/data-model
    +$ pnpm add @iroha2/crypto-target-web
    $ yarn add @iroha2/data-model
    +$ pnpm add @iroha2/crypto-target-web

    The set of packages that you need to install depends on what you are trying to achieve. If you only need to play with the Data Model to perform (de-)serialisation, the data-model package is sufficient. If you need to check on a peer in terms of its status or health, then you only need the client library.

  3. Install the following packages as well:

    bash
    $ npm i hada
    +$ npm i tsx -g
    $ npm i hada
    +$ npm i tsx -g
  4. If you are planning to use the Transaction or Query API, you'll also need to inject an appropriate crypto instance into the client at runtime. This has to be adjusted according to your particular environment.

    For example, Node.js users need the following:

    ts
    import { crypto } from '@iroha2/crypto-target-node'
    +import { setCrypto } from '@iroha2/client'
    +
    +setCrypto(crypto)
    import { crypto } from '@iroha2/crypto-target-node'
    +import { setCrypto } from '@iroha2/client'
    +
    +setCrypto(crypto)

    INFO

    Please refer to the documentation of the respective @iroha2/crypto-target-* package, because each case has specific configuration steps. For example, the web target needs to be initialised (via asynchronous init()) before you can use any cryptographic methods.

NOTE

When you are creating files in the following steps, you must place them in the same directory that contains node_modules, like so:

╭───┬───────────────────┬──────╮
+│ # │       name        │ type │
+├───┼───────────────────┼──────┤
+│ 0 │ node_modules      │ dir  │
+│ 1 │ addClient.ts      │ file │
+│ 2 │ example.ts        │ file │
+│ 3 │ package.json      │ file │
+│ 4 │ pnpm-lock.yaml    │ file │
+│ 5 │ registerDomain.ts │ file │
+╰───┴───────────────────┴──────╯
╭───┬───────────────────┬──────╮
+│ # │       name        │ type │
+├───┼───────────────────┼──────┤
+│ 0 │ node_modules      │ dir  │
+│ 1 │ addClient.ts      │ file │
+│ 2 │ example.ts        │ file │
+│ 3 │ package.json      │ file │
+│ 4 │ pnpm-lock.yaml    │ file │
+│ 5 │ registerDomain.ts │ file │
+╰───┴───────────────────┴──────╯

We recommend using tsx to run the scripts you've created. For example:

bash
$ tsx example.ts
$ tsx example.ts

2. Client Configuration

The JavaScript Client is fairly low-level in a sense that it doesn't expose any convenience features like a TransactionBuilder or a ConfigBuilder.

INFO

The work on implementing those is underway, and these features will very likely be available in the second round of this tutorial's release.

Thus, on the plus side, configuration of the client is simple. On the downside, you have to prepare a lot manually.

You may need to use transactions or queries, so before we initialize the client, let's set up this part. Let's assume that you have stringified public & private keys (more on that later). Thus, a key-pair generation could look like this:

ts
import { crypto } from '@iroha2/crypto-target-node'
+
+const keyPair = crypto.KeyPair.fromJSON({
+  public_key: 'ed0120e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3',
+  private_key: {
+    digest_function: 'ed25519',
+    payload:
+      'de757bcb79f4c63e8fa0795edc26f86dfdba189b846e903d0b732bb644607720e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3',
+  },
+})
import { crypto } from '@iroha2/crypto-target-node'
+
+const keyPair = crypto.KeyPair.fromJSON({
+  public_key: 'ed0120e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3',
+  private_key: {
+    digest_function: 'ed25519',
+    payload:
+      'de757bcb79f4c63e8fa0795edc26f86dfdba189b846e903d0b732bb644607720e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3',
+  },
+})

When you have a key pair, you might create a Signer using the key pair:

ts
import { cryptoTypes } from '@iroha2/crypto-core'
+import { Signer } from '@iroha2/client'
+import { AccountId, DomainId } from '@iroha2/data-model'
+
+// Key pair from previous step
+declare const keyPair: cryptoTypes.KeyPair
+
+const accountId = AccountId({
+  // Account name
+  name: 'alice',
+  // The domain where this account is registered
+  domain_id: DomainId({
+    name: 'wonderland',
+  }),
+})
+
+const signer = new Signer(accountId, keyPair)
import { cryptoTypes } from '@iroha2/crypto-core'
+import { Signer } from '@iroha2/client'
+import { AccountId, DomainId } from '@iroha2/data-model'
+
+// Key pair from previous step
+declare const keyPair: cryptoTypes.KeyPair
+
+const accountId = AccountId({
+  // Account name
+  name: 'alice',
+  // The domain where this account is registered
+  domain_id: DomainId({
+    name: 'wonderland',
+  }),
+})
+
+const signer = new Signer(accountId, keyPair)

Now we're able to make signatures with signer.sign(binary)! However, to interact with Iroha, we need to be able to do more than just sign. We would need to send something to Iroha, like transactions or queries. Torii will help us with that.

Torii handles HTTP / WebSocket communications with Iroha. We will use it to communicate with Iroha endpoints. With the help of Torii we can:

  • Submit transactions with Torii.submit()
  • Send queries with Torii.request()
  • Listen for events with Torii.listenForEvents()
  • Listen for blocks stream with Torii.listenForBlocksStream()
  • and so on

Torii is a stateless object, a compendium of methods. You can look at it as if it is a class with only static methods. Each method has its own requirements to be passed in — some of them only need an HTTP transport and Iroha Torii Telemetry URL, others — a WebSocket transport and Iroha Torii API URL. To better understand how Torii is used, look at this example:

ts
import { Torii } from '@iroha2/client'
+import { VersionedSignedQueryRequest } from '@iroha2/data-model'
+
+// --snip--
+declare const query: VersionedSignedQueryRequest
+
+const result = await Torii.request(
+  {
+    fetch,
+    apiURL: 'http://127.0.0.1:8080',
+  },
+  query,
+)
import { Torii } from '@iroha2/client'
+import { VersionedSignedQueryRequest } from '@iroha2/data-model'
+
+// --snip--
+declare const query: VersionedSignedQueryRequest
+
+const result = await Torii.request(
+  {
+    fetch,
+    apiURL: 'http://127.0.0.1:8080',
+  },
+  query,
+)

In this example, we pass fetch (the HTTP transport) and apiURL as the first parameter, and the query itself as the second.

To work with Torii, we need to know Iroha Torii URLs. Our Iroha Peer is configured to listen for API endpoints at http://127.0.0.1:8080 and for telemetry endpoints at http://127.0.0.1:8081. Then, we need to provide appropriate HTTP / WebSocket adapters which Torii will use[1]. These adapters depend on the environment in which you are going to use @iroha2/client.

In Node.js, the full list of Torii requirements (i.e. covering all its methods) will look like this:

ts
import {
+  ToriiRequirementsForApiHttp,
+  ToriiRequirementsForApiWebSocket,
+  ToriiRequirementsForTelemetry,
+} from '@iroha2/client'
+import { adapter as WS } from '@iroha2/client/web-socket/node'
+
+import nodeFetch from 'node-fetch'
+// another alternative
+import { fetch as undiciFetch } from 'undici'
+
+const toriiRequirements: ToriiRequirementsForApiHttp &
+  ToriiRequirementsForApiWebSocket &
+  ToriiRequirementsForTelemetry = {
+  apiURL: 'http://127.0.0.1:8080',
+  telemetryURL: 'http://127.0.0.1:8081',
+  ws: WS,
+  // type assertion is acceptable here
+  // you can pass \`undiciFetch\` here as well
+  fetch: nodeFetch as typeof fetch,
+}
import {
+  ToriiRequirementsForApiHttp,
+  ToriiRequirementsForApiWebSocket,
+  ToriiRequirementsForTelemetry,
+} from '@iroha2/client'
+import { adapter as WS } from '@iroha2/client/web-socket/node'
+
+import nodeFetch from 'node-fetch'
+// another alternative
+import { fetch as undiciFetch } from 'undici'
+
+const toriiRequirements: ToriiRequirementsForApiHttp &
+  ToriiRequirementsForApiWebSocket &
+  ToriiRequirementsForTelemetry = {
+  apiURL: 'http://127.0.0.1:8080',
+  telemetryURL: 'http://127.0.0.1:8081',
+  ws: WS,
+  // type assertion is acceptable here
+  // you can pass \`undiciFetch\` here as well
+  fetch: nodeFetch as typeof fetch,
+}

TIP

In the example above, we use node-fetch package which implements Fetch API in Node.js. However, you can use undici as well.

INFO

fetch: nodeFetch as typeof fetch type assertion is acceptable here for a reason. Torii expects the "classic", native fetch function, which is available natively in Browser. However, both node-fetch and undici don't provide fetch that is 100% compatible with the native one. Since Torii doesn't rely on those corner-features that are partially provided by node-fetch and undici, it's fine to ignore the TypeScript error here.

And here is a sample of full Torii in-Browser requirements:

ts
import {
+  ToriiRequirementsForApiHttp,
+  ToriiRequirementsForApiWebSocket,
+  ToriiRequirementsForTelemetry,
+} from '@iroha2/client'
+import { adapter as WS } from '@iroha2/client/web-socket/native'
+
+const toriiRequirements: ToriiRequirementsForApiHttp &
+  ToriiRequirementsForApiWebSocket &
+  ToriiRequirementsForTelemetry = {
+  apiURL: 'http://127.0.0.1:8080',
+  telemetryURL: 'http://127.0.0.1:8081',
+  ws: WS,
+  fetch:
+    // passing globally available \`fetch\`, but binding it to \`window\`
+    // to avoid \`TypeError: "'fetch' called on an
+    //           object that does not implement interface Window."\`
+    fetch.bind(window),
+}
import {
+  ToriiRequirementsForApiHttp,
+  ToriiRequirementsForApiWebSocket,
+  ToriiRequirementsForTelemetry,
+} from '@iroha2/client'
+import { adapter as WS } from '@iroha2/client/web-socket/native'
+
+const toriiRequirements: ToriiRequirementsForApiHttp &
+  ToriiRequirementsForApiWebSocket &
+  ToriiRequirementsForTelemetry = {
+  apiURL: 'http://127.0.0.1:8080',
+  telemetryURL: 'http://127.0.0.1:8081',
+  ws: WS,
+  fetch:
+    // passing globally available \`fetch\`, but binding it to \`window\`
+    // to avoid \`TypeError: "'fetch' called on an
+    //           object that does not implement interface Window."\`
+    fetch.bind(window),
+}

NOTE

We make fetch.bind(window) to avoid TypeError: "'fetch' called on an object that does not implement interface Window.".

Great! Now we have signer and Torii requirements to work with. Finally, we can create a Client:

ts
import { Client, Signer, ToriiRequirementsForApiHttp } from '@iroha2/client'
+import { Executable } from '@iroha2/data-model'
+
+// --snip--
+declare const signer: Signer
+declare const toriiRequirements: ToriiRequirementsForApiHttp
+
+const client = new Client({ signer })
+
+// \`Client\` will sign & wrap \`Executable\` into \`VersionedSignedTransaction\`
+declare const exec: Executable
+await client.submitExecutable(toriiRequirements, exec)
import { Client, Signer, ToriiRequirementsForApiHttp } from '@iroha2/client'
+import { Executable } from '@iroha2/data-model'
+
+// --snip--
+declare const signer: Signer
+declare const toriiRequirements: ToriiRequirementsForApiHttp
+
+const client = new Client({ signer })
+
+// \`Client\` will sign & wrap \`Executable\` into \`VersionedSignedTransaction\`
+declare const exec: Executable
+await client.submitExecutable(toriiRequirements, exec)

Client provides useful utilities for transactions and queries. You can also use Torii to communicate with the endpoints directly. Signer is accessible with client.signer.

3. Registering a Domain

Here we see how similar the JavaScript code is to the Rust counterpart. It should be emphasised that the JavaScript library is a thin wrapper: It doesn't provide any special builder structures, meaning you have to work with bare-bones compiled Data Model structures and define all internal fields explicitly.

Doubly so, since JavaScript employs many implicit conversions, we highly recommend that you employ TypeScript. This makes many errors far easier to debug, but, unfortunately, results in more boilerplates.

Let's register a new domain named looking_glass using our current account, alice@wondeland.

First, we need to import necessary models and a pre-configured client instance:

ts
import { Client, ToriiRequirementsForApiHttp } from '@iroha2/client'
+import {
+  DomainId,
+  EvaluatesToRegistrableBox,
+  Executable,
+  Expression,
+  IdentifiableBox,
+  Instruction,
+  MapNameValue,
+  Metadata,
+  NewDomain,
+  OptionIpfsPath,
+  QueryBox,
+  RegisterBox,
+  Value,
+  VecInstruction,
+} from '@iroha2/data-model'
+
+// --snip--
+declare const client: Client
+declare const toriiRequirements: ToriiRequirementsForApiHttp
import { Client, ToriiRequirementsForApiHttp } from '@iroha2/client'
+import {
+  DomainId,
+  EvaluatesToRegistrableBox,
+  Executable,
+  Expression,
+  IdentifiableBox,
+  Instruction,
+  MapNameValue,
+  Metadata,
+  NewDomain,
+  OptionIpfsPath,
+  QueryBox,
+  RegisterBox,
+  Value,
+  VecInstruction,
+} from '@iroha2/data-model'
+
+// --snip--
+declare const client: Client
+declare const toriiRequirements: ToriiRequirementsForApiHttp

To register a new domain, we need to submit a transaction with a single instruction: to register a new domain. Let's wrap it all in an async function:

ts
async function registerDomain(domainName: string) {
+  const registerBox = RegisterBox({
+    object: EvaluatesToRegistrableBox({
+      expression: Expression(
+        'Raw',
+        Value(
+          'Identifiable',
+          IdentifiableBox(
+            'NewDomain',
+            NewDomain({
+              id: DomainId({
+                name: domainName, 
+              }),
+              metadata: Metadata({ map: MapNameValue(new Map()) }),
+              logo: OptionIpfsPath('None'),
+            }),
+          ),
+        ),
+      ),
+    }),
+  })
+
+  await client.submitExecutable(
+    toriiRequirements,
+    Executable('Instructions', VecInstruction([Instruction('Register', registerBox)])),
+  )
+}
async function registerDomain(domainName: string) {
+  const registerBox = RegisterBox({
+    object: EvaluatesToRegistrableBox({
+      expression: Expression(
+        'Raw',
+        Value(
+          'Identifiable',
+          IdentifiableBox(
+            'NewDomain',
+            NewDomain({
+              id: DomainId({
+                name: domainName, 
+              }),
+              metadata: Metadata({ map: MapNameValue(new Map()) }),
+              logo: OptionIpfsPath('None'),
+            }),
+          ),
+        ),
+      ),
+    }),
+  })
+
+  await client.submitExecutable(
+    toriiRequirements,
+    Executable('Instructions', VecInstruction([Instruction('Register', registerBox)])),
+  )
+}

Which we use to register the domain like so:

ts
await registerDomain('looking_glass')
await registerDomain('looking_glass')

We can also use Query API to ensure that the new domain is created. Let's create another function that wraps that functionality:

ts
async function ensureDomainExistence(domainName: string) {
+  // Query all domains
+  const result = await client.requestWithQueryBox(
+    toriiRequirements,
+    QueryBox('FindAllDomains', null),
+  )
+
+  // Display the request status
+  console.log('%o', result)
+
+  // Obtain the domain
+  const domain = result
+    .as('Ok')
+    .result.enum.as('Vec')
+    .map((x) => x.enum.as('Identifiable').enum.as('Domain'))
+    .find((x) => x.id.name === domainName) 
+
+  // Throw an error if the domain is unavailable
+  if (!domain) throw new Error('Not found')
+}
async function ensureDomainExistence(domainName: string) {
+  // Query all domains
+  const result = await client.requestWithQueryBox(
+    toriiRequirements,
+    QueryBox('FindAllDomains', null),
+  )
+
+  // Display the request status
+  console.log('%o', result)
+
+  // Obtain the domain
+  const domain = result
+    .as('Ok')
+    .result.enum.as('Vec')
+    .map((x) => x.enum.as('Identifiable').enum.as('Domain'))
+    .find((x) => x.id.name === domainName) 
+
+  // Throw an error if the domain is unavailable
+  if (!domain) throw new Error('Not found')
+}

Now you can ensure that domain is created by calling:

ts
await ensureDomainExistence('looking_glass')
await ensureDomainExistence('looking_glass')

4. Registering an Account

Registering an account is a bit more involved than registering a domain. With a domain, the only concern is the domain name. However, with an account, there are a few more things to worry about.

First of all, we need to create an AccountId. Note that we can only register an account to an existing domain. The best UX design practices dictate that you should check if the requested domain exists now, and if it doesn't, suggest a fix to the user. After that, we can create a new account named white_rabbit.

Imports we need:

ts
import {
+  AccountId,
+  DomainId,
+  EvaluatesToRegistrableBox,
+  Expression,
+  IdentifiableBox,
+  Instruction,
+  MapNameValue,
+  Metadata,
+  NewAccount,
+  PublicKey,
+  RegisterBox,
+  Value,
+  VecPublicKey,
+} from '@iroha2/data-model'
import {
+  AccountId,
+  DomainId,
+  EvaluatesToRegistrableBox,
+  Expression,
+  IdentifiableBox,
+  Instruction,
+  MapNameValue,
+  Metadata,
+  NewAccount,
+  PublicKey,
+  RegisterBox,
+  Value,
+  VecPublicKey,
+} from '@iroha2/data-model'

The AccountId structure:

ts
const accountId = AccountId({
+  name: 'white_rabbit',
+  domain_id: DomainId({
+    name: 'looking_glass',
+  }),
+})
const accountId = AccountId({
+  name: 'white_rabbit',
+  domain_id: DomainId({
+    name: 'looking_glass',
+  }),
+})

Second, you should provide the account with a public key. It is tempting to generate both it and the private key at this time, but it isn't the brightest idea. Remember that the white_rabbit trusts you, alice@wonderland, to create an account for them in the domain looking_glass, but doesn't want you to have access to that account after creation.

If you gave white_rabbit a key that you generated yourself, how would they know if you don't have a copy of their private key? Instead, the best way is to ask white_rabbit to generate a new key-pair, and give you the public half of it.

ts
const pubKey = PublicKey({
+  payload: new Uint8Array([
+    /* put bytes here */
+  ]),
+  digest_function: 'some_digest',
+})
const pubKey = PublicKey({
+  payload: new Uint8Array([
+    /* put bytes here */
+  ]),
+  digest_function: 'some_digest',
+})

Only then do we build an instruction from it:

ts
const registerAccountInstruction = Instruction(
+  'Register',
+  RegisterBox({
+    object: EvaluatesToRegistrableBox({
+      expression: Expression(
+        'Raw',
+        Value(
+          'Identifiable',
+          IdentifiableBox(
+            'NewAccount',
+            NewAccount({
+              id: accountId, 
+              signatories: VecPublicKey([pubKey]),
+              metadata: Metadata({ map: MapNameValue(new Map()) }),
+            }),
+          ),
+        ),
+      ),
+    }),
+  }),
+)
const registerAccountInstruction = Instruction(
+  'Register',
+  RegisterBox({
+    object: EvaluatesToRegistrableBox({
+      expression: Expression(
+        'Raw',
+        Value(
+          'Identifiable',
+          IdentifiableBox(
+            'NewAccount',
+            NewAccount({
+              id: accountId, 
+              signatories: VecPublicKey([pubKey]),
+              metadata: Metadata({ map: MapNameValue(new Map()) }),
+            }),
+          ),
+        ),
+      ),
+    }),
+  }),
+)

Which is then wrapped in a transaction and submitted to the peer the same way as in the previous section when we registered a domain.

5. Registering and minting assets

Iroha has been built with few underlying assumptions about what the assets need to be in terms of their value type and characteristics (fungible or non-fungible, mintable or non-mintable).

In JS, you can create a new asset with the following construction:

ts
import {
+  AssetDefinition,
+  AssetDefinitionId,
+  AssetValueType,
+  DomainId,
+  EvaluatesToRegistrableBox,
+  Expression,
+  IdentifiableBox,
+  Instruction,
+  MapNameValue,
+  Metadata,
+  Mintable,
+  RegisterBox,
+  Value,
+} from '@iroha2/data-model'
+
+const time = AssetDefinition({
+  value_type: AssetValueType('Quantity'),
+  id: AssetDefinitionId({
+    name: 'time',
+    domain_id: DomainId({ name: 'looking_glass' }),
+  }),
+  metadata: Metadata({ map: MapNameValue(new Map()) }),
+  mintable: Mintable('Infinitely'), // If only we could mint more time.
+})
+
+const register = Instruction(
+  'Register',
+  RegisterBox({
+    object: EvaluatesToRegistrableBox({
+      expression: Expression(
+        'Raw',
+        Value('Identifiable', IdentifiableBox('AssetDefinition', time)), 
+      ),
+    }),
+  }),
+)
import {
+  AssetDefinition,
+  AssetDefinitionId,
+  AssetValueType,
+  DomainId,
+  EvaluatesToRegistrableBox,
+  Expression,
+  IdentifiableBox,
+  Instruction,
+  MapNameValue,
+  Metadata,
+  Mintable,
+  RegisterBox,
+  Value,
+} from '@iroha2/data-model'
+
+const time = AssetDefinition({
+  value_type: AssetValueType('Quantity'),
+  id: AssetDefinitionId({
+    name: 'time',
+    domain_id: DomainId({ name: 'looking_glass' }),
+  }),
+  metadata: Metadata({ map: MapNameValue(new Map()) }),
+  mintable: Mintable('Infinitely'), // If only we could mint more time.
+})
+
+const register = Instruction(
+  'Register',
+  RegisterBox({
+    object: EvaluatesToRegistrableBox({
+      expression: Expression(
+        'Raw',
+        Value('Identifiable', IdentifiableBox('AssetDefinition', time)), 
+      ),
+    }),
+  }),
+)

Pay attention to the fact that we have defined the asset as Mintable('Not'). What this means is that we cannot create more of time. The late bunny will always be late, because even the super-user of the blockchain cannot mint more of time than already exists in the genesis block.

This means that no matter how hard the white_rabbit tries, the time that he has is the time that was given to him at genesis. And since we haven't defined any time in the domain looking_glass at genesis and defined time in a non-mintable fashion afterwards, the white_rabbit is doomed to always be late.

If we had set mintable: Mintable('Infinitely') on our time asset, we could mint it:

ts
import {
+  AccountId,
+  AssetDefinitionId,
+  AssetId,
+  DomainId,
+  EvaluatesToIdBox,
+  EvaluatesToValue,
+  Expression,
+  IdBox,
+  Instruction,
+  MintBox,
+  NumericValue,
+  Value,
+} from '@iroha2/data-model'
+
+const mint = Instruction(
+  'Mint',
+  MintBox({
+    object: EvaluatesToValue({
+      expression: Expression('Raw', Value('Numeric', NumericValue('U32', 42))),
+    }),
+    destination_id: EvaluatesToIdBox({
+      expression: Expression(
+        'Raw',
+        Value(
+          'Id',
+          IdBox(
+            'AssetId',
+            AssetId({
+              account_id: AccountId({
+                name: 'alice',
+                domain_id: DomainId({
+                  name: 'wonderland',
+                }),
+              }),
+              definition_id: AssetDefinitionId({
+                name: 'time',
+                domain_id: DomainId({ name: 'looking_glass' }),
+              }),
+            }),
+          ),
+        ),
+      ),
+    }),
+  }),
+)
import {
+  AccountId,
+  AssetDefinitionId,
+  AssetId,
+  DomainId,
+  EvaluatesToIdBox,
+  EvaluatesToValue,
+  Expression,
+  IdBox,
+  Instruction,
+  MintBox,
+  NumericValue,
+  Value,
+} from '@iroha2/data-model'
+
+const mint = Instruction(
+  'Mint',
+  MintBox({
+    object: EvaluatesToValue({
+      expression: Expression('Raw', Value('Numeric', NumericValue('U32', 42))),
+    }),
+    destination_id: EvaluatesToIdBox({
+      expression: Expression(
+        'Raw',
+        Value(
+          'Id',
+          IdBox(
+            'AssetId',
+            AssetId({
+              account_id: AccountId({
+                name: 'alice',
+                domain_id: DomainId({
+                  name: 'wonderland',
+                }),
+              }),
+              definition_id: AssetDefinitionId({
+                name: 'time',
+                domain_id: DomainId({ name: 'looking_glass' }),
+              }),
+            }),
+          ),
+        ),
+      ),
+    }),
+  }),
+)

Again it should be emphasised that an Iroha 2 network is strongly typed. You need to take special care to make sure that only unsigned integers are passed to the Value('U32', ...) factory method. Fixed precision values also need to be taken into consideration. Any attempt to add to or subtract from a negative Fixed-precision value will result in an error.

6. Transferring assets

After minting the assets, you can transfer them to another account. In the example below, Alice transfers to Mouse 100 units of time asset:

ts
import {
+  AccountId,
+  AssetDefinitionId,
+  AssetId,
+  DomainId,
+  EvaluatesToIdBox,
+  EvaluatesToValue,
+  Expression,
+  IdBox,
+  Instruction,
+  NumericValue,
+  TransferBox,
+  Value,
+} from '@iroha2/data-model'
+
+const domainId = DomainId({
+  name: 'wonderland',
+})
+
+const assetDefinitionId = AssetDefinitionId({
+  name: 'time',
+  domain_id: domainId,
+})
+
+const amountToTransfer = Value('Numeric', NumericValue('U32', 100))
+
+const fromAccount = AccountId({
+  name: 'alice',
+  domain_id: domainId,
+})
+
+const toAccount = AccountId({
+  name: 'mouse',
+  domain_id: domainId,
+})
+
+const evaluatesToAssetId = (assetId: AssetId): EvaluatesToIdBox =>
+  EvaluatesToIdBox({
+    expression: Expression('Raw', Value('Id', IdBox('AssetId', assetId))),
+  })
+
+const transferAssetInstruction = Instruction(
+  'Transfer',
+  TransferBox({
+    source_id: evaluatesToAssetId(
+      AssetId({
+        definition_id: assetDefinitionId,
+        account_id: fromAccount,
+      }),
+    ),
+    destination_id: evaluatesToAssetId(
+      AssetId({
+        definition_id: assetDefinitionId,
+        account_id: toAccount,
+      }),
+    ),
+    object: EvaluatesToValue({
+      expression: Expression('Raw', amountToTransfer),
+    }),
+  }),
+)
import {
+  AccountId,
+  AssetDefinitionId,
+  AssetId,
+  DomainId,
+  EvaluatesToIdBox,
+  EvaluatesToValue,
+  Expression,
+  IdBox,
+  Instruction,
+  NumericValue,
+  TransferBox,
+  Value,
+} from '@iroha2/data-model'
+
+const domainId = DomainId({
+  name: 'wonderland',
+})
+
+const assetDefinitionId = AssetDefinitionId({
+  name: 'time',
+  domain_id: domainId,
+})
+
+const amountToTransfer = Value('Numeric', NumericValue('U32', 100))
+
+const fromAccount = AccountId({
+  name: 'alice',
+  domain_id: domainId,
+})
+
+const toAccount = AccountId({
+  name: 'mouse',
+  domain_id: domainId,
+})
+
+const evaluatesToAssetId = (assetId: AssetId): EvaluatesToIdBox =>
+  EvaluatesToIdBox({
+    expression: Expression('Raw', Value('Id', IdBox('AssetId', assetId))),
+  })
+
+const transferAssetInstruction = Instruction(
+  'Transfer',
+  TransferBox({
+    source_id: evaluatesToAssetId(
+      AssetId({
+        definition_id: assetDefinitionId,
+        account_id: fromAccount,
+      }),
+    ),
+    destination_id: evaluatesToAssetId(
+      AssetId({
+        definition_id: assetDefinitionId,
+        account_id: toAccount,
+      }),
+    ),
+    object: EvaluatesToValue({
+      expression: Expression('Raw', amountToTransfer),
+    }),
+  }),
+)

7. Querying for Domains, Accounts and Assets

TODO

ts
import { Client, ToriiRequirementsForApiHttp } from '@iroha2/client'
+import { QueryBox } from '@iroha2/data-model'
+
+declare const client: Client
+declare const toriiRequirements: ToriiRequirementsForApiHttp
import { Client, ToriiRequirementsForApiHttp } from '@iroha2/client'
+import { QueryBox } from '@iroha2/data-model'
+
+declare const client: Client
+declare const toriiRequirements: ToriiRequirementsForApiHttp
ts
const result = await client.requestWithQueryBox(
+  toriiRequirements,
+  QueryBox('FindAllDomains', null),
+)
+
+const domains = result
+  .as('Ok')
+  .result.enum.as('Vec')
+  .map((x) => x.enum.as('Identifiable').enum.as('Domain'))
+
+for (const domain of domains) {
+  console.log(
+    \`Domain "\${domain.id.name}" has \${domain.accounts.size} accounts\` +
+      \` and \${domain.asset_definitions.size} asset definitions\`,
+  )
+  // => Domain "wonderland" has 5 accounts and 3 asset definitions
+}
const result = await client.requestWithQueryBox(
+  toriiRequirements,
+  QueryBox('FindAllDomains', null),
+)
+
+const domains = result
+  .as('Ok')
+  .result.enum.as('Vec')
+  .map((x) => x.enum.as('Identifiable').enum.as('Domain'))
+
+for (const domain of domains) {
+  console.log(
+    \`Domain "\${domain.id.name}" has \${domain.accounts.size} accounts\` +
+      \` and \${domain.asset_definitions.size} asset definitions\`,
+  )
+  // => Domain "wonderland" has 5 accounts and 3 asset definitions
+}
ts
const result = await client.requestWithQueryBox(
+  toriiRequirements,
+  QueryBox('FindAllAccounts', null),
+)
+
+const accounts = result
+  .as('Ok')
+  .result.enum.as('Vec')
+  .map((x) => x.enum.as('Identifiable').enum.as('Account'))
+
+for (const account of accounts) {
+  console.log(
+    \`Account "\${account.id.name}@\${account.id.domain_id.name}" \` +
+      \`has \${account.assets.size} assets\`,
+  )
+  // => Account "alice@wonderland" has 3 assets
+}
const result = await client.requestWithQueryBox(
+  toriiRequirements,
+  QueryBox('FindAllAccounts', null),
+)
+
+const accounts = result
+  .as('Ok')
+  .result.enum.as('Vec')
+  .map((x) => x.enum.as('Identifiable').enum.as('Account'))
+
+for (const account of accounts) {
+  console.log(
+    \`Account "\${account.id.name}@\${account.id.domain_id.name}" \` +
+      \`has \${account.assets.size} assets\`,
+  )
+  // => Account "alice@wonderland" has 3 assets
+}
ts
const result = await client.requestWithQueryBox(
+  toriiRequirements,
+  QueryBox('FindAllAssets', null),
+)
+
+const assets = result
+  .as('Ok')
+  .result.enum.as('Vec')
+  .map((x) => x.enum.as('Identifiable').enum.as('Asset'))
+
+for (const asset of assets) {
+  console.log(
+    \`Asset "\${asset.id.definition_id.name}#\${asset.id.definition_id.domain_id.name}" \` +
+      \`at account "\${asset.id.account_id.name}@\${asset.id.account_id.domain_id.name}" \` +
+      \`has type "\${asset.value.enum.tag}"\`,
+  )
+  // => Asset "rose#wonderland" at account "alice@wonderland" has type "Quantity"
+}
const result = await client.requestWithQueryBox(
+  toriiRequirements,
+  QueryBox('FindAllAssets', null),
+)
+
+const assets = result
+  .as('Ok')
+  .result.enum.as('Vec')
+  .map((x) => x.enum.as('Identifiable').enum.as('Asset'))
+
+for (const asset of assets) {
+  console.log(
+    \`Asset "\${asset.id.definition_id.name}#\${asset.id.definition_id.domain_id.name}" \` +
+      \`at account "\${asset.id.account_id.name}@\${asset.id.account_id.domain_id.name}" \` +
+      \`has type "\${asset.value.enum.tag}"\`,
+  )
+  // => Asset "rose#wonderland" at account "alice@wonderland" has type "Quantity"
+}

8. Visualizing outputs in Web UI

Finally, we should talk about visualising data. The Rust API is currently the most complete in terms of available queries and instructions. After all, this is the language in which Iroha 2 was built.

Let's build a small Vue 3 application that uses each API we've discovered in this guide!

TIP

In this guide, we are roughly recreating the project that is a part of iroha-javascript integration tests. If you want to see the full project, please refer to the @iroha2/client-test-web sources.

Our app will consist of 3 main views:

  • Status checker that periodically requests peer status (e.g. current blocks height) and shows it;
  • Domain creator, which is a form to create a new domain with specified name;
  • Listener with a toggle to setup listening for events.

You can use this folder structure as a reference:

╭───┬──────────────────────────────╮
+│ # │             name             │
+├───┼──────────────────────────────┤
+│ 0 │ App.vue                      │
+│ 1 │ client.ts                    │
+│ 2 │ components/CreateDomain.vue  │
+│ 3 │ components/Listener.vue      │
+│ 4 │ components/StatusChecker.vue │
+│ 5 │ config.json                  │
+│ 6 │ crypto.ts                    │
+│ 7 │ main.ts                      │
+╰───┴──────────────────────────────╯
╭───┬──────────────────────────────╮
+│ # │             name             │
+├───┼──────────────────────────────┤
+│ 0 │ App.vue                      │
+│ 1 │ client.ts                    │
+│ 2 │ components/CreateDomain.vue  │
+│ 3 │ components/Listener.vue      │
+│ 4 │ components/StatusChecker.vue │
+│ 5 │ config.json                  │
+│ 6 │ crypto.ts                    │
+│ 7 │ main.ts                      │
+╰───┴──────────────────────────────╯
json
{
+  "torii": {
+    "apiURL": "http://127.0.0.1:8080",
+    "telemetryURL": "http://127.0.0.1:8081"
+  },
+  "account": {
+    "name": "alice",
+    "domain_id": {
+      "name": "wonderland"
+    }
+  },
+  "public_key": "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",
+  "private_key": {
+    "digest_function": "ed25519",
+    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
+  }
+}
{
+  "torii": {
+    "apiURL": "http://127.0.0.1:8080",
+    "telemetryURL": "http://127.0.0.1:8081"
+  },
+  "account": {
+    "name": "alice",
+    "domain_id": {
+      "name": "wonderland"
+    }
+  },
+  "public_key": "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",
+  "private_key": {
+    "digest_function": "ed25519",
+    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
+  }
+}
ts
import { crypto, init } from '@iroha2/crypto-target-web'
+
+await init()
+
+export { crypto }
import { crypto, init } from '@iroha2/crypto-target-web'
+
+await init()
+
+export { crypto }
ts
import { Client, Signer } from '@iroha2/client'
+import { adapter as WS } from '@iroha2/client/web-socket/native'
+import { crypto } from './crypto'
+import { client_config } from '../../config'
+import { AccountId } from '@iroha2/data-model'
+
+const HOST = window.location.host
+
+export const toriiPre = {
+  // proxified with vite
+  apiURL: \`http://\${HOST}/torii/api\`,
+  telemetryURL: \`http://\${HOST}/torii/telemetry\`,
+  ws: WS,
+  fetch: fetch.bind(window),
+}
+
+const signer = new Signer(client_config.account as AccountId, crypto.KeyPair.fromJSON(client_config))
+
+export const client = new Client({ signer })
import { Client, Signer } from '@iroha2/client'
+import { adapter as WS } from '@iroha2/client/web-socket/native'
+import { crypto } from './crypto'
+import { client_config } from '../../config'
+import { AccountId } from '@iroha2/data-model'
+
+const HOST = window.location.host
+
+export const toriiPre = {
+  // proxified with vite
+  apiURL: \`http://\${HOST}/torii/api\`,
+  telemetryURL: \`http://\${HOST}/torii/telemetry\`,
+  ws: WS,
+  fetch: fetch.bind(window),
+}
+
+const signer = new Signer(client_config.account as AccountId, crypto.KeyPair.fromJSON(client_config))
+
+export const client = new Client({ signer })
vue
<script setup lang="ts">
+import { useIntervalFn } from '@vueuse/core'
+import { useStaleState, useTask } from '@vue-kakuyaku/core'
+import { toriiPre } from '../client'
+import { Torii } from '@iroha2/client'
+
+const { state, run } = useTask(() => Torii.getStatus(toriiPre), { immediate: true })
+const stale = useStaleState(state)
+useIntervalFn(run, 1000)
+</script>
+
+<template>
+  <div>
+    <h3>Status</h3>
+
+    <ul v-if="stale.fulfilled">
+      <li>Blocks: {{ stale.fulfilled.value.blocks }}</li>
+      <li>Uptime (sec): {{ stale.fulfilled.value.uptime.secs }}</li>
+    </ul>
+  </div>
+</template>
<script setup lang="ts">
+import { useIntervalFn } from '@vueuse/core'
+import { useStaleState, useTask } from '@vue-kakuyaku/core'
+import { toriiPre } from '../client'
+import { Torii } from '@iroha2/client'
+
+const { state, run } = useTask(() => Torii.getStatus(toriiPre), { immediate: true })
+const stale = useStaleState(state)
+useIntervalFn(run, 1000)
+</script>
+
+<template>
+  <div>
+    <h3>Status</h3>
+
+    <ul v-if="stale.fulfilled">
+      <li>Blocks: {{ stale.fulfilled.value.blocks }}</li>
+      <li>Uptime (sec): {{ stale.fulfilled.value.uptime.secs }}</li>
+    </ul>
+  </div>
+</template>
vue
<script setup lang="ts">
+import {
+  DomainId,
+  EvaluatesToRegistrableBox,
+  Executable,
+  Expression,
+  IdentifiableBox,
+  Instruction,
+  MapNameValue,
+  Metadata,
+  NewDomain,
+  OptionIpfsPath,
+  RegisterBox,
+  Value,
+  VecInstruction,
+} from '@iroha2/data-model'
+import { ref } from 'vue'
+import { client, toriiPre } from '../client'
+import { useTask } from '@vue-kakuyaku/core'
+
+const domainName = ref('')
+
+const { state, run: registerDomain } = useTask(async () => {
+  await client.submitExecutable(
+    toriiPre,
+    Executable(
+      'Instructions',
+      VecInstruction([
+        Instruction(
+          'Register',
+          RegisterBox({
+            object: EvaluatesToRegistrableBox({
+              expression: Expression(
+                'Raw',
+                Value(
+                  'Identifiable',
+                  IdentifiableBox(
+                    'NewDomain',
+                    NewDomain({
+                      id: DomainId({
+                        name: domainName.value,
+                      }),
+                      metadata: Metadata({ map: MapNameValue(new Map()) }),
+                      logo: OptionIpfsPath('None'),
+                    }),
+                  ),
+                ),
+              ),
+            }),
+          }),
+        ),
+      ]),
+    ),
+  )
+})
+</script>
+
+<template>
+  <div>
+    <h3>Create Domain</h3>
+    <p>
+      <label for="domain">New domain name:</label> <input
+        id="domain"
+        v-model="domainName"
+      >
+    </p>
+    <p>
+      <button @click="registerDomain()">
+        Register domain{{ state.pending ? '...' : '' }}
+      </button>
+    </p>
+  </div>
+</template>
<script setup lang="ts">
+import {
+  DomainId,
+  EvaluatesToRegistrableBox,
+  Executable,
+  Expression,
+  IdentifiableBox,
+  Instruction,
+  MapNameValue,
+  Metadata,
+  NewDomain,
+  OptionIpfsPath,
+  RegisterBox,
+  Value,
+  VecInstruction,
+} from '@iroha2/data-model'
+import { ref } from 'vue'
+import { client, toriiPre } from '../client'
+import { useTask } from '@vue-kakuyaku/core'
+
+const domainName = ref('')
+
+const { state, run: registerDomain } = useTask(async () => {
+  await client.submitExecutable(
+    toriiPre,
+    Executable(
+      'Instructions',
+      VecInstruction([
+        Instruction(
+          'Register',
+          RegisterBox({
+            object: EvaluatesToRegistrableBox({
+              expression: Expression(
+                'Raw',
+                Value(
+                  'Identifiable',
+                  IdentifiableBox(
+                    'NewDomain',
+                    NewDomain({
+                      id: DomainId({
+                        name: domainName.value,
+                      }),
+                      metadata: Metadata({ map: MapNameValue(new Map()) }),
+                      logo: OptionIpfsPath('None'),
+                    }),
+                  ),
+                ),
+              ),
+            }),
+          }),
+        ),
+      ]),
+    ),
+  )
+})
+</script>
+
+<template>
+  <div>
+    <h3>Create Domain</h3>
+    <p>
+      <label for="domain">New domain name:</label> <input
+        id="domain"
+        v-model="domainName"
+      >
+    </p>
+    <p>
+      <button @click="registerDomain()">
+        Register domain{{ state.pending ? '...' : '' }}
+      </button>
+    </p>
+  </div>
+</template>
vue
<script setup lang="ts">
+import { SetupEventsReturn, Torii } from '@iroha2/client'
+import {
+  FilterBox,
+  OptionHash,
+  OptionPipelineEntityKind,
+  OptionPipelineStatusKind,
+  PipelineEntityKind,
+  PipelineEventFilter,
+  PipelineStatus,
+  PipelineStatusKind,
+} from '@iroha2/data-model'
+import { computed, onBeforeUnmount, shallowReactive, shallowRef } from 'vue'
+import { toriiPre } from '../client'
+
+function bytesToHex(bytes: number[]): string {
+  return bytes.map((byte) => byte.toString(16).padStart(2, '0')).join('')
+}
+
+interface EventData {
+  hash: string
+  status: string
+}
+
+const events = shallowReactive<EventData[]>([])
+
+const currentListener = shallowRef<null | SetupEventsReturn>(null)
+
+const isListening = computed(() => !!currentListener.value)
+
+function displayStatus(status: PipelineStatus): string {
+  switch (status.enum.tag) {
+    case 'Validating':
+      return 'validating'
+    case 'Committed':
+      return 'committed'
+    case 'Rejected':
+      return 'rejected with some reason'
+  }
+}
+
+async function startListening() {
+  currentListener.value = await Torii.listenForEvents(toriiPre, {
+    filter: FilterBox(
+      'Pipeline',
+      PipelineEventFilter({
+        entity_kind: OptionPipelineEntityKind('Some', PipelineEntityKind('Transaction')),
+        status_kind: OptionPipelineStatusKind('Some', PipelineStatusKind('Committed')),
+        hash: OptionHash('None'),
+      }),
+    ),
+  })
+
+  currentListener.value.ee.on('event', (event) => {
+    const { hash, status } = event.enum.as('Pipeline')
+    events.push({
+      hash: bytesToHex([...hash]),
+      status: displayStatus(status),
+    })
+  })
+}
+
+async function stopListening() {
+  await currentListener.value?.stop()
+  currentListener.value = null
+}
+
+onBeforeUnmount(stopListening)
+</script>
+
+<template>
+  <div>
+    <h3>Listening</h3>
+
+    <p>
+      <button @click="isListening ? stopListening() : startListening()">
+        {{ isListening ? 'Stop' : 'Listen' }}
+      </button>
+    </p>
+
+    <p>Events:</p>
+
+    <ul class="events-list">
+      <li
+        v-for="{ hash, status } in events"
+        :key="hash"
+      >
+        Transaction <code>{{ hash }}</code> status:
+        {{ status }}
+      </li>
+    </ul>
+  </div>
+</template>
<script setup lang="ts">
+import { SetupEventsReturn, Torii } from '@iroha2/client'
+import {
+  FilterBox,
+  OptionHash,
+  OptionPipelineEntityKind,
+  OptionPipelineStatusKind,
+  PipelineEntityKind,
+  PipelineEventFilter,
+  PipelineStatus,
+  PipelineStatusKind,
+} from '@iroha2/data-model'
+import { computed, onBeforeUnmount, shallowReactive, shallowRef } from 'vue'
+import { toriiPre } from '../client'
+
+function bytesToHex(bytes: number[]): string {
+  return bytes.map((byte) => byte.toString(16).padStart(2, '0')).join('')
+}
+
+interface EventData {
+  hash: string
+  status: string
+}
+
+const events = shallowReactive<EventData[]>([])
+
+const currentListener = shallowRef<null | SetupEventsReturn>(null)
+
+const isListening = computed(() => !!currentListener.value)
+
+function displayStatus(status: PipelineStatus): string {
+  switch (status.enum.tag) {
+    case 'Validating':
+      return 'validating'
+    case 'Committed':
+      return 'committed'
+    case 'Rejected':
+      return 'rejected with some reason'
+  }
+}
+
+async function startListening() {
+  currentListener.value = await Torii.listenForEvents(toriiPre, {
+    filter: FilterBox(
+      'Pipeline',
+      PipelineEventFilter({
+        entity_kind: OptionPipelineEntityKind('Some', PipelineEntityKind('Transaction')),
+        status_kind: OptionPipelineStatusKind('Some', PipelineStatusKind('Committed')),
+        hash: OptionHash('None'),
+      }),
+    ),
+  })
+
+  currentListener.value.ee.on('event', (event) => {
+    const { hash, status } = event.enum.as('Pipeline')
+    events.push({
+      hash: bytesToHex([...hash]),
+      status: displayStatus(status),
+    })
+  })
+}
+
+async function stopListening() {
+  await currentListener.value?.stop()
+  currentListener.value = null
+}
+
+onBeforeUnmount(stopListening)
+</script>
+
+<template>
+  <div>
+    <h3>Listening</h3>
+
+    <p>
+      <button @click="isListening ? stopListening() : startListening()">
+        {{ isListening ? 'Stop' : 'Listen' }}
+      </button>
+    </p>
+
+    <p>Events:</p>
+
+    <ul class="events-list">
+      <li
+        v-for="{ hash, status } in events"
+        :key="hash"
+      >
+        Transaction <code>{{ hash }}</code> status:
+        {{ status }}
+      </li>
+    </ul>
+  </div>
+</template>
vue
<script setup lang="ts">
+import CreateDomain from './components/CreateDomain.vue'
+import EventListener from './components/EventListener.vue'
+import StatusChecker from './components/StatusChecker.vue'
+</script>
+
+<template>
+  <StatusChecker />
+  <hr>
+  <CreateDomain />
+  <hr>
+  <EventListener />
+</template>
+
+<style lang="scss">
+#app {
+  padding: 16px;
+  font-family: sans-serif;
+}
+</style>
<script setup lang="ts">
+import CreateDomain from './components/CreateDomain.vue'
+import EventListener from './components/EventListener.vue'
+import StatusChecker from './components/StatusChecker.vue'
+</script>
+
+<template>
+  <StatusChecker />
+  <hr>
+  <CreateDomain />
+  <hr>
+  <EventListener />
+</template>
+
+<style lang="scss">
+#app {
+  padding: 16px;
+  font-family: sans-serif;
+}
+</style>
ts
import { createApp } from 'vue'
+import App from './App.vue'
+import { Logger } from '@iroha2/data-model'
+import { crypto } from './crypto'
+import { setCrypto } from '@iroha2/client'
+
+setCrypto(crypto)
+new Logger().mount()
+localStorage.debug = '*'
+
+createApp(App).mount('#app')
import { createApp } from 'vue'
+import App from './App.vue'
+import { Logger } from '@iroha2/data-model'
+import { crypto } from './crypto'
+import { setCrypto } from '@iroha2/client'
+
+setCrypto(crypto)
+new Logger().mount()
+localStorage.debug = '*'
+
+createApp(App).mount('#app')

INFO

In client.ts, we import the configuration file like this:

ts
import { client_config } from '../../config'
import { client_config } from '../../config'

Note that you need to import the config in this way because this is how the source code of this application works. You can interpret this line as import client_config from 'config.json'.

Demo

Here is a small demo with the usage of this component:

Demo of the sample Vue application

9. Subscribing to Block Stream

You can use /block/stream endpoint to send a subscription request for block streaming.

Via this endpoint, the client first provides the starting block number (i.e. height) in the subscription request. After sending the confirmation message, the server starts streaming all the blocks from the given block number up to the current block, and continues to stream blocks as they are added to the blockchain.

Here is an example of how to listen to the block stream:

ts
import { Torii, ToriiRequirementsForApiWebSocket } from '@iroha2/client'
+
+declare const requirements: ToriiRequirementsForApiWebSocket
+
+const stream = await Torii.listenForBlocksStream(requirements, {
+  height: 0n,
+})
+
+stream.ee.on('block', (block) => {
+  const height = block.enum.as('V1').header.height
+  console.log('Got block with height', height)
+})
import { Torii, ToriiRequirementsForApiWebSocket } from '@iroha2/client'
+
+declare const requirements: ToriiRequirementsForApiWebSocket
+
+const stream = await Torii.listenForBlocksStream(requirements, {
+  height: 0n,
+})
+
+stream.ee.on('block', (block) => {
+  const height = block.enum.as('V1').header.height
+  console.log('Got block with height', height)
+})

  1. We have to pass environment-specific ws and fetch, because there is no way for Iroha Client to communicate with a peer in an environment-agnostic way. ↩︎

`,100),t=[e];function c(r,E,y,i,F,d){return n(),a("div",null,t)}const m=s(o,[["render",c]]);export{h as __pageData,m as default}; diff --git a/assets/guide_get-started_javascript.md.274c1036.lean.js b/assets/guide_get-started_javascript.md.274c1036.lean.js new file mode 100644 index 000000000..540ff7afc --- /dev/null +++ b/assets/guide_get-started_javascript.md.274c1036.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.1293becd.js";const p="/iroha-2-docs/assets/sample-vue-app.4caffed6.gif",h=JSON.parse('{"title":"JavaScript/TypeScript Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/javascript.md","filePath":"guide/get-started/javascript.md","lastUpdated":1700563625000}'),o={name:"guide/get-started/javascript.md"},e=l("",100),t=[e];function c(r,E,y,i,F,d){return n(),a("div",null,t)}const m=s(o,[["render",c]]);export{h as __pageData,m as default}; diff --git a/assets/guide_get-started_kotlin-java.md.fd61b4c2.js b/assets/guide_get-started_kotlin-java.md.fd61b4c2.js new file mode 100644 index 000000000..247447ec4 --- /dev/null +++ b/assets/guide_get-started_kotlin-java.md.fd61b4c2.js @@ -0,0 +1,969 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.1293becd.js";const p="/iroha-2-docs/assets/iroha_java_hash.c677e97a.png",o="/iroha-2-docs/assets/iroha_java_commits.a73a8bf6.png",A=JSON.parse('{"title":"Kotlin/Java Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/kotlin-java.md","filePath":"guide/get-started/kotlin-java.md","lastUpdated":1700563625000}'),e={name:"guide/get-started/kotlin-java.md"},t=l(`

Kotlin/Java Guide

1. Iroha 2 Client Setup

In this part we shall cover the main things to look out for if you want to use Iroha 2 in your Kotlin application. Instead of providing the complete basics, we shall assume knowledge of the most widely used concepts, explain the unusual, and provide some instructions for creating your own Iroha 2-compatible client.

We assume that you know how to create a new package and have basic understanding of the fundamental Kotlin code. Specifically, we shall assume that you know how to build and deploy your program on the target platforms. To clone Iroha 2 JVM compatible SDKs, you can use Iroha Java.

Without further ado, here's a part of an example build.gradle.kts file, specifically, the plugins, repositories and dependencies sections:

kotlin
plugins {
+    kotlin("jvm") version "1.6.10"
+    application
+}
+
+group = "jp.co.soramitsu"
+version = "1.0-SNAPSHOT"
+
+repositories {
+    mavenCentral()
+    maven(url = "https://jitpack.io")
+}
+
+dependencies {
+    val iroha2Ver by System.getProperties()
+
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.0")
+
+    api("com.github.hyperledger.iroha-java:admin-client:SNAPSHOT")
+    implementation("com.github.hyperledger.iroha-java:model:SNAPSHOT")
+    implementation("com.github.hyperledger.iroha-java:block:SNAPSHOT")
+
+    implementation("net.i2p.crypto:eddsa:0.3.0")
+    implementation("org.bouncycastle:bcprov-jdk15on:1.65")
+    implementation("com.github.multiformats:java-multihash:1.3.0")
+}
plugins {
+    kotlin("jvm") version "1.6.10"
+    application
+}
+
+group = "jp.co.soramitsu"
+version = "1.0-SNAPSHOT"
+
+repositories {
+    mavenCentral()
+    maven(url = "https://jitpack.io")
+}
+
+dependencies {
+    val iroha2Ver by System.getProperties()
+
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.0")
+
+    api("com.github.hyperledger.iroha-java:admin-client:SNAPSHOT")
+    implementation("com.github.hyperledger.iroha-java:model:SNAPSHOT")
+    implementation("com.github.hyperledger.iroha-java:block:SNAPSHOT")
+
+    implementation("net.i2p.crypto:eddsa:0.3.0")
+    implementation("org.bouncycastle:bcprov-jdk15on:1.65")
+    implementation("com.github.multiformats:java-multihash:1.3.0")
+}

You should replace the SNAPSHOT in the above configuration with the latest iroha-java snapshot.

Snapshot versions match the Git commits. To get the latest snapshot, simply visit the iroha-java repository on the iroha-2-dev branch and copy the short hash of the last commit on the main page.

You can also check the commit history and copy the commit hash of a previous commit.

This will give you the latest development release of Iroha 2.

2. Configuring Iroha 2

At present, the Kotlin SDK doesn't have any classes to interact with the configuration. Instead, you are provided with a ready-made Iroha2Client that reads the configuration from the environment variables and/or the resident config.json in the working directory.

If you are so inclined, you can have a look at the testcontainers module, and see how the Iroha2Config is implemented.

kotlin
package jp.co.soramitsu.iroha2.testengine
+
+import jp.co.soramitsu.iroha2.DEFAULT_API_PORT
+import jp.co.soramitsu.iroha2.DEFAULT_P2P_PORT
+import jp.co.soramitsu.iroha2.DEFAULT_TELEMETRY_PORT
+import jp.co.soramitsu.iroha2.Genesis
+import jp.co.soramitsu.iroha2.generateKeyPair
+import jp.co.soramitsu.iroha2.generated.datamodel.peer.PeerId
+import jp.co.soramitsu.iroha2.toIrohaPublicKey
+import org.slf4j.LoggerFactory.getLogger
+import org.testcontainers.containers.Network
+import org.testcontainers.containers.Network.newNetwork
+import org.testcontainers.containers.output.OutputFrame
+import org.testcontainers.containers.output.Slf4jLogConsumer
+import org.testcontainers.images.ImagePullPolicy
+import org.testcontainers.images.PullPolicy
+import java.security.KeyPair
+import java.time.Duration
+import java.util.function.Consumer
+
+/**
+ * Iroha configuration
+ */
+class IrohaConfig(
+    var networkToJoin: Network = newNetwork(),
+    var logConsumer: Consumer<OutputFrame> = Slf4jLogConsumer(getLogger(IrohaContainer::class.java)),
+    var genesis: Genesis = Genesis.getEmpty(),
+    var imageTag: String = IrohaContainer.DEFAULT_IMAGE_TAG,
+    var imageName: String = IrohaContainer.DEFAULT_IMAGE_NAME,
+    var pullPolicy: ImagePullPolicy = PullPolicy.ageBased(Duration.ofMinutes(10)),
+    var alias: String = IrohaContainer.NETWORK_ALIAS + DEFAULT_P2P_PORT,
+    var keyPair: KeyPair = generateKeyPair(),
+    var trustedPeers: List<PeerId> = listOf(
+        PeerId(
+            "$alias:$DEFAULT_P2P_PORT",
+            keyPair.public.toIrohaPublicKey()
+        )
+    ),
+    var ports: List<Int> = listOf(DEFAULT_P2P_PORT, DEFAULT_API_PORT, DEFAULT_TELEMETRY_PORT),
+    var shouldCloseNetwork: Boolean = true,
+    var waitStrategy: Boolean = true,
+    var submitGenesis: Boolean = true
+) {
+    companion object {
+        const val P2P_PORT_IDX = 0
+        const val API_PORT_IDX = 1
+        const val TELEMETRY_PORT_IDX = 2
+    }
+}
package jp.co.soramitsu.iroha2.testengine
+
+import jp.co.soramitsu.iroha2.DEFAULT_API_PORT
+import jp.co.soramitsu.iroha2.DEFAULT_P2P_PORT
+import jp.co.soramitsu.iroha2.DEFAULT_TELEMETRY_PORT
+import jp.co.soramitsu.iroha2.Genesis
+import jp.co.soramitsu.iroha2.generateKeyPair
+import jp.co.soramitsu.iroha2.generated.datamodel.peer.PeerId
+import jp.co.soramitsu.iroha2.toIrohaPublicKey
+import org.slf4j.LoggerFactory.getLogger
+import org.testcontainers.containers.Network
+import org.testcontainers.containers.Network.newNetwork
+import org.testcontainers.containers.output.OutputFrame
+import org.testcontainers.containers.output.Slf4jLogConsumer
+import org.testcontainers.images.ImagePullPolicy
+import org.testcontainers.images.PullPolicy
+import java.security.KeyPair
+import java.time.Duration
+import java.util.function.Consumer
+
+/**
+ * Iroha configuration
+ */
+class IrohaConfig(
+    var networkToJoin: Network = newNetwork(),
+    var logConsumer: Consumer<OutputFrame> = Slf4jLogConsumer(getLogger(IrohaContainer::class.java)),
+    var genesis: Genesis = Genesis.getEmpty(),
+    var imageTag: String = IrohaContainer.DEFAULT_IMAGE_TAG,
+    var imageName: String = IrohaContainer.DEFAULT_IMAGE_NAME,
+    var pullPolicy: ImagePullPolicy = PullPolicy.ageBased(Duration.ofMinutes(10)),
+    var alias: String = IrohaContainer.NETWORK_ALIAS + DEFAULT_P2P_PORT,
+    var keyPair: KeyPair = generateKeyPair(),
+    var trustedPeers: List<PeerId> = listOf(
+        PeerId(
+            "$alias:$DEFAULT_P2P_PORT",
+            keyPair.public.toIrohaPublicKey()
+        )
+    ),
+    var ports: List<Int> = listOf(DEFAULT_P2P_PORT, DEFAULT_API_PORT, DEFAULT_TELEMETRY_PORT),
+    var shouldCloseNetwork: Boolean = true,
+    var waitStrategy: Boolean = true,
+    var submitGenesis: Boolean = true
+) {
+    companion object {
+        const val P2P_PORT_IDX = 0
+        const val API_PORT_IDX = 1
+        const val TELEMETRY_PORT_IDX = 2
+    }
+}

3. Querying and Registering Domains

Querying and Registering a domain are easier operations. The usual boilerplate code, that often only serves to instantiate a client from an on-disk configuration file, is unnecessary. We will immediately add all the necessary imports to implement this client:

kotlin
import jp.co.soramitsu.iroha2.*
+import jp.co.soramitsu.iroha2.generated.crypto.PublicKey
+import jp.co.soramitsu.iroha2.generated.datamodel.Value
+import jp.co.soramitsu.iroha2.generated.datamodel.account.AccountId
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValue
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValueType
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.Mintable
+import jp.co.soramitsu.iroha2.generated.datamodel.metadata.Metadata
+import jp.co.soramitsu.iroha2.generated.datamodel.name.Name
+import jp.co.soramitsu.iroha2.generated.datamodel.predicate.GenericValuePredicateBox
+import jp.co.soramitsu.iroha2.generated.datamodel.predicate.value.ValuePredicate
+import jp.co.soramitsu.iroha2.query.QueryBuilder
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
+import java.net.URL
+import java.security.KeyPair
import jp.co.soramitsu.iroha2.*
+import jp.co.soramitsu.iroha2.generated.crypto.PublicKey
+import jp.co.soramitsu.iroha2.generated.datamodel.Value
+import jp.co.soramitsu.iroha2.generated.datamodel.account.AccountId
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValue
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValueType
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.Mintable
+import jp.co.soramitsu.iroha2.generated.datamodel.metadata.Metadata
+import jp.co.soramitsu.iroha2.generated.datamodel.name.Name
+import jp.co.soramitsu.iroha2.generated.datamodel.predicate.GenericValuePredicateBox
+import jp.co.soramitsu.iroha2.generated.datamodel.predicate.value.ValuePredicate
+import jp.co.soramitsu.iroha2.query.QueryBuilder
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
+import java.net.URL
+import java.security.KeyPair

We shall write this example in the form of a test class, hence the presence of test-related packages. Note the presence of coroutines.runBlocking. Iroha makes extensive use of asynchronous programming (in Rust terminology), hence blocking is not necessarily the only mode of interaction with the Iroha 2 code.

In order to make sure that the raised peers work correctly, you can do a simple operation to get all registered domains.

Next, we will add wrappers to the classes created in this section.

kotlin
fun main(args: Array<String>): Unit = runBlocking{
+    val peerUrl = "http://127.0.0.1:8080"
+    val telemetryUrl = "http://127.0.0.1:8180"
+    val admin = AccountId("bob".asName(), "wonderland".asDomainId())
+    val adminKeyPair = keyPairFromHex("7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",
+        "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e")
+
+    val client = AdminIroha2Client(URL(peerUrl), URL(telemetryUrl), log = true)
+    val query = Query(client, admin, adminKeyPair)
+
+    query.findAllDomains()
+        .also { println("ALL DOMAINS: \${it.map { d -> d.id.asString() }}") }
+
+}
+
+open class Query (private val client: AdminIroha2Client,
+                  private val admin: AccountId,
+                  private val keyPair: KeyPair) {
+    
+    suspend fun findAllDomains(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
+        .findAllDomains(queryFilter)
+        .account(admin)
+        .buildSigned(keyPair)
+        .let { client.sendQuery(it) }
+}
fun main(args: Array<String>): Unit = runBlocking{
+    val peerUrl = "http://127.0.0.1:8080"
+    val telemetryUrl = "http://127.0.0.1:8180"
+    val admin = AccountId("bob".asName(), "wonderland".asDomainId())
+    val adminKeyPair = keyPairFromHex("7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",
+        "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e")
+
+    val client = AdminIroha2Client(URL(peerUrl), URL(telemetryUrl), log = true)
+    val query = Query(client, admin, adminKeyPair)
+
+    query.findAllDomains()
+        .also { println("ALL DOMAINS: \${it.map { d -> d.id.asString() }}") }
+
+}
+
+open class Query (private val client: AdminIroha2Client,
+                  private val admin: AccountId,
+                  private val keyPair: KeyPair) {
+    
+    suspend fun findAllDomains(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
+        .findAllDomains(queryFilter)
+        .account(admin)
+        .buildSigned(keyPair)
+        .let { client.sendQuery(it) }
+}

The output in the terminal will contain a list of all domains that are currently registered.

Expand to see the expected output
ALL DOMAINS: [wonderland, genesis, garden_of_live_flowers]
ALL DOMAINS: [wonderland, genesis, garden_of_live_flowers]

To register a new domain, add the following lines to Main.kt:

kotlin
val sendTransaction = SendTransaction(client, admin, adminKeyPair)
+
+val domain = "looking_glass_\${System.currentTimeMillis()}"
+    sendTransaction.registerDomain(domain).also { println("DOMAIN $domain CREATED") }
val sendTransaction = SendTransaction(client, admin, adminKeyPair)
+
+val domain = "looking_glass_\${System.currentTimeMillis()}"
+    sendTransaction.registerDomain(domain).also { println("DOMAIN $domain CREATED") }

Then create new open class SendTransaction in your project:

kotlin
open class SendTransaction (private val client: AdminIroha2Client,
+                            private val admin: AccountId,
+                            private val keyPair: KeyPair,
+                            private val timeout: Long = 10000) {
+
+    suspend fun registerDomain(
+        id: String,
+        metadata: Map<Name, Value> = mapOf(),
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.registerDomain(id.asDomainId(), metadata)
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }
+}
open class SendTransaction (private val client: AdminIroha2Client,
+                            private val admin: AccountId,
+                            private val keyPair: KeyPair,
+                            private val timeout: Long = 10000) {
+
+    suspend fun registerDomain(
+        id: String,
+        metadata: Map<Name, Value> = mapOf(),
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.registerDomain(id.asDomainId(), metadata)
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }
+}
Expand to see the expected output
DOMAIN looking_glass CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, wonderland]
DOMAIN looking_glass CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, wonderland]

4. Registering an Account

Registering an account is more involved than the aforementioned functions. Previously, we only had to worry about submitting a single instruction, with a single string-based registration box (in Rust terminology, the heap-allocated reference types are all called boxes).

When registering an account, there are a few more variables. The account can only be registered to an existing domain. Also, an account typically has to have a key pair.

To register a new account, add the following lines to Main.kt:

Kotlin
val madHatter = "madHatter_\${System.currentTimeMillis()}$ACCOUNT_ID_DELIMITER$domain"
+    val madHatterKeyPair = generateKeyPair()
+    sendTransaction.registerAccount(madHatter, listOf(madHatterKeyPair.public.toIrohaPublicKey()))
+        .also { println("ACCOUNT $madHatter CREATED") }
+
+    query.findAllAccounts()
+        .also { println("ALL ACCOUNTS: \${it.map { a -> a.id.asString() }}") }
val madHatter = "madHatter_\${System.currentTimeMillis()}$ACCOUNT_ID_DELIMITER$domain"
+    val madHatterKeyPair = generateKeyPair()
+    sendTransaction.registerAccount(madHatter, listOf(madHatterKeyPair.public.toIrohaPublicKey()))
+        .also { println("ACCOUNT $madHatter CREATED") }
+
+    query.findAllAccounts()
+        .also { println("ALL ACCOUNTS: \${it.map { a -> a.id.asString() }}") }

Then implement new method for class SendTransaction in your project.

Kotlin
suspend fun registerAccount(
+        id: String,
+        signatories: List<PublicKey>,
+        metadata: Map<Name, Value> = mapOf(),
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.registerAccount(id.asAccountId(), signatories, Metadata(metadata))
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }
suspend fun registerAccount(
+        id: String,
+        signatories: List<PublicKey>,
+        metadata: Map<Name, Value> = mapOf(),
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.registerAccount(id.asAccountId(), signatories, Metadata(metadata))
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }

Also, a new method has been added to the Query class.

Kotlin
suspend fun findAllAccounts(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
+        .findAllAccounts(queryFilter)
+        .account(admin)
+        .buildSigned(keyPair)
+        .let {
+            client.sendQuery(it)
+    }
suspend fun findAllAccounts(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
+        .findAllAccounts(queryFilter)
+        .account(admin)
+        .buildSigned(keyPair)
+        .let {
+            client.sendQuery(it)
+    }
Expand to see the expected output
DOMAIN looking_glass_1684835731653 CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, wonderland, looking_glass_1684835731653]
+ACCOUNT madHatter_1684835733686@looking_glass_1684835731653 CREATED
+ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
DOMAIN looking_glass_1684835731653 CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, wonderland, looking_glass_1684835731653]
+ACCOUNT madHatter_1684835733686@looking_glass_1684835731653 CREATED
+ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]

As you can see, for illustrative purposes, we have generated a new key-pair. We converted that key-pair into an Iroha-compatible format using toIrohaPublicKey, and added the public key to the instruction to register an account.

5. Registering and minting assets

Iroha has been built with few underlying assumptions about what the assets need to be in terms of their value type and characteristics (fungible or non-fungible, mintable or non-mintable).

INFO

The non-mintable assets are a relatively recent addition to Iroha 2, thus registering and minting such assets is not presently possible through the Kotlin SDK.

To register new assets definition, add the following lines of code to main

Kotlin
val assetDefinition = "asset_time_\${System.currentTimeMillis()}$ASSET_ID_DELIMITER$domain"
+    sendTransaction.registerAssetDefinition(assetDefinition, AssetValueType.Quantity())
+        .also { println("ASSET DEFINITION $assetDefinition CREATED") }
val assetDefinition = "asset_time_\${System.currentTimeMillis()}$ASSET_ID_DELIMITER$domain"
+    sendTransaction.registerAssetDefinition(assetDefinition, AssetValueType.Quantity())
+        .also { println("ASSET DEFINITION $assetDefinition CREATED") }

Then implement new method for class SendTransaction in your project.

Kotlin
suspend fun registerAssetDefinition(
+        id: String,
+        type: AssetValueType = AssetValueType.Store(),
+        metadata: Map<Name, Value> = mapOf(),
+        mintable: Mintable = Mintable.Infinitely(),
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.registerAssetDefinition(id.asAssetDefinitionId(), type, Metadata(metadata), mintable)
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }
suspend fun registerAssetDefinition(
+        id: String,
+        type: AssetValueType = AssetValueType.Store(),
+        metadata: Map<Name, Value> = mapOf(),
+        mintable: Mintable = Mintable.Infinitely(),
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.registerAssetDefinition(id.asAssetDefinitionId(), type, Metadata(metadata), mintable)
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }

To mint new assets, add the following lines of code to main

Kotlin
val madHatterAsset = "$assetDefinition$ASSET_ID_DELIMITER$madHatter"
+    sendTransaction.registerAsset(madHatterAsset, AssetValue.Quantity(100))
+        .also { println("ASSET $madHatterAsset CREATED") }
val madHatterAsset = "$assetDefinition$ASSET_ID_DELIMITER$madHatter"
+    sendTransaction.registerAsset(madHatterAsset, AssetValue.Quantity(100))
+        .also { println("ASSET $madHatterAsset CREATED") }

Then implement new method for class SendTransaction in your project.

Kotlin
suspend fun registerAsset(
+        id: String,
+        value: AssetValue,
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+        ) {
+        client.sendTransaction {
+            account(admin)
+            this.registerAsset(id.asAssetId(), value)
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+            }
+        }
suspend fun registerAsset(
+        id: String,
+        value: AssetValue,
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+        ) {
+        client.sendTransaction {
+            account(admin)
+            this.registerAsset(id.asAssetId(), value)
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+            }
+        }

To check the result, add the following line of code to the class main

Kotlin
query.findAllAssets()
+        .also { println("ALL ASSETS: \${it.map { a -> a.id.asString() }}") }
query.findAllAssets()
+        .also { println("ALL ASSETS: \${it.map { a -> a.id.asString() }}") }

Also, a new method has been added to the open class Query

Kotlin
suspend fun findAllAssets(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
+        .findAllAssets(queryFilter)
+        .account(admin)
+        .buildSigned(keyPair)
+        .let { client.sendQuery(it) }
suspend fun findAllAssets(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
+        .findAllAssets(queryFilter)
+        .account(admin)
+        .buildSigned(keyPair)
+        .let { client.sendQuery(it) }
Expand to see the expected output
DOMAIN looking_glass_1684842996549 CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, looking_glass_1684842996549, wonderland, looking_glass_1684835731653]
+ACCOUNT madHatter_1684842997930@looking_glass_1684842996549 CREATED
+ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, madHatter_1684842997930@looking_glass_1684842996549, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
+ASSET DEFINITION asset_time_1684842998891#looking_glass_1684842996549 CREATED
+ASSET asset_time_1684842998891#looking_glass_1684842996549#madHatter_1684842997930@looking_glass_1684842996549 CREATED
+ALL ASSETS: [asset_time_1684842998891#looking_glass_1684842996549#madHatter_1684842997930@looking_glass_1684842996549, cabbage#garden_of_live_flowers#alice@wonderland, rose#wonderland#alice@wonderland]\`\`\`
DOMAIN looking_glass_1684842996549 CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, looking_glass_1684842996549, wonderland, looking_glass_1684835731653]
+ACCOUNT madHatter_1684842997930@looking_glass_1684842996549 CREATED
+ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, madHatter_1684842997930@looking_glass_1684842996549, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
+ASSET DEFINITION asset_time_1684842998891#looking_glass_1684842996549 CREATED
+ASSET asset_time_1684842998891#looking_glass_1684842996549#madHatter_1684842997930@looking_glass_1684842996549 CREATED
+ALL ASSETS: [asset_time_1684842998891#looking_glass_1684842996549#madHatter_1684842997930@looking_glass_1684842996549, cabbage#garden_of_live_flowers#alice@wonderland, rose#wonderland#alice@wonderland]\`\`\`

6. Transferring assets

After we have registered and minted madHatter's assets, let's transfer some of them to another blockchain user. To do this, we will create a new user, register their asset with the main method and add transfer operations for the asset.

Kotlin
val whiteRabbit = "whiteRabbit_\${System.currentTimeMillis()}$ACCOUNT_ID_DELIMITER$domain"
+    val whiteRabbitKeyPair = generateKeyPair()
+    sendTransaction.registerAccount(whiteRabbit, listOf(whiteRabbitKeyPair.public.toIrohaPublicKey()))
+        .also { println("ACCOUNT $whiteRabbit CREATED") }
+    
+    val whiteRabbitAsset = "$assetDefinition$ASSET_ID_DELIMITER$whiteRabbit"
+    sendTransaction.registerAsset(whiteRabbitAsset, AssetValue.Quantity(0))
+        .also { println("ASSET $whiteRabbitAsset CREATED") }
+    
+    sendTransaction.transferAsset(madHatterAsset, 10, whiteRabbitAsset, madHatter.asAccountId(), madHatterKeyPair)
+        .also { println("$madHatter TRANSFERRED FROM $madHatterAsset TO $whiteRabbitAsset: 10") }
+    query.getAccountAmount(madHatter, madHatterAsset).also { println("$madHatterAsset BALANCE: $it") }
+    query.getAccountAmount(whiteRabbit, whiteRabbitAsset).also { println("$whiteRabbitAsset BALANCE: $it") }
val whiteRabbit = "whiteRabbit_\${System.currentTimeMillis()}$ACCOUNT_ID_DELIMITER$domain"
+    val whiteRabbitKeyPair = generateKeyPair()
+    sendTransaction.registerAccount(whiteRabbit, listOf(whiteRabbitKeyPair.public.toIrohaPublicKey()))
+        .also { println("ACCOUNT $whiteRabbit CREATED") }
+    
+    val whiteRabbitAsset = "$assetDefinition$ASSET_ID_DELIMITER$whiteRabbit"
+    sendTransaction.registerAsset(whiteRabbitAsset, AssetValue.Quantity(0))
+        .also { println("ASSET $whiteRabbitAsset CREATED") }
+    
+    sendTransaction.transferAsset(madHatterAsset, 10, whiteRabbitAsset, madHatter.asAccountId(), madHatterKeyPair)
+        .also { println("$madHatter TRANSFERRED FROM $madHatterAsset TO $whiteRabbitAsset: 10") }
+    query.getAccountAmount(madHatter, madHatterAsset).also { println("$madHatterAsset BALANCE: $it") }
+    query.getAccountAmount(whiteRabbit, whiteRabbitAsset).also { println("$whiteRabbitAsset BALANCE: $it") }

In the sendTransaction class, add a method for transferring assets.

Kotlin
suspend fun transferAsset(
+        from: String,
+        value: Int,
+        to: String,
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.transferAsset(from.asAssetId(), value, to.asAssetId())
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }
suspend fun transferAsset(
+        from: String,
+        value: Int,
+        to: String,
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.transferAsset(from.asAssetId(), value, to.asAssetId())
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }

To check the result of the asset transfer, add the getAccountAmount() method to the Query class:

Kotlin
suspend fun getAccountAmount(accountId: String, assetId: String): Long {
+        return QueryBuilder.findAccountById(accountId.asAccountId())
+            .account(admin)
+            .buildSigned(keyPair)
+            .let { query ->
+                client.sendQuery(query).assets[assetId.asAssetId()]?.value
+            }.let { value ->
+                value?.cast<AssetValue.Quantity>()?.u32
+            } ?: throw RuntimeException("NOT FOUND")
+    }
suspend fun getAccountAmount(accountId: String, assetId: String): Long {
+        return QueryBuilder.findAccountById(accountId.asAccountId())
+            .account(admin)
+            .buildSigned(keyPair)
+            .let { query ->
+                client.sendQuery(query).assets[assetId.asAssetId()]?.value
+            }.let { value ->
+                value?.cast<AssetValue.Quantity>()?.u32
+            } ?: throw RuntimeException("NOT FOUND")
+    }

The console output should contain similar information.

Expand to see the expected output
DOMAIN looking_glass_1684843200289 CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, looking_glass_1684843200289, looking_glass_1684842996549, wonderland, looking_glass_1684835731653]
+ACCOUNT madHatter_1684843202389@looking_glass_1684843200289 CREATED
+ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, madHatter_1684843202389@looking_glass_1684843200289, madHatter_1684842997930@looking_glass_1684842996549, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
+ASSET DEFINITION asset_time_1684843203337#looking_glass_1684843200289 CREATED
+ASSET asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 CREATED
+ACCOUNT whiteRabbit_1684843205383@looking_glass_1684843200289 CREATED
+ASSET asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289 CREATED
+madHatter_1684843202389@looking_glass_1684843200289 TRANSFERRED FROM asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 TO asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289: 10
+asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 BALANCE: 90
+asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289 BALANCE: 10
+ALL ASSETS: [asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289, cabbage#garden_of_live_flowers#alice@wonderland, rose#wonderland#alice@wonderland]
DOMAIN looking_glass_1684843200289 CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, looking_glass_1684843200289, looking_glass_1684842996549, wonderland, looking_glass_1684835731653]
+ACCOUNT madHatter_1684843202389@looking_glass_1684843200289 CREATED
+ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, madHatter_1684843202389@looking_glass_1684843200289, madHatter_1684842997930@looking_glass_1684842996549, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
+ASSET DEFINITION asset_time_1684843203337#looking_glass_1684843200289 CREATED
+ASSET asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 CREATED
+ACCOUNT whiteRabbit_1684843205383@looking_glass_1684843200289 CREATED
+ASSET asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289 CREATED
+madHatter_1684843202389@looking_glass_1684843200289 TRANSFERRED FROM asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 TO asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289: 10
+asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 BALANCE: 90
+asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289 BALANCE: 10
+ALL ASSETS: [asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289, cabbage#garden_of_live_flowers#alice@wonderland, rose#wonderland#alice@wonderland]

7. Burning assets

Burning assets is quite similar to minting them. To get started, let's add the following lines to the main() method:

Kotlin
sendTransaction.burnAssets(madHatterAsset, 10, madHatter.asAccountId(), madHatterKeyPair)
+        .also { println("\${madHatterAsset} WAS BURN") }
+
+    query.getAccountAmount(madHatter, madHatterAsset)
+        .also { println("$madHatterAsset BALANCE: $it AFTER ASSETS BURNING") }
sendTransaction.burnAssets(madHatterAsset, 10, madHatter.asAccountId(), madHatterKeyPair)
+        .also { println("\${madHatterAsset} WAS BURN") }
+
+    query.getAccountAmount(madHatter, madHatterAsset)
+        .also { println("$madHatterAsset BALANCE: $it AFTER ASSETS BURNING") }

Then implement a wrapper over the burnAssets() method in the sendTransaction class:

Kotlin
suspend fun burnAssets(
+        assetId: String,
+        value: Int,
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.burnAsset(assetId.asAssetId(), value)
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }
suspend fun burnAssets(
+        assetId: String,
+        value: Int,
+        admin: AccountId = this.admin,
+        keyPair: KeyPair = this.keyPair
+    ) {
+        client.sendTransaction {
+            account(admin)
+            this.burnAsset(assetId.asAssetId(), value)
+            buildSigned(keyPair)
+        }.also {
+            withTimeout(timeout) { it.await() }
+        }
+    }
Expand to see the expected output
DOMAIN looking_glass_1684843511587 CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, looking_glass_1684843344208, genesis, looking_glass_1684843200289, looking_glass_1684842996549, wonderland, looking_glass_1684843511587, looking_glass_1684843451130, looking_glass_1684835731653]
+ACCOUNT madHatter_1684843513272@looking_glass_1684843511587 CREATED
+ALL ACCOUNTS: [carpenter@garden_of_live_flowers, madHatter_1684843345604@looking_glass_1684843344208, whiteRabbit_1684843348692@looking_glass_1684843344208, genesis@genesis, madHatter_1684835733686@looking_glass_1684835731653]
+ASSET DEFINITION asset_time_1684843514251#looking_glass_1684843511587 CREATED
+ASSET asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 CREATED
+ACCOUNT whiteRabbit_1684843516303@looking_glass_1684843511587 CREATED
+ASSET asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587 CREATED
+madHatter_1684843513272@looking_glass_1684843511587 TRANSFERRED FROM asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 TO asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587: 10
+asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 BALANCE: 90
+asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587 BALANCE: 10
+asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 WAS BURN
+asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 BALANCE: 80 AFTER ASSETS BURNING
+ALL ASSETS: [asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587, asset_time_1684843454049#looking_glass_1684843451130#madHatter_1684843453085@looking_glass_1684843451130, asset_time_1684843454049#looking_glass_1684843451130#whiteRabbit_1684843456091@looking_glass_1684843451130]
DOMAIN looking_glass_1684843511587 CREATED
+ALL DOMAINS: [looking_glass, garden_of_live_flowers, looking_glass_1684843344208, genesis, looking_glass_1684843200289, looking_glass_1684842996549, wonderland, looking_glass_1684843511587, looking_glass_1684843451130, looking_glass_1684835731653]
+ACCOUNT madHatter_1684843513272@looking_glass_1684843511587 CREATED
+ALL ACCOUNTS: [carpenter@garden_of_live_flowers, madHatter_1684843345604@looking_glass_1684843344208, whiteRabbit_1684843348692@looking_glass_1684843344208, genesis@genesis, madHatter_1684835733686@looking_glass_1684835731653]
+ASSET DEFINITION asset_time_1684843514251#looking_glass_1684843511587 CREATED
+ASSET asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 CREATED
+ACCOUNT whiteRabbit_1684843516303@looking_glass_1684843511587 CREATED
+ASSET asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587 CREATED
+madHatter_1684843513272@looking_glass_1684843511587 TRANSFERRED FROM asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 TO asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587: 10
+asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 BALANCE: 90
+asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587 BALANCE: 10
+asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 WAS BURN
+asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 BALANCE: 80 AFTER ASSETS BURNING
+ALL ASSETS: [asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587, asset_time_1684843454049#looking_glass_1684843451130#madHatter_1684843453085@looking_glass_1684843451130, asset_time_1684843454049#looking_glass_1684843451130#whiteRabbit_1684843456091@looking_glass_1684843451130]

8. Visualizing outputs

Finally, we should talk about visualising data. The Rust API is currently the most complete in terms of available queries and instructions. After all, this is the language in which Iroha 2 was built. Kotlin, by contrast, supports only some features.

There are two possible event filters: PipelineEventFilter and DataEventFilter, we shall focus on the former. This filter sieves events pertaining to the process of submitting a transaction, executing a transaction and committing it to a block.

kotlin
import jp.co.soramitsu.iroha2.generated.datamodel.events.EventFilter.Pipeline
+import jp.co.soramitsu.iroha2.generated.datamodel.events.pipeline.EventFilter
+import jp.co.soramitsu.iroha2.generated.datamodel.events.pipeline.EntityType.Transaction
+import jp.co.soramitsu.iroha2.generated.crypto.hash.Hash
+
+val hash: ByteArray
+val eventFilter = Pipeline(EventFilter(Transaction(), Hash(hash)))
import jp.co.soramitsu.iroha2.generated.datamodel.events.EventFilter.Pipeline
+import jp.co.soramitsu.iroha2.generated.datamodel.events.pipeline.EventFilter
+import jp.co.soramitsu.iroha2.generated.datamodel.events.pipeline.EntityType.Transaction
+import jp.co.soramitsu.iroha2.generated.crypto.hash.Hash
+
+val hash: ByteArray
+val eventFilter = Pipeline(EventFilter(Transaction(), Hash(hash)))

What this short code snippet does is the following: It creates an event pipeline filter that checks if a transaction with the specified hash was submitted/rejected. This can then be used to see if the transaction we submitted was processed correctly and provide feedback to the end-user.

9. Samples in pure Java

java
package jp.co.soramitsu.iroha2;
+
+import jp.co.soramitsu.iroha2.client.Iroha2AsyncClient;
+import jp.co.soramitsu.iroha2.generated.datamodel.Value;
+import jp.co.soramitsu.iroha2.generated.datamodel.account.Account;
+import jp.co.soramitsu.iroha2.generated.datamodel.account.AccountId;
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetId;
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValue;
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValueType;
+import jp.co.soramitsu.iroha2.generated.datamodel.domain.Domain;
+import jp.co.soramitsu.iroha2.generated.datamodel.domain.DomainId;
+import jp.co.soramitsu.iroha2.generated.datamodel.metadata.Metadata;
+import jp.co.soramitsu.iroha2.generated.datamodel.name.Name;
+import jp.co.soramitsu.iroha2.generated.datamodel.transaction.VersionedSignedTransaction;
+import jp.co.soramitsu.iroha2.query.QueryAndExtractor;
+import jp.co.soramitsu.iroha2.query.QueryBuilder;
+import jp.co.soramitsu.iroha2.testengine.DefaultGenesis;
+import jp.co.soramitsu.iroha2.testengine.IrohaTest;
+import jp.co.soramitsu.iroha2.testengine.WithIroha;
+import jp.co.soramitsu.iroha2.transaction.TransactionBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static jp.co.soramitsu.iroha2.testengine.TestConstsKt.*;
+
+public class JavaTest extends IrohaTest<Iroha2AsyncClient> {
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void instructionFailed() {
+        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .fail("FAIL MESSAGE")
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<byte[]> future = client.sendTransactionAsync(transaction);
+        Assertions.assertThrows(ExecutionException.class,
+            () -> future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS)
+        );
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void registerDomainInstructionCommitted() throws ExecutionException, InterruptedException, TimeoutException {
+        final DomainId domainId = new DomainId(new Name("new_domain_name"));
+        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerDomain(domainId)
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(transaction).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Domain> query = QueryBuilder
+            .findDomainById(domainId)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Domain> future = client.sendQueryAsync(query);
+        final Domain domain = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+        Assertions.assertEquals(domain.getId(), domainId);
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void registerAccountInstructionCommitted() throws Exception {
+        final AccountId accountId = new AccountId(
+            new Name("new_account"),
+            DEFAULT_DOMAIN_ID
+        );
+        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerAccount(accountId, new ArrayList<>())
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(transaction).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Account> query = QueryBuilder
+            .findAccountById(accountId)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Account> future = client.sendQueryAsync(query);
+        final Account account = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+        Assertions.assertEquals(account.getId(), accountId);
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void mintAssetInstructionCommitted() throws Exception {
+        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Quantity())
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final VersionedSignedTransaction mintAssetTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .mintAsset(DEFAULT_ASSET_ID, 5)
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(mintAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Account> query = QueryBuilder
+            .findAccountById(ALICE_ACCOUNT_ID)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Account> future = client.sendQueryAsync(query);
+        final Account account = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+        final AssetValue value = account.getAssets().get(DEFAULT_ASSET_ID).getValue();
+        Assertions.assertEquals(5, ((AssetValue.Quantity) value).getU32());
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void updateKeyValueInstructionCommitted() throws Exception {
+        final Name assetMetadataKey = new Name("asset_metadata_key");
+        final Value.String assetMetadataValue = new Value.String("some string value");
+        final Value.String assetMetadataValue2 = new Value.String("some string value 2");
+        final Metadata metadata = new Metadata(new HashMap<Name, Value>() {{
+            put(assetMetadataKey, assetMetadataValue);
+        }});
+
+        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Store(), metadata)
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final AssetId assetId = new AssetId(DEFAULT_ASSET_DEFINITION_ID, ALICE_ACCOUNT_ID);
+        final VersionedSignedTransaction keyValueTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .setKeyValue(
+                assetId,
+                assetMetadataKey,
+                assetMetadataValue2
+            ).buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(keyValueTx).get(10, TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Value> assetDefinitionValueQuery = QueryBuilder
+            .findAssetKeyValueByIdAndKey(assetId, assetMetadataKey)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Value> future = client.sendQueryAsync(assetDefinitionValueQuery);
+
+        final Value value = future.get(10, TimeUnit.SECONDS);
+        Assertions.assertEquals(
+            ((Value.String) value).getString(),
+            assetMetadataValue2.getString()
+        );
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void setKeyValueInstructionCommitted() throws Exception {
+        final Value.String assetValue = new Value.String("some string value");
+        final Name assetKey = new Name("asset_metadata_key");
+
+        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Store())
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final VersionedSignedTransaction keyValueTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .setKeyValue(
+                DEFAULT_ASSET_DEFINITION_ID,
+                assetKey,
+                assetValue
+            ).buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(keyValueTx).get(10, TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Value> assetDefinitionValueQuery = QueryBuilder
+            .findAssetDefinitionKeyValueByIdAndKey(DEFAULT_ASSET_DEFINITION_ID, assetKey)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Value> future = client.sendQueryAsync(assetDefinitionValueQuery);
+
+        final Value value = future.get(10, TimeUnit.SECONDS);
+        Assertions.assertEquals(
+            ((Value.String) value).getString(),
+            assetValue.getString()
+        );
+    }
+}
package jp.co.soramitsu.iroha2;
+
+import jp.co.soramitsu.iroha2.client.Iroha2AsyncClient;
+import jp.co.soramitsu.iroha2.generated.datamodel.Value;
+import jp.co.soramitsu.iroha2.generated.datamodel.account.Account;
+import jp.co.soramitsu.iroha2.generated.datamodel.account.AccountId;
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetId;
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValue;
+import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValueType;
+import jp.co.soramitsu.iroha2.generated.datamodel.domain.Domain;
+import jp.co.soramitsu.iroha2.generated.datamodel.domain.DomainId;
+import jp.co.soramitsu.iroha2.generated.datamodel.metadata.Metadata;
+import jp.co.soramitsu.iroha2.generated.datamodel.name.Name;
+import jp.co.soramitsu.iroha2.generated.datamodel.transaction.VersionedSignedTransaction;
+import jp.co.soramitsu.iroha2.query.QueryAndExtractor;
+import jp.co.soramitsu.iroha2.query.QueryBuilder;
+import jp.co.soramitsu.iroha2.testengine.DefaultGenesis;
+import jp.co.soramitsu.iroha2.testengine.IrohaTest;
+import jp.co.soramitsu.iroha2.testengine.WithIroha;
+import jp.co.soramitsu.iroha2.transaction.TransactionBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static jp.co.soramitsu.iroha2.testengine.TestConstsKt.*;
+
+public class JavaTest extends IrohaTest<Iroha2AsyncClient> {
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void instructionFailed() {
+        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .fail("FAIL MESSAGE")
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<byte[]> future = client.sendTransactionAsync(transaction);
+        Assertions.assertThrows(ExecutionException.class,
+            () -> future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS)
+        );
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void registerDomainInstructionCommitted() throws ExecutionException, InterruptedException, TimeoutException {
+        final DomainId domainId = new DomainId(new Name("new_domain_name"));
+        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerDomain(domainId)
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(transaction).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Domain> query = QueryBuilder
+            .findDomainById(domainId)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Domain> future = client.sendQueryAsync(query);
+        final Domain domain = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+        Assertions.assertEquals(domain.getId(), domainId);
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void registerAccountInstructionCommitted() throws Exception {
+        final AccountId accountId = new AccountId(
+            new Name("new_account"),
+            DEFAULT_DOMAIN_ID
+        );
+        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerAccount(accountId, new ArrayList<>())
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(transaction).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Account> query = QueryBuilder
+            .findAccountById(accountId)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Account> future = client.sendQueryAsync(query);
+        final Account account = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+        Assertions.assertEquals(account.getId(), accountId);
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void mintAssetInstructionCommitted() throws Exception {
+        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Quantity())
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final VersionedSignedTransaction mintAssetTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .mintAsset(DEFAULT_ASSET_ID, 5)
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(mintAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Account> query = QueryBuilder
+            .findAccountById(ALICE_ACCOUNT_ID)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Account> future = client.sendQueryAsync(query);
+        final Account account = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+        final AssetValue value = account.getAssets().get(DEFAULT_ASSET_ID).getValue();
+        Assertions.assertEquals(5, ((AssetValue.Quantity) value).getU32());
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void updateKeyValueInstructionCommitted() throws Exception {
+        final Name assetMetadataKey = new Name("asset_metadata_key");
+        final Value.String assetMetadataValue = new Value.String("some string value");
+        final Value.String assetMetadataValue2 = new Value.String("some string value 2");
+        final Metadata metadata = new Metadata(new HashMap<Name, Value>() {{
+            put(assetMetadataKey, assetMetadataValue);
+        }});
+
+        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Store(), metadata)
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final AssetId assetId = new AssetId(DEFAULT_ASSET_DEFINITION_ID, ALICE_ACCOUNT_ID);
+        final VersionedSignedTransaction keyValueTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .setKeyValue(
+                assetId,
+                assetMetadataKey,
+                assetMetadataValue2
+            ).buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(keyValueTx).get(10, TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Value> assetDefinitionValueQuery = QueryBuilder
+            .findAssetKeyValueByIdAndKey(assetId, assetMetadataKey)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Value> future = client.sendQueryAsync(assetDefinitionValueQuery);
+
+        final Value value = future.get(10, TimeUnit.SECONDS);
+        Assertions.assertEquals(
+            ((Value.String) value).getString(),
+            assetMetadataValue2.getString()
+        );
+    }
+
+    @Test
+    @WithIroha(sources = DefaultGenesis.class)
+    public void setKeyValueInstructionCommitted() throws Exception {
+        final Value.String assetValue = new Value.String("some string value");
+        final Name assetKey = new Name("asset_metadata_key");
+
+        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Store())
+            .buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
+
+        final VersionedSignedTransaction keyValueTx = TransactionBuilder.Companion
+            .builder()
+            .account(ALICE_ACCOUNT_ID)
+            .setKeyValue(
+                DEFAULT_ASSET_DEFINITION_ID,
+                assetKey,
+                assetValue
+            ).buildSigned(ALICE_KEYPAIR);
+        client.sendTransactionAsync(keyValueTx).get(10, TimeUnit.SECONDS);
+
+        final QueryAndExtractor<Value> assetDefinitionValueQuery = QueryBuilder
+            .findAssetDefinitionKeyValueByIdAndKey(DEFAULT_ASSET_DEFINITION_ID, assetKey)
+            .account(ALICE_ACCOUNT_ID)
+            .buildSigned(ALICE_KEYPAIR);
+        final CompletableFuture<Value> future = client.sendQueryAsync(assetDefinitionValueQuery);
+
+        final Value value = future.get(10, TimeUnit.SECONDS);
+        Assertions.assertEquals(
+            ((Value.String) value).getString(),
+            assetValue.getString()
+        );
+    }
+}
`,79),c=[t];function r(E,y,i,F,d,u){return n(),a("div",null,c)}const m=s(e,[["render",r]]);export{A as __pageData,m as default}; diff --git a/assets/guide_get-started_kotlin-java.md.fd61b4c2.lean.js b/assets/guide_get-started_kotlin-java.md.fd61b4c2.lean.js new file mode 100644 index 000000000..04435decd --- /dev/null +++ b/assets/guide_get-started_kotlin-java.md.fd61b4c2.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.1293becd.js";const p="/iroha-2-docs/assets/iroha_java_hash.c677e97a.png",o="/iroha-2-docs/assets/iroha_java_commits.a73a8bf6.png",A=JSON.parse('{"title":"Kotlin/Java Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/kotlin-java.md","filePath":"guide/get-started/kotlin-java.md","lastUpdated":1700563625000}'),e={name:"guide/get-started/kotlin-java.md"},t=l("",79),c=[t];function r(E,y,i,F,d,u){return n(),a("div",null,c)}const m=s(e,[["render",r]]);export{A as __pageData,m as default}; diff --git a/assets/guide_get-started_python.md.694940f1.js b/assets/guide_get-started_python.md.694940f1.js new file mode 100644 index 000000000..1c514f7c6 --- /dev/null +++ b/assets/guide_get-started_python.md.694940f1.js @@ -0,0 +1,73 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Python 3 Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/python.md","filePath":"guide/get-started/python.md","lastUpdated":1700563625000}'),e={name:"guide/get-started/python.md"},t=o(`

Python 3 Guide

WARNING

WIP: The iroha-python SDK only works with the iroha2-lts for now. It applies both to the iroha2-edge and the iroha2 branches. Our team recommends using the iroha2-edge branch while we update the iroha2 one.

1. Iroha 2 Client Setup

There are two versions of Iroha Python to choose from. In theory, the Iroha 1 version of Iroha Python (that also has the best documentation) should be compatible with an Iroha 2 deployment.

Thus we should build and install the Iroha 2 compatible version of Iroha-python, using (for now) its GitHub repository.

Let's create a separate folder for Iroha Python and clone its GitHub repository into it:

bash
$ cd ~/Git/
+$ git clone https://github.com/hyperledger/iroha-python/ --branch iroha2
+$ cd iroha-python
$ cd ~/Git/
+$ git clone https://github.com/hyperledger/iroha-python/ --branch iroha2
+$ cd iroha-python

Iroha Python is written in Rust using the PyO3 library. Thus, unlike most Python packages, you must build it first:

bash
$ pip install maturin
+$ maturin build
$ pip install maturin
+$ maturin build

After the build is complete, install it into your system:

bash
$ pip install ./target/wheels/iroha_python-*.whl
$ pip install ./target/wheels/iroha_python-*.whl

Finally, you will need a working client configuration:

bash
$ cp -vfr ~/Git/iroha/configs/client/config.json example/config.json
$ cp -vfr ~/Git/iroha/configs/client/config.json example/config.json

TIP

You can also use the provided config.json in the example folder if you also call docker-compose up from that same folder. This has to do with the fact that the configuration for the Docker files in Iroha Python is slightly different.

2. Configuring Iroha 2

Unlike iroha_client_cli, finding the configuration file in a scripting language is the responsibility of the person writing the script. The easiest method is to de-serialise a dictionary type from the provided config.json.

This is an example of how you could do that in Python:

python
import json
+from iroha2 import Client
+
+cfg = json.loads(open("config.json").read())
+cl = Client(cfg)
import json
+from iroha2 import Client
+
+cfg = json.loads(open("config.json").read())
+cl = Client(cfg)

If the configuration file is malformed, you can expect an exception to notify you. However, the client doesn't do any verification: if the account used in config.json is not in the blockchain or has the wrong private key, you won't know that until you try and execute a simple instruction. More on that in the following section.

INFO

It should also be noted that Iroha Python is under heavy development. It severely lacks in documentation and its API can be made more idiomatically Python.

3. Registering a Domain

It is important to remember that Iroha Python is wrapping Rust code. As such, many of Python idioms are not yet accommodated; for example, there's no duck-typing of the Register instruction.

python
from iroha2.data_model.isi import *
+from iroha2.data_model.domain import *
+
+domain = Domain("looking_glass")
+register = Register(Expression(Value(Identifiable(domain))))
from iroha2.data_model.isi import *
+from iroha2.data_model.domain import *
+
+domain = Domain("looking_glass")
+register = Register(Expression(Value(Identifiable(domain))))

Instead, we are creating a domain and wrapping it in multiple type-erasing constructs. A domain is wrapped in Identifiable (which would be a trait in Rust), which is wrapped in Value, which is wrapped in Expression, which finally is wrapped in the Register instruction. This is not entirely against Python conventions (it is strongly typed, after all), and not entirely counter-intuitive, once you see the corresponding Rust code.

The instruction to register must be submitted, in order for anything to happen.

python
hash = cl.submit_isi(register)
hash = cl.submit_isi(register)

Note that we also keep track of the hash of the transaction. This will become useful when you visualize the output.

4. Registering an Account

Registering an account is similar to the process of registering a domain, except the wrapping structures are different. There are a couple of things to watch out for.

First of all, we can only register an account to an existing domain. The best UX design practices dictate that you should check if the requested domain exists now, and if it doesn't, suggest a fix to the user.

python
from iroha2.data_model.isi import *
+from iroha2.data_model.account import *
+
+public_key =# Get this from white_rabbit.
+bunny = Account("white_rabbit@looking_glass", signatories=[public_key])
+register = Register(Expression(Value(Identifiable(bunny))))
from iroha2.data_model.isi import *
+from iroha2.data_model.account import *
+
+public_key =# Get this from white_rabbit.
+bunny = Account("white_rabbit@looking_glass", signatories=[public_key])
+register = Register(Expression(Value(Identifiable(bunny))))

Second, you should provide the account with a public key. It is tempting to generate both the public and the private key at this time, but it isn't the brightest idea. Remember that the white_rabbit trusts you, alice@wonderland, to create an account for them in the domain looking_glass, but doesn't want you to have access to that account after creation.

If you gave white_rabbit a key that you generated yourself, how would they know if you don't have a copy of their private key? Instead, the best way is to ask white_rabbit to generate a new key-pair, and then give you the public half of it.

After putting all of this together, we submit it as before:

python
hash = cl.submit_isi(register)
hash = cl.submit_isi(register)

5. Registering and minting assets

Iroha has been built with few underlying assumptions about what the assets need to be in terms of their value type and characteristics (fungible or non-fungible, mintable or non-mintable).

Asset creation is by far the most cumbersome:

python
import iroha2.data_model.asset as asset
+from iroha2.sys.iroha_data_model import Value
+
+time = asset.Definition(
+    value_type=asset.ValueType.Quantity,
+    id=asset.DefinitionId(name="time", domain_name="looking_glass"),
+    metadata={"a": Value.U32(10)},
+    mintable=False
+)
import iroha2.data_model.asset as asset
+from iroha2.sys.iroha_data_model import Value
+
+time = asset.Definition(
+    value_type=asset.ValueType.Quantity,
+    id=asset.DefinitionId(name="time", domain_name="looking_glass"),
+    metadata={"a": Value.U32(10)},
+    mintable=False
+)

Note the following; First, we used the **kwargs syntax to make everything more explicit.

We have a value_type which must be specified. Python is duck-typed, while Rust isn't. Make sure that you track the types diligently, and make use of mypy annotations.

The Quantity value type is an internal 32-bit unsigned integer. Your other options are BigQuantity, which is a 128-bit unsigned integer, and Fixed. All of these are unsigned. Any checked operation with a negative Fixed value (one that you got by converting a negative floating-point number), will result in an error.

Continuing the theme of explicit typing, the asset.DefinitionId is its own type. We could have also written asset.DefinitionId.parse("time#looking_glass"), but making sure that you know what's going on is more useful in this case.

Finally, we have mintable. By default this is set to True, however, setting it to False means that any attempt to mint more of time#looking_glass is doomed to fail. Unfortunately, since we didn't add any time at genesis, the white_rabbit will never have time. There just isn't any in his domain, and more can't be minted.

OK. So how about a mint demonstration? Fortunately, alice@wonderland has an asset called roses#wonderland, which can be minted. For that we need to do something much simpler.

python
amount = Expression(Value(U32(42)))
+destination = Expression(Value(Identifiable(asset.DefinitionId.parse("rose#wonderland"))))
+mint_amount = Mint(amount, destination)
+cl.submit_isi(mint_amount)
amount = Expression(Value(U32(42)))
+destination = Expression(Value(Identifiable(asset.DefinitionId.parse("rose#wonderland"))))
+mint_amount = Mint(amount, destination)
+cl.submit_isi(mint_amount)

This would add 42 to the current tally of roses that Alice has.

6. Visualizing outputs

The paradigm that Iroha chose to allow monitoring some events is the filter-map paradigm. Let's look at what we need to do in order to know e.g. what happened to a submitted instruction.

First, we'll need to remember the hash of the transaction that we want to track, next we create a filter:

python
filter = EventFilter.Pipeline(
+    pipeline.EventFilter(
+        entity=pipeline.EntityType.Transaction(),
+        hash=None,
+    ))
filter = EventFilter.Pipeline(
+    pipeline.EventFilter(
+        entity=pipeline.EntityType.Transaction(),
+        hash=None,
+    ))

And add a listener on that filter. Don't worry, the Rust side of the process is asynchronous, so barring issues with the GIL, you won't lock up your interpreter.

Note the types. The EventFilter is a type that filters out anything that isn't an event (and non-event types are beyond the scope of this tutorial). The pipeline module helps us by providing a concrete type of EventFilter, namely one that listens for transactions. Note that we haven't used the hash here.

Finally, we add a listening filter to the client:

python
listener = cl.listen(filter)
listener = cl.listen(filter)

Now we must actually listen for events:

python
for event in listener:
+    print(event)
+
+    if event["Pipeline"]["status"] == "Committed" \\
+        and event["Pipeline"]["hash"] == hash:
+        break
for event in listener:
+    print(event)
+
+    if event["Pipeline"]["status"] == "Committed" \\
+        and event["Pipeline"]["hash"] == hash:
+        break

And now, we have an infinite loop that will not quit until the event gets committed.

WARNING

Nobody should do this in production code, and instead monitor the event queue for (at least) the possibility that the transaction gets Rejected.

`,59),l=[t];function p(i,c,r,y,d,h){return a(),n("div",null,l)}const g=s(e,[["render",p]]);export{u as __pageData,g as default}; diff --git a/assets/guide_get-started_python.md.694940f1.lean.js b/assets/guide_get-started_python.md.694940f1.lean.js new file mode 100644 index 000000000..a594bf4bb --- /dev/null +++ b/assets/guide_get-started_python.md.694940f1.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Python 3 Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/python.md","filePath":"guide/get-started/python.md","lastUpdated":1700563625000}'),e={name:"guide/get-started/python.md"},t=o("",59),l=[t];function p(i,c,r,y,d,h){return a(),n("div",null,l)}const g=s(e,[["render",p]]);export{u as __pageData,g as default}; diff --git a/assets/guide_get-started_quick-start.md.90fe4bdf.js b/assets/guide_get-started_quick-start.md.90fe4bdf.js new file mode 100644 index 000000000..7e9d7a177 --- /dev/null +++ b/assets/guide_get-started_quick-start.md.90fe4bdf.js @@ -0,0 +1,63 @@ +import{_ as s,o as a,c as e,Q as n}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Quick Start with Docker","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/quick-start.md","filePath":"guide/get-started/quick-start.md","lastUpdated":1700563625000}'),o={name:"guide/get-started/quick-start.md"},l=n(`

Quick Start with Docker

  1. Install the prerequisites:

  2. Install Iroha from GitHub.

  3. Run docker-compose to bring up a network of 4 containerised peers:

    bash
    $ docker-compose up
    $ docker-compose up

    Depending on your set-up, this might either pull the image off of DockerHub, or build the container locally. After this process is complete, you'll be greeted with,

    [+] Running 4/0
    + ⠿ Container iroha-iroha2-1  Created                                         0.0s
    + ⠿ Container iroha-iroha0-1  Created                                         0.0s
    + ⠿ Container iroha-iroha3-1  Created                                         0.0s
    + ⠿ Container iroha-iroha1-1  Created                                         0.0s
    +Attaching to iroha-iroha0-1, iroha-iroha1-1, iroha-iroha2-1, iroha-iroha3-1
    +iroha-iroha1-1  | 2023-02-27T12:43:08.540651Z  INFO iroha: Hyperledgerいろは2にようこそ!
    +iroha-iroha1-1  | 2023-02-27T12:43:08.542379Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
    +iroha-iroha1-1  | 2023-02-27T12:43:08.542906Z  INFO iroha: Starting peer listen_addr=iroha1:1338
    +iroha-iroha1-1  | 2023-02-27T12:43:08.543188Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha1:1338
    +iroha-iroha1-1  | 2023-02-27T12:43:08.551356Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha1:1338
    +iroha-iroha1-1  | 2023-02-27T12:43:08.569289Z  INFO iroha_core::kura: Loaded 0 blocks at init.
    +iroha-iroha1-1  | 2023-02-27T12:43:08.572457Z ERROR iroha: Telemetry did not start
    +iroha-iroha1-1  | 2023-02-27T12:43:08.594190Z  INFO iroha: Starting Iroha
    +iroha-iroha2-1  | 2023-02-27T12:43:08.698491Z  INFO iroha: Hyperledgerいろは2にようこそ!
    +iroha-iroha2-1  | 2023-02-27T12:43:08.700998Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
    +iroha-iroha2-1  | 2023-02-27T12:43:08.701624Z  INFO iroha: Starting peer listen_addr=iroha2:1339
    +iroha-iroha2-1  | 2023-02-27T12:43:08.701895Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha2:1339
    +iroha-iroha2-1  | 2023-02-27T12:43:08.707759Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha2:1339
    +iroha-iroha0-1  | 2023-02-27T12:43:08.719683Z  INFO iroha: Hyperledgerいろは2にようこそ!
    +iroha-iroha0-1  | 2023-02-27T12:43:08.722029Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
    +iroha-iroha2-1  | 2023-02-27T12:43:08.730201Z  INFO iroha_core::kura: Loaded 1 blocks at init.
    +iroha-iroha2-1  | 2023-02-27T12:43:08.746975Z ERROR iroha: Telemetry did not start
    +iroha-iroha0-1  | 2023-02-27T12:43:08.748879Z  INFO iroha: Starting peer listen_addr=iroha0:1337
    +iroha-iroha0-1  | 2023-02-27T12:43:08.749155Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha0:1337
    +iroha-iroha0-1  | 2023-02-27T12:43:08.754613Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha0:1337
    +iroha-iroha3-1  | 2023-02-27T12:43:08.753230Z  INFO iroha: Hyperledgerいろは2にようこそ!
    +iroha-iroha3-1  | 2023-02-27T12:43:08.754934Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
    +iroha-iroha3-1  | 2023-02-27T12:43:08.755503Z  INFO iroha: Starting peer listen_addr=iroha3:1340
    +iroha-iroha3-1  | 2023-02-27T12:43:08.755802Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha3:1340
    +iroha-iroha3-1  | 2023-02-27T12:43:08.760437Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha3:1340
    +iroha-iroha2-1  | 2023-02-27T12:43:08.763216Z  INFO iroha: Starting Iroha
    [+] Running 4/0
    + ⠿ Container iroha-iroha2-1  Created                                         0.0s
    + ⠿ Container iroha-iroha0-1  Created                                         0.0s
    + ⠿ Container iroha-iroha3-1  Created                                         0.0s
    + ⠿ Container iroha-iroha1-1  Created                                         0.0s
    +Attaching to iroha-iroha0-1, iroha-iroha1-1, iroha-iroha2-1, iroha-iroha3-1
    +iroha-iroha1-1  | 2023-02-27T12:43:08.540651Z  INFO iroha: Hyperledgerいろは2にようこそ!
    +iroha-iroha1-1  | 2023-02-27T12:43:08.542379Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
    +iroha-iroha1-1  | 2023-02-27T12:43:08.542906Z  INFO iroha: Starting peer listen_addr=iroha1:1338
    +iroha-iroha1-1  | 2023-02-27T12:43:08.543188Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha1:1338
    +iroha-iroha1-1  | 2023-02-27T12:43:08.551356Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha1:1338
    +iroha-iroha1-1  | 2023-02-27T12:43:08.569289Z  INFO iroha_core::kura: Loaded 0 blocks at init.
    +iroha-iroha1-1  | 2023-02-27T12:43:08.572457Z ERROR iroha: Telemetry did not start
    +iroha-iroha1-1  | 2023-02-27T12:43:08.594190Z  INFO iroha: Starting Iroha
    +iroha-iroha2-1  | 2023-02-27T12:43:08.698491Z  INFO iroha: Hyperledgerいろは2にようこそ!
    +iroha-iroha2-1  | 2023-02-27T12:43:08.700998Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
    +iroha-iroha2-1  | 2023-02-27T12:43:08.701624Z  INFO iroha: Starting peer listen_addr=iroha2:1339
    +iroha-iroha2-1  | 2023-02-27T12:43:08.701895Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha2:1339
    +iroha-iroha2-1  | 2023-02-27T12:43:08.707759Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha2:1339
    +iroha-iroha0-1  | 2023-02-27T12:43:08.719683Z  INFO iroha: Hyperledgerいろは2にようこそ!
    +iroha-iroha0-1  | 2023-02-27T12:43:08.722029Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
    +iroha-iroha2-1  | 2023-02-27T12:43:08.730201Z  INFO iroha_core::kura: Loaded 1 blocks at init.
    +iroha-iroha2-1  | 2023-02-27T12:43:08.746975Z ERROR iroha: Telemetry did not start
    +iroha-iroha0-1  | 2023-02-27T12:43:08.748879Z  INFO iroha: Starting peer listen_addr=iroha0:1337
    +iroha-iroha0-1  | 2023-02-27T12:43:08.749155Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha0:1337
    +iroha-iroha0-1  | 2023-02-27T12:43:08.754613Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha0:1337
    +iroha-iroha3-1  | 2023-02-27T12:43:08.753230Z  INFO iroha: Hyperledgerいろは2にようこそ!
    +iroha-iroha3-1  | 2023-02-27T12:43:08.754934Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
    +iroha-iroha3-1  | 2023-02-27T12:43:08.755503Z  INFO iroha: Starting peer listen_addr=iroha3:1340
    +iroha-iroha3-1  | 2023-02-27T12:43:08.755802Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha3:1340
    +iroha-iroha3-1  | 2023-02-27T12:43:08.760437Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha3:1340
    +iroha-iroha2-1  | 2023-02-27T12:43:08.763216Z  INFO iroha: Starting Iroha
  4. Follow the Bash tutorial to check out Iroha's capabilities.

  5. When you're done with the test network, just hit Control + C to stop the containers (⌃ + C on Mac).

Docker Options

You might also be interested in other options for local compilation:

  • For testing Iroha code quickly, you can use docker-compose-single.yml, which starts a container with a single peer.
  • For testing Iroha code in normal conditions, you can use docker-compose-local.yml, which starts 4 connected containers with peers.

INFO

Please note that there is ongoing work to make our configurations for Docker even more customizable with the help of Swarm.

`,6),p=[l];function r(t,c,i,y,h,d){return a(),e("div",null,p)}const k=s(o,[["render",r]]);export{g as __pageData,k as default}; diff --git a/assets/guide_get-started_quick-start.md.90fe4bdf.lean.js b/assets/guide_get-started_quick-start.md.90fe4bdf.lean.js new file mode 100644 index 000000000..7cd8902a0 --- /dev/null +++ b/assets/guide_get-started_quick-start.md.90fe4bdf.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as e,Q as n}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Quick Start with Docker","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/quick-start.md","filePath":"guide/get-started/quick-start.md","lastUpdated":1700563625000}'),o={name:"guide/get-started/quick-start.md"},l=n("",6),p=[l];function r(t,c,i,y,h,d){return a(),e("div",null,p)}const k=s(o,[["render",r]]);export{g as __pageData,k as default}; diff --git a/assets/guide_get-started_rust.md.bea2cc81.js b/assets/guide_get-started_rust.md.bea2cc81.js new file mode 100644 index 000000000..dd1df57d1 --- /dev/null +++ b/assets/guide_get-started_rust.md.bea2cc81.js @@ -0,0 +1,183 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Rust Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/rust.md","filePath":"guide/get-started/rust.md","lastUpdated":1700563625000}'),l={name:"guide/get-started/rust.md"},e=o(`

Rust Guide

1. Iroha 2 Client Setup

In this part we shall cover the process of using the Iroha 2 Rust libraries. Instead of providing the complete basics, we shall assume knowledge of the most widely used concepts, explain what's unusual about Iroha 2 specifically, and provide a step-by-step guide to creating your own Rust client for it.

We assume that you know how to create a new package and have basic understanding of the fundamental Rust code; async functions, enum types, traits and borrowing/ownership, as well as the libraries that we use: serde, tokio, tracing, etc.

TIP

If you don't feel comfortable with any of the above, we recommend consulting the Rust book and docs.rs.

Iroha 2 makes extensive use of workspaces. Currently, there are two workspaces, the one that contains the WASM support library and the one that contains the core support libraries, which go in a domain-first order. What that means is that instead of having a global constants crate, we have a crate for the blockchain data model (iroha_data_model), a crate with cryptographic primitives (iroha_crypto), and so on. These, individually, have a module for constants.

If you add iroha_client to the other two crates, you get the minimum number of dependencies to start your own client, similar to iroha_client_cli.

Once the initial v2.0.0 release is complete, we plan to create a package on crates.io with all the documentation. In the meantime, you could use the local copy that you've just created in the previous step as a local installation in your client's Cargo.toml:

toml
[dependencies]
+iroha_client = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/client" }
+iroha_data_model = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/data_model" }
+iroha_crypto = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/crypto" }
+iroha_config = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/config" }
[dependencies]
+iroha_client = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/client" }
+iroha_data_model = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/data_model" }
+iroha_crypto = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/crypto" }
+iroha_config = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/config" }

The added benefit of using a local copy is that you have access to the minimal BFT network in the form of docker-compose.yml, which allows you to experiment. The drawbacks are mitigated by the fact that Rust links statically by default, so we recommend you experiment with the local set up first.

INFO

You could also make use of our test_network crate, which is available via GitHub but not via crates.io.

You would also benefit from having immediate access to the example configurations in the ~/Git/iroha/configs folder.

So let's copy the example client configuration somewhere useful:

bash
$ cp -vfr ~/Git/iroha/configs/client/config.json example/config.json
$ cp -vfr ~/Git/iroha/configs/client/config.json example/config.json

We recommend looking through it to familiarise yourself with the key pieces of information that every Iroha 2 client needs. Specifically, each client operates on behalf of a pre-existing account. These accounts are identified by a name@domain_name ID and can only be accessed provided that you know their specific key.

2. Configuring Iroha 2

Your application written in Rust needs to instantiate a client. The client typically needs specific configuration options, which you could either generate or load from the provided config.json. Let's do that now:

rs
use iroha_config::client::Configuration;
+use iroha_data_model::TryToValue;
use iroha_config::client::Configuration;
+use iroha_data_model::TryToValue;
rs
let config_loc = "../configs/client/config.json";
+let file = File::open(config_loc)
+    .wrap_err("Unable to load the configuration file at \`.....\`")
+    .expect("Config file is loading normally.");
+let config: Configuration = serde_json::from_reader(file)
+    .wrap_err("Failed to parse \`../configs/client/config.json\`")
+    .expect("Verified in tests");
let config_loc = "../configs/client/config.json";
+let file = File::open(config_loc)
+    .wrap_err("Unable to load the configuration file at \`.....\`")
+    .expect("Config file is loading normally.");
+let config: Configuration = serde_json::from_reader(file)
+    .wrap_err("Failed to parse \`../configs/client/config.json\`")
+    .expect("Verified in tests");

Using said configuration, instantiate a client:

rs
// Create an Iroha client
+let iroha_client: Client = Client::new(&config)?;
// Create an Iroha client
+let iroha_client: Client = Client::new(&config)?;

Note that it used to be necessary to create a mutable client. Sending and receiving messages affects the client's internal state, but now that state is hidden behind interior mutable smart pointers.

Of course, depending on your application, you might want to de-serialise your ClientConfiguration structure from a different location. Perhaps, you might want to build the configuration in place using the command-line arguments, or perhaps, you're using the XDG specification to store the file persistently in a different location. For this purpose, it's useful to try and construct an instance of ClientConfiguration:

rust
use iroha_core::prelude::*;
+use iroha_data_model::prelude::*;
+
+let kp = KeyPair::new(
+    PublicKey::from_str(
+        r#"ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"#,
+    )?,
+    PrivateKey::from_hex(
+        Algorithm::Ed25519,
+        "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
+            .into(),
+    )?
+)?;
+
+let (public_key, private_key) = kp.clone().into();
+let account_id: AccountId = "alice@wonderland".parse()?;
+
+let config = ClientConfiguration {
+    public_key,
+    private_key,
+    account_id,
+    torii_api_url: SmallStr::from_string(iroha_config::torii::uri::DEFAULT_API_URL.to_owned()),
+    ..ClientConfiguration::default()
+};
use iroha_core::prelude::*;
+use iroha_data_model::prelude::*;
+
+let kp = KeyPair::new(
+    PublicKey::from_str(
+        r#"ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"#,
+    )?,
+    PrivateKey::from_hex(
+        Algorithm::Ed25519,
+        "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
+            .into(),
+    )?
+)?;
+
+let (public_key, private_key) = kp.clone().into();
+let account_id: AccountId = "alice@wonderland".parse()?;
+
+let config = ClientConfiguration {
+    public_key,
+    private_key,
+    account_id,
+    torii_api_url: SmallStr::from_string(iroha_config::torii::uri::DEFAULT_API_URL.to_owned()),
+    ..ClientConfiguration::default()
+};

INFO

Note that the keys in client configuration are given in multi-hash format. If you are experiencing issues parsing the keys in this format, check the troubleshooting section.

3. Registering a Domain

Registering a domain is a relatively easy operation. Most of the boilerplate code has to do with setting up the Iroha 2 client and deserialising its configuration.

rs
use iroha_client::client::Client;
+use iroha_data_model::{
+    metadata::UnlimitedMetadata,
+    prelude::{Domain, DomainId, InstructionBox, RegisterBox},
+};
use iroha_client::client::Client;
+use iroha_data_model::{
+    metadata::UnlimitedMetadata,
+    prelude::{Domain, DomainId, InstructionBox, RegisterBox},
+};

To register a domain, you need the domain name:

rs
// Create a domain Id
+let looking_glass: DomainId = "looking_glass".parse()?;
// Create a domain Id
+let looking_glass: DomainId = "looking_glass".parse()?;

Which we convert into an instruction:

rs
// Create an ISI
+let create_looking_glass = RegisterBox::new(Domain::new(looking_glass));
// Create an ISI
+let create_looking_glass = RegisterBox::new(Domain::new(looking_glass));

Note that we use RegisterBox and IdentifiableBox. Despite what your instincts as a Rust developer might suggest, we're not actually using any kind of dynamic dispatch. There's no dyn anywhere, and RegisterBox isn't an alias for Box<dyn Register>.

A RegisterBox is a specialised enum that uses static dispatch to achieve what looks like dynamic dispatch, without any heap allocation. If you want to add more types to RegisterBox you must either open an issue on GitHub, or do that by yourself on a local fork of Iroha.

The instruction is then batched into a transaction:

rs
// Prepare a transaction
+let metadata = UnlimitedMetadata::default();
+let instructions: Vec<InstructionBox> = vec![create_looking_glass.into()];
+let tx = iroha_client
+    .build_transaction(instructions, metadata)
+    .wrap_err("Error building a domain registration transaction")?;
// Prepare a transaction
+let metadata = UnlimitedMetadata::default();
+let instructions: Vec<InstructionBox> = vec![create_looking_glass.into()];
+let tx = iroha_client
+    .build_transaction(instructions, metadata)
+    .wrap_err("Error building a domain registration transaction")?;

Which is then submitted into the pipeline:

rs
// Submit a prepared domain registration transaction
+iroha_client
+    .submit_transaction(&tx)
+    .wrap_err("Failed to submit transaction")?;
// Submit a prepared domain registration transaction
+iroha_client
+    .submit_transaction(&tx)
+    .wrap_err("Failed to submit transaction")?;

Note the question mark here. This will return an Err variant if there's something immediately and obviously wrong with the transaction: for example, if it couldn't submit the transaction to the peer (e.g. there's no connection), or if the transaction got rejected with an error. The cost is that the submit_transaction function is synchronous.

We could have also done the following:

rust
iroha_client
+    .submit_with_metadata(create_looking_glass, UnlimitedMetadata::default())?;
iroha_client
+    .submit_with_metadata(create_looking_glass, UnlimitedMetadata::default())?;

or

rust
iroha_client.submit(create_looking_glass)?;
iroha_client.submit(create_looking_glass)?;

The latter style is just syntactic sugar. Every submission comes in the form of a transaction that has metadata.

While the latter is a convenient shorthand that we shall use frequently, we strongly advise using explicit construction in production code.

INFO

It is likely that we shall replace most if not all instances of submit in our code base with explicit transactions.

4. Registering an Account

Registering an account is a bit more involved than registering a domain. With a domain, the only concern is the domain name. However, with an account, there are a few more things to worry about.

First of all, we need to create an AccountId. Note that we can only register an account to an existing domain. The best UX design practices dictate that you should check if the requested domain exists now, and if it doesn't, suggest a fix to the user. After that, we can create a new account named white_rabbit.

rs
use iroha_data_model::prelude::AccountId;
+
+// Create an \`iroha_data_model::AccountId\` instance
+// with a DomainId instance and a Domain ID for an account
+let longhand_account_id = AccountId::new("white_rabbit".parse()?, "looking_glass".parse()?);
+let account_id: AccountId = "white_rabbit@looking_glass"
+    .parse()
+    .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after");
+
+// Check that two ways to define an account match
+assert_eq!(account_id, longhand_account_id);
use iroha_data_model::prelude::AccountId;
+
+// Create an \`iroha_data_model::AccountId\` instance
+// with a DomainId instance and a Domain ID for an account
+let longhand_account_id = AccountId::new("white_rabbit".parse()?, "looking_glass".parse()?);
+let account_id: AccountId = "white_rabbit@looking_glass"
+    .parse()
+    .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after");
+
+// Check that two ways to define an account match
+assert_eq!(account_id, longhand_account_id);

Second, you should provide the account with a public key. It is tempting to generate both it and the private key at this time, but it isn't the brightest idea. Remember, that the white_rabbit trusts you, alice@wonderland, to create an account for them in the domain _looking_glass, but doesn't want you to have access to that account after creation.

If you gave white_rabbit a key that you generated yourself, how would they know if you don't have a copy of their private key? Instead, the best way is to ask white_rabbit to generate a new key-pair, and give you the public half of it:

rust
let key: PublicKey = get_key_from_white_rabbit();
let key: PublicKey = get_key_from_white_rabbit();

Only then do we build an instruction from it:

rust
let create_account =
+    RegisterBox::new(IdentifiableBox::from(NewAccount::with_signatory(id, key)));
let create_account =
+    RegisterBox::new(IdentifiableBox::from(NewAccount::with_signatory(id, key)));

Which is then wrapped in a transaction and submitted to the peer as in the previous section.

5. Registering and minting assets

Iroha has been built with few underlying assumptions about what the assets need to be in terms of their value type and characteristics (fungible or non-fungible, mintable or non-mintable).

To register an asset, we first construct an iroha_data_model::asset::DefinitionId like so:

rs
// Create an asset
+let asset_def_id = AssetDefinitionId::from_str("time#looking_glass")
+    .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after");
// Create an asset
+let asset_def_id = AssetDefinitionId::from_str("time#looking_glass")
+    .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after");

INFO

Note that we use # symbol to separate the name of the asset from the domain to which it belongs. This is intentional. This reflects the rule that there can be many alices in many domains, with only one alice per domain, and there can be an asset that is also named alice, but there can be only one, regardless of type.

Then construct an instruction:

rs
// Initialise the registration time
+let register_time =
+    RegisterBox::new(AssetDefinition::fixed(asset_def_id.clone()).mintable_once());
+
+// Submit a registration time
+iroha_client.submit(register_time)?;
// Initialise the registration time
+let register_time =
+    RegisterBox::new(AssetDefinition::fixed(asset_def_id.clone()).mintable_once());
+
+// Submit a registration time
+iroha_client.submit(register_time)?;

This creates an asset time that can only be minted once and has the type fixed. AssetDefinition::fixed just like its other cousins (quantity and big_quantity) returns a builder of an AssetDefinition.

This asset is mintable_once, which means that the next time we mint it, we have to specify the entire amount that is going to exist for the rest of the existence of the blockchain.

rs
// Create a MintBox using a previous asset and account
+let mint = MintBox::new(
+    12.34_f64.try_to_value()?,
+    IdBox::AssetId(AssetId::new(asset_def_id, account_id)),
+);
+
+// Submit a minting transaction
+iroha_client.submit_all([mint])?;
// Create a MintBox using a previous asset and account
+let mint = MintBox::new(
+    12.34_f64.try_to_value()?,
+    IdBox::AssetId(AssetId::new(asset_def_id, account_id)),
+);
+
+// Submit a minting transaction
+iroha_client.submit_all([mint])?;

Now imagine that the white_rabbit@looking_glass was not very keen and didn't notice that he wanted 123.4_f64 as the amount of time. Now white rabbit notices the problem and thinks "oh dear, not a lot of time has passed, perhaps I can give myself some more", and submits another mint request with 111.06_f64 instead of the original 12.34_f64. But, alas, no such luck. The white rabbit cannot mint more time and is thus perpetually late.

Roses, by contrast, are already registered in the network during the genesis round, and belong to alice@wonderland. Moreover, when they were registered, we didn't add the restriction, so we can mint them again and again as alice:

rs
let mint_roses = MintBox::new(
+    42_u32.to_value(),
+    IdBox::AssetId(AssetId::new(roses, alice)),
+);
let mint_roses = MintBox::new(
+    42_u32.to_value(),
+    IdBox::AssetId(AssetId::new(roses, alice)),
+);

Then submit the instruction as usual:

rs
iroha_client
+    .submit(mint_roses)
+    .wrap_err("Failed to submit transaction")?;
iroha_client
+    .submit(mint_roses)
+    .wrap_err("Failed to submit transaction")?;

INFO

Our assets are strongly typed. As such, when you create a MintBox, you need to check that the asset has the correct underlying type. If you don't know the type, query it. This is also why we specifically annotate numerical literals with their type.

Contrary to what you might think, this restriction isn't just for pedantry. Implicit conversion errors are the bane of all programmers, if you got the AssetValueType incorrect, how do you know that it was the only mistake in that transaction?

6. Burning assets

Burning assets is quite similar to minting. First, you create the burn instruction indicating which asset to burn and its quantity.

rs
// Burn the Asset instance
+let burn_roses = BurnBox::new(
+    10_u32.to_value(),
+    IdBox::AssetId(AssetId::new(roses, alice)),
+);
// Burn the Asset instance
+let burn_roses = BurnBox::new(
+    10_u32.to_value(),
+    IdBox::AssetId(AssetId::new(roses, alice)),
+);

Then submit this instruction:

rust
iroha_client.submit(burn_roses)?;
iroha_client.submit(burn_roses)?;

7. Visualising outputs

Finally, we should talk about visualising data. The Rust API is currently the most complete in terms of available queries and instructions. After all, this is the language in which Iroha 2 was built.

We shall, however, leave most of the aforementioned advanced features down the rabbit hole, up to the reader's own devices to discover. This document can easily get out of sync with the state of the API features. By contrast, the online documentation is always up to date. Plus a short tutorial wouldn't be able to do all these features justice. Instead, we shall retain parity with other language tutorials and introduce you to pipeline filters.

There are two possible event filters: PipelineEventFilter and DataEventFilter, we shall focus on the former. This filter sieves events pertaining to the process of submitting a transaction, executing a transaction, and committing it to a block.

First, let's build a filter:

rust
use iroha_data_model::prelude::*;
+
+let filter = FilterBox::Pipeline(PipelineEventFilter::identity());
use iroha_data_model::prelude::*;
+
+let filter = FilterBox::Pipeline(PipelineEventFilter::identity());

Then, we start listening for events in an infinite loop:

rust
for event in iroha_client.listen_for_events(filter)? {
+    match event {
+        Ok(event) => println!("Success: {:#?}", event),
+        Err(err) => println!("Sadness:( {:#?}",  err),
+    }
+};
for event in iroha_client.listen_for_events(filter)? {
+    match event {
+        Ok(event) => println!("Success: {:#?}", event),
+        Err(err) => println!("Sadness:( {:#?}",  err),
+    }
+};

Needless to say, an synchronous infinite blocking loop is bad UX for anything but a command-line program, but for illustration purposes, this would create a nice printout, just like in iroha_client_cli.

`,86),p=[e];function t(c,r,i,y,E,d){return a(),n("div",null,p)}const F=s(l,[["render",t]]);export{h as __pageData,F as default}; diff --git a/assets/guide_get-started_rust.md.bea2cc81.lean.js b/assets/guide_get-started_rust.md.bea2cc81.lean.js new file mode 100644 index 000000000..a744b2aa5 --- /dev/null +++ b/assets/guide_get-started_rust.md.bea2cc81.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Rust Guide","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/rust.md","filePath":"guide/get-started/rust.md","lastUpdated":1700563625000}'),l={name:"guide/get-started/rust.md"},e=o("",86),p=[e];function t(c,r,i,y,E,d){return a(),n("div",null,p)}const F=s(l,[["render",t]]);export{h as __pageData,F as default}; diff --git a/assets/guide_get-started_tutorials.md.dee1ad88.js b/assets/guide_get-started_tutorials.md.dee1ad88.js new file mode 100644 index 000000000..43f57be5a --- /dev/null +++ b/assets/guide_get-started_tutorials.md.dee1ad88.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as a,Q as o}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Introduction","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/tutorials.md","filePath":"guide/get-started/tutorials.md","lastUpdated":1700563625000}'),i={name:"guide/get-started/tutorials.md"},r=o('

Introduction

Welcome to the Hyperledger Iroha 2 tutorial. This document is designed to help you get started with Iroha 2, regardless of your knowledge of Hyperledger technology, coding experience or familiarity with blockchains.

Preamble

This tutorial is suitable for both experienced developers, prospective users, and people casually curious about blockchain technology. The level of detail is sufficient so that you wouldn't need any supplementary guide. However, should you want to learn more, we have more detailed documentation in the works.

In this guide, we shall

  • walk you through starting an Iroha network, either with docker (recommended) or using one of the provided scripts;
  • introduce you to the client libraries;
  • take a small detour into the basic concepts of Iroha special instructions, and how they interact with the world state.

We invite you to follow the tutorial in this order:

  1. If you are already familiar with Hyperledger Iroha, read about the differences between the two versions of Iroha.
  2. Install Iroha 2.
  3. Follow one of the language-specific guides to learn how to set up and configure Iroha 2, register a domain and an account, register and mind assets, and visualize outputs:

Tutorial Updates

The current iteration of the Iroha 2 tutorial is a constant work in progress. We are updating the tutorial with each release to reflect the state of Iroha and the newly added features. While we do our best to keep this tutorial up to date, it can go out of sync by a few days or maybe a week.

',11),l=[r];function s(n,d,h,u,c,p){return t(),a("div",null,l)}const m=e(i,[["render",s]]);export{f as __pageData,m as default}; diff --git a/assets/guide_get-started_tutorials.md.dee1ad88.lean.js b/assets/guide_get-started_tutorials.md.dee1ad88.lean.js new file mode 100644 index 000000000..de07ee3da --- /dev/null +++ b/assets/guide_get-started_tutorials.md.dee1ad88.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as a,Q as o}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Introduction","description":"","frontmatter":{},"headers":[],"relativePath":"guide/get-started/tutorials.md","filePath":"guide/get-started/tutorials.md","lastUpdated":1700563625000}'),i={name:"guide/get-started/tutorials.md"},r=o("",11),l=[r];function s(n,d,h,u,c,p){return t(),a("div",null,l)}const m=e(i,[["render",s]]);export{f as __pageData,m as default}; diff --git a/assets/guide_introduction.md.5d831587.js b/assets/guide_introduction.md.5d831587.js new file mode 100644 index 000000000..ffb2e422e --- /dev/null +++ b/assets/guide_introduction.md.5d831587.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as r,Q as o}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Iroha 2","description":"","frontmatter":{},"headers":[],"relativePath":"guide/introduction.md","filePath":"guide/introduction.md","lastUpdated":1700563625000}'),t={name:"guide/introduction.md"},i=o('

Iroha 2

Iroha is a fully-featured blockchain ledger. With Iroha you can:

  • Create and manage custom fungible assets, such as currencies, gold, and others
  • Create and manage non-fungible assets
  • Manage user accounts with a domain hierarchy and multi-signature transactions
  • Use efficient portable smart contracts implemented either via WebAssembly or Iroha Special Instructions
  • Use both permissioned and permission-less blockchain deployments

Learn More

For more information on Iroha 2, please take a look at the Iroha 2 Whitepaper, as well as the Hyperledger Iroha section within the Hyperledger Foundation Wiki.

For more information on Iroha 1, take a look at the Iroha 1 documentation.

TIP

If you want to contribute to Hyperledger Iroha, please look at our Contributing Guide.

',7),n=[i];function s(l,h,c,d,u,p){return a(),r("div",null,n)}const g=e(t,[["render",s]]);export{f as __pageData,g as default}; diff --git a/assets/guide_introduction.md.5d831587.lean.js b/assets/guide_introduction.md.5d831587.lean.js new file mode 100644 index 000000000..4d078a3e5 --- /dev/null +++ b/assets/guide_introduction.md.5d831587.lean.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as r,Q as o}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Iroha 2","description":"","frontmatter":{},"headers":[],"relativePath":"guide/introduction.md","filePath":"guide/introduction.md","lastUpdated":1700563625000}'),t={name:"guide/introduction.md"},i=o("",7),n=[i];function s(l,h,c,d,u,p){return a(),r("div",null,n)}const g=e(t,[["render",s]]);export{f as __pageData,g as default}; diff --git a/assets/guide_iroha-2.md.9c7ff7ce.js b/assets/guide_iroha-2.md.9c7ff7ce.js new file mode 100644 index 000000000..b7db8bab5 --- /dev/null +++ b/assets/guide_iroha-2.md.9c7ff7ce.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as t,Q as o}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Iroha 2 vs. Iroha 1","description":"","frontmatter":{},"headers":[],"relativePath":"guide/iroha-2.md","filePath":"guide/iroha-2.md","lastUpdated":1700563625000}'),i={name:"guide/iroha-2.md"},r=o('

Iroha 2 vs. Iroha 1

Iroha 2 is a complete re-write of Hyperledger Iroha in Rust. As of writing, the two projects are developed concurrently.

In this introduction we outline the differences between the two versions of Iroha and highlight the new features of Iroha 2. It should be of particular interest to those who are already familiar with Iroha but wish to upgrade and start using the newer version.

INFO

Note that Iroha 1 and Iroha 2 are not compatible. Both projects are very different in their approaches and implementations as we present in the comparison below.

Among other changes, cryptography and account structures are implemented differently, meaning that users would have to generate new keys and link to their old account through a centralized service and no data from Iroha 1 would be accessible from Iroha 2.

Fault Tolerance

Iroha 2 learned a great deal from the development of the original Iroha. Of particular importance is the new and improved Byzantine-fault-tolerant consensus algorithm: Sumeragi. This new consensus allowed us to expand what could be done on a blockchain without any security risks.

INFO

The first version of Iroha used a consensus algorithm called Yac. Yac is crash-fault-tolerant, which means that it can survive a set number of nodes crashing: i.e. losing power, being cut off from the network, or being destroyed with a sledgehammer.

Sumeragi, by contrast, was designed to be Byzantine-fault-tolerant. This means that Iroha 2 can tolerate not only peers being inactive on the network, but also running malicious software and actively trying to falsify data in the blockchain.

We can mathematically prove that Iroha 2 can work when up to 33% of its nodes are actively trying to stop Iroha 2 from working properly or at all. In other words, even if someone gained control of a third of all of your network nodes, an Iroha 2 deployment is mathematically guaranteed to keep working.

Minimalist Code Base

Iroha 2 is a minimalist code base. We take great care to vet our dependencies and avoid large inter-dependent chunks of code.

We provide a few telemetry APIs, including prometheus tooling, structured logging in JSON, as well as compatibility with standard tools used in Parity Substrate.

Our data is strongly-typed, our methods are statically dispatched. We make use of the best that Rust has to offer: serde and parity_scale_codec for serialisation, tokio for co-operative multi-threading, as well as testing, bench-marking, static analysis and code auditing tools that come packaged with the exemplary cargo.

Our code is easy to reason about, and quick to compile, whilst also being ergonomic to use and thoughtfully crafted. We have no panics and no unsafe code.

Flexibility

Iroha 2 is also more flexible than the original Iroha due to its modular design.

It is possible to add or remove features based on a particular use-case. If you want the blockchain to be extremely fast and work on embedded hardware, just compile Iroha 2 without the expensive-metrics feature. Don't use telemetry at all? Remove it entirely and enjoy even more performance. Permission sets are plugins that can be upgraded during run-time.

We have an extensive module system as well as a robust WASM runtime framework.

Smart Contracts

Iroha 2 is an event-driven ledger. Each change in the state of the blockchain is necessarily accompanied by its own event that can trigger a smart contract: complex logic designed for use in on-chain scripting.

For smart contracts, Iroha 2 supports two approaches:

The first approach is useful when you want very simple transparent logic and also want to minimise the footprint in the blockchain. All interactions with the World state, that is, the state of the blockchain at this point in time, has to be done using the aforementioned instructions. There is also rudimentary support for domain-specific conditional logic.

If you want to learn more about smart contracts in Iroha 2, please consult our Wiki.

Static and Dynamic Linking

Iroha 2 smartly chooses when to use dynamic linking. This strikes a balance between it being easy to patch a Critical security vulnerability in a vendored library like OpenSSL, but also remaining reproducible and portable across platforms, architectures, and deployments.

INFO

You get the best of both worlds. Patching a security vulnerability is as easy as running sudo apt upgrade. On the other hand, only security-critical dependencies are linked dynamically, so it is highly unlikely that any of the smaller and less important libraries can break Iroha after an upgrade.

Testing

Iroha 2 is extensively tested. Despite being in active development, Iroha has 80% line coverage. Keep in mind that line coverage includes documentation comments, some of which are also tests.

INFO

There are plans to include Fuzz testing, property-based testing, and failure-point testing to ensure that Iroha is reliable.

',28),n=[r];function s(l,c,h,d,p,m){return a(),t("div",null,n)}const f=e(i,[["render",s]]);export{g as __pageData,f as default}; diff --git a/assets/guide_iroha-2.md.9c7ff7ce.lean.js b/assets/guide_iroha-2.md.9c7ff7ce.lean.js new file mode 100644 index 000000000..1b3bf53da --- /dev/null +++ b/assets/guide_iroha-2.md.9c7ff7ce.lean.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as t,Q as o}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Iroha 2 vs. Iroha 1","description":"","frontmatter":{},"headers":[],"relativePath":"guide/iroha-2.md","filePath":"guide/iroha-2.md","lastUpdated":1700563625000}'),i={name:"guide/iroha-2.md"},r=o("",28),n=[r];function s(l,c,h,d,p,m){return a(),t("div",null,n)}const f=e(i,[["render",s]]);export{g as __pageData,f as default}; diff --git a/assets/guide_reports_csd-rtgs.md.9808d30a.js b/assets/guide_reports_csd-rtgs.md.9808d30a.js new file mode 100644 index 000000000..5521a7112 --- /dev/null +++ b/assets/guide_reports_csd-rtgs.md.9808d30a.js @@ -0,0 +1 @@ +import{_ as t,o,c as s,k as e,a}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"CSD/RTGS linkages Proof of concept","description":"","frontmatter":{},"headers":[],"relativePath":"guide/reports/csd-rtgs.md","filePath":"guide/reports/csd-rtgs.md","lastUpdated":1700563625000}'),r={name:"guide/reports/csd-rtgs.md"},c=e("h1",{id:"csd-rtgs-linkages-proof-of-concept",tabindex:"-1"},[a("CSD/RTGS linkages Proof of concept "),e("a",{class:"header-anchor",href:"#csd-rtgs-linkages-proof-of-concept","aria-label":'Permalink to "CSD/RTGS linkages Proof of concept"'},"​")],-1),n=e("p",null,"In this document we will describe the CSD/RTGS linkages PoC execution via Iroha. This is a project which was done in collaboration with the Asian Development Bank, and Fujitsu. Other participants used technologies such as R3 Corda, Hyperledger Cactus (Cacti), Hyperledger Fabric and many other popular blockchain solutions.",-1),i=[c,n];function d(l,p,h,f,g,u){return o(),s("div",null,i)}const k=t(r,[["render",d]]);export{m as __pageData,k as default}; diff --git a/assets/guide_reports_csd-rtgs.md.9808d30a.lean.js b/assets/guide_reports_csd-rtgs.md.9808d30a.lean.js new file mode 100644 index 000000000..5521a7112 --- /dev/null +++ b/assets/guide_reports_csd-rtgs.md.9808d30a.lean.js @@ -0,0 +1 @@ +import{_ as t,o,c as s,k as e,a}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"CSD/RTGS linkages Proof of concept","description":"","frontmatter":{},"headers":[],"relativePath":"guide/reports/csd-rtgs.md","filePath":"guide/reports/csd-rtgs.md","lastUpdated":1700563625000}'),r={name:"guide/reports/csd-rtgs.md"},c=e("h1",{id:"csd-rtgs-linkages-proof-of-concept",tabindex:"-1"},[a("CSD/RTGS linkages Proof of concept "),e("a",{class:"header-anchor",href:"#csd-rtgs-linkages-proof-of-concept","aria-label":'Permalink to "CSD/RTGS linkages Proof of concept"'},"​")],-1),n=e("p",null,"In this document we will describe the CSD/RTGS linkages PoC execution via Iroha. This is a project which was done in collaboration with the Asian Development Bank, and Fujitsu. Other participants used technologies such as R3 Corda, Hyperledger Cactus (Cacti), Hyperledger Fabric and many other popular blockchain solutions.",-1),i=[c,n];function d(l,p,h,f,g,u){return o(),s("div",null,i)}const k=t(r,[["render",d]]);export{m as __pageData,k as default}; diff --git a/assets/guide_security_generating-cryptographic-keys.md.3297fbf3.js b/assets/guide_security_generating-cryptographic-keys.md.3297fbf3.js new file mode 100644 index 000000000..0051dc3e7 --- /dev/null +++ b/assets/guide_security_generating-cryptographic-keys.md.3297fbf3.js @@ -0,0 +1,89 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const F=JSON.parse('{"title":"Generating Cryptographic Keys","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/generating-cryptographic-keys.md","filePath":"guide/security/generating-cryptographic-keys.md","lastUpdated":1700563625000}'),l={name:"guide/security/generating-cryptographic-keys.md"},e=o(`

Generating Cryptographic Keys

In the realm of blockchain technology, cryptographic keys play a crucial role in upholding the security and authenticity of data transactions. With Iroha 2, you can create these vital keys to safeguard your digital assets and communications.

This section describes how to generate keys using the kagami tool, shipped alongside Iroha 2.

In the future, alternative methods of generating public keys shall be added.

Generating Cryptographic Keys with Kagami

TIP

Since kagami does not come with a manual page, you can use the --help (-h) command to retrieve a brief summary of all the usable kagami parameters within the CLI you are using.

After installing Iroha, run the following command from the project's root directory to generate a new key pair:

bash
$ cargo run --bin kagami --release -- crypto
$ cargo run --bin kagami --release -- crypto

You can specify a number of different parameters to tailor the generated key pair to your specific needs. The following parameters are available:

  • --algorithm (-a): Specifies the algorithm used for the key pair generation and encryption. If no algorithm is specified, ed25519 is used by default. Can be one of the following:

    • ed25519ECC an algorithm that utilises the Ed25519 curve, offering efficient and secure cryptographic operations for digital signatures and key exchange. If no algorithm is specified in a request, then ed25519 is used by default. Learn more:

    • secp256k1ECC an algorithm known for its application in blockchain systems like Bitcoin. It provides a robust foundation for secure key generation, digital signatures, and encryption.

    Learn more:
    Secp256k1 (Bitcoin Wiki)

    • bls_small — The Boneh-Lynn-Shacham algorithm with a small parameter configuration. This variant of the BLS cryptographic scheme is optimised for efficiency in certain resource-constrained environments while maintaining fundamental security properties.

    Learn more:
    BLS digital signature (Wikipedia)

    • bls_normal — The Boneh-Lynn-Shacham algorithm with a standard parameter configuration. This configuration of the BLS cryptographic scheme offers a balanced approach between efficiency and security, making it suitable for a wide range of applications in blockchain and cryptographic protocols.

    Learn more:
    BLS digital signature (Wikipedia)

  • --seed (-s): Specifies a string that serves as a deterministic starting point for the key pair generation. If a seed string is specified, kagami will generate the same key for the same string. If no seed is specified, a random seed value is chosen, and each invocation of kagami crypto will result in a different key. This parameter accepts a valid string of Unicode characters. For example, the seed string can contain not only numeric and latin, but also cyrillic, logographic (e.g., Japanese kanji characters) and ideographic (e.g., emojis) characters, as well as any font-related variations of those characters introduced to Unicode over the years.

    TIP

    If one chooses to use a seed, it must be treated as if it were a password: more randomness and longer seed strings make the cryptographic keys more resilient to dictionary attacks.

  • --private-key (-p): Specifies an existing private key as a string in the multihash format that is used to generate a public key.

  • --json (-j): Specifies that the output must be generated in the JSON format, which is mostly helpful for copy-and-pasting into the config.json file.

  • --compact (-c): Specifies that the output private and public keys are displayed on separate lines and are not labeled.

Examples

bash
# Input
+$ cargo run --bin kagami crypto
+
+# Possible Output (same layout, different keys)
+Public key (multihash): "ed01206B0F56F58761060056355DBA0E0FC489CFB2F974481ED64873082E6032796235"
+Private key (ed25519): "F71EA9D897C4338CBF4F1DC7B492AAD0BF6CE896B803D7CDB9CF25ECC15109826B0F56F58761060056355DBA0E0FC489CFB2F974481ED64873082E6032796235"
# Input
+$ cargo run --bin kagami crypto
+
+# Possible Output (same layout, different keys)
+Public key (multihash): "ed01206B0F56F58761060056355DBA0E0FC489CFB2F974481ED64873082E6032796235"
+Private key (ed25519): "F71EA9D897C4338CBF4F1DC7B492AAD0BF6CE896B803D7CDB9CF25ECC15109826B0F56F58761060056355DBA0E0FC489CFB2F974481ED64873082E6032796235"
bash
# Input
+$ kagami crypto -a secp256k1
+
+# Possible output
+Public key (multihash): "e70121022A9D6E0D54022C0E2752E43ADD91ADA28259E1F2CE0C6D4E9183FB2882DE6749"
+Private key (secp256k1): "7687B1433FB6731E6DC635A376B3EB3B5FCD1E02C9775C1642E7FD5DA035EC75"
# Input
+$ kagami crypto -a secp256k1
+
+# Possible output
+Public key (multihash): "e70121022A9D6E0D54022C0E2752E43ADD91ADA28259E1F2CE0C6D4E9183FB2882DE6749"
+Private key (secp256k1): "7687B1433FB6731E6DC635A376B3EB3B5FCD1E02C9775C1642E7FD5DA035EC75"
bash
# Input
+$ kagami crypto -s 1729
+
+# Exact output
+Public key (multihash): "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
+Private key (ed25519): "2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
# Input
+$ kagami crypto -s 1729
+
+# Exact output
+Public key (multihash): "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
+Private key (ed25519): "2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
bash
# Input
+$ kagami crypto -p 2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
+
+# Exact output
+Public key (multihash): "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
+Private key (ed25519): "2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
# Input
+$ kagami crypto -p 2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
+
+# Exact output
+Public key (multihash): "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
+Private key (ed25519): "2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
bash
# Input
+$ cargo run --bin kagami crypto -j
+
+# Possible output
+{
+  "public_key": "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04",
+  "private_key": {
+    "digest_function": "ed25519",
+    "payload": "2669bb1099477b970e1d7d7c54e345a64a54213fcfba2465cbcd6d4e5091a71db678073cfae6e247a58b442661c7da0e13bac5031cbc6343ef566b8718d47d04"
+  }
+}
# Input
+$ cargo run --bin kagami crypto -j
+
+# Possible output
+{
+  "public_key": "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04",
+  "private_key": {
+    "digest_function": "ed25519",
+    "payload": "2669bb1099477b970e1d7d7c54e345a64a54213fcfba2465cbcd6d4e5091a71db678073cfae6e247a58b442661c7da0e13bac5031cbc6343ef566b8718d47d04"
+  }
+}
bash
# Input
+$ cargo run --bin kagami crypto -c
+
+# Possible output
+ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
+2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
+ed25519
# Input
+$ cargo run --bin kagami crypto -c
+
+# Possible output
+ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
+2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
+ed25519
bash
# Input
+$ cargo run --bin kagami crypto -a bls_normal -s 2048
+
+# Exact output
+Public key (multihash): "ea01610402A54ABCC40819F15E3553CC8D42D628EEAD7E1B10724BD2AFE523A7C0446EB1CB3F14D4500BD68C997784136FD056BA04215DFD2D3FDC7883B43AE94AC52B7D01525F5A80B41C01701502B46DBB9F0384CC7BE037DC2CBC928014E52A4C5C3B"
+Private key (bls_normal): "0000000000000000000000000000000035D9120A174E35E966DD92DE90B2446D4B060C8B72018B3917A1C97D7E93EAEC"
# Input
+$ cargo run --bin kagami crypto -a bls_normal -s 2048
+
+# Exact output
+Public key (multihash): "ea01610402A54ABCC40819F15E3553CC8D42D628EEAD7E1B10724BD2AFE523A7C0446EB1CB3F14D4500BD68C997784136FD056BA04215DFD2D3FDC7883B43AE94AC52B7D01525F5A80B41C01701502B46DBB9F0384CC7BE037DC2CBC928014E52A4C5C3B"
+Private key (bls_normal): "0000000000000000000000000000000035D9120A174E35E966DD92DE90B2446D4B060C8B72018B3917A1C97D7E93EAEC"

Other Operations with Kagami

1. Building kagami

The Iroha 2 node binary and all supporting tools are supplied in the official docker image. However, using it like this is cumbersome, as kagami is meant to be used as a standalone external tool, so building it from a source may be helpful.

To build kagami, run the following:

bash
$ cargo build --bin kagami
$ cargo build --bin kagami

This will produce a single statically linked executable in the target/debug directory, that still links dynamically against the system-provided standard C-library.

Note

Iroha and all supporting tools can also be built to statically link against the musl standard library, which allows the application to run on any POSIX-compliant ELF-capable system (all GNU+Linux distributions, some BSD variants).

2. Installing the source-built kagami into /bin

There are multiple ways to make your command line be able to use the kagami version that you have just compiled. One of the easiest ways that should work on most systems is to move or link the binary into the /bin directory on UNIX systems.

bash
$ sudo mv target/debug/kagami /bin
$ sudo mv target/debug/kagami /bin

3. Moving kagami to the .local/bin directory

To circumvent the requirement of having the binary in the global binary folder, and thus necessarily exposing the binary to all other users, as well as requiring root authentication (which is not always available), one can instead install the application as a regular user.

To move kagami to the authenticated user's .local/bin directory, making it uniquely accessible only by that user, run the following:

bash
$ mv target/debug/kagami ~/.local/bin
$ mv target/debug/kagami ~/.local/bin

This method works on most GNU Linux distributions, but is not guaranteed to do so. If it doesn't, consult the next subtopic.

Making the <username>/.local/bin directory available to the shell

To make the <username>/.local/bin directory explicitly available to your shell's .rc file, perform the following:

  1. Check if kagami is available by running the following:
bash
$ whereis kagami
+kagami:
$ whereis kagami
+kagami:
  1. Depending on the shell that you are using, perform one of the following:
  • If using Bash: Fix the PATH variable for the shell and then reload the .bashrc script by running the following:

    bash
    $ echo "export PATH='\${HOME}/.local/bin:\${PATH}'" >> ~/.bashrc
    +$ source ~/.bashrc
    $ echo "export PATH='\${HOME}/.local/bin:\${PATH}'" >> ~/.bashrc
    +$ source ~/.bashrc
  • If using Zsh: Fix the PATH variable for the shell and reload the .zshrc script by running the following:

    bash
    $ echo "export PATH='\${HOME}/.local/bin:\${PATH}'" >> ~/.zshrc
    +$ source ~/.zshrc
    $ echo "export PATH='\${HOME}/.local/bin:\${PATH}'" >> ~/.zshrc
    +$ source ~/.zshrc
  • If using fish: Fix the PATH variable for the shell variable permanently by running the following:

    bash
    $ fish_add_path ~/.local/bin
    $ fish_add_path ~/.local/bin

::: note

In addition to the methods listed above, consult documentation for the shell you're using or consider adding the PATH variant to your terminal's session configuration.

:::

`,36),p=[e];function t(c,r,i,y,E,d){return a(),n("div",null,p)}const u=s(l,[["render",t]]);export{F as __pageData,u as default}; diff --git a/assets/guide_security_generating-cryptographic-keys.md.3297fbf3.lean.js b/assets/guide_security_generating-cryptographic-keys.md.3297fbf3.lean.js new file mode 100644 index 000000000..c53757fcc --- /dev/null +++ b/assets/guide_security_generating-cryptographic-keys.md.3297fbf3.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as o}from"./chunks/framework.1293becd.js";const F=JSON.parse('{"title":"Generating Cryptographic Keys","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/generating-cryptographic-keys.md","filePath":"guide/security/generating-cryptographic-keys.md","lastUpdated":1700563625000}'),l={name:"guide/security/generating-cryptographic-keys.md"},e=o("",36),p=[e];function t(c,r,i,y,E,d){return a(),n("div",null,p)}const u=s(l,[["render",t]]);export{F as __pageData,u as default}; diff --git a/assets/guide_security_index.md.0ab406c3.js b/assets/guide_security_index.md.0ab406c3.js new file mode 100644 index 000000000..26793e090 --- /dev/null +++ b/assets/guide_security_index.md.0ab406c3.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as i,Q as a}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Security","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/index.md","filePath":"guide/security/index.md","lastUpdated":1700563625000}'),r={name:"guide/security/index.md"},o=a('

Security

When utilizing Iroha 2—or any other blockchain ledger for that matter—security is paramount for financial organizations, as it forms the foundation of trust in an industry where sensitive financial data and transactions are routine. A successful security breach performed by a malicious party can lead to devastating consequences for you. Therefore establishing preemptive security measures is essential to protect the integrity and confidentiality of your sensitive data.

In this section you can learn about various aspects of securing your Iroha 2 network. To learn more, choose one of the following topics:

  • Security Principles:

    The core security principles that individuals and organizations can adopt to protect their data and decrease the chance of a breach and/or leak.

  • Operational Security:

    Best practices for securing the day-to-day operations of your network, including access controls, monitoring, incident responses, the use of browsers, etc.

  • Password Security:

    A deep-dive into password entropy, creating strong passwords and avoiding password vulnerabilities.

  • Public Key Cryptography:

    An introduction into public key cryptography, encryption, signatures, and their role in establishing secure communication within the blockchain.

    • Generating Cryptographic Keys:

      Instructions on how to generate cryptographic keys and use kagami (a supporting tool shipped alongside Iroha 2).

    • Storing Cryptographic Keys:

      Best practices for securing your cryptographic keys with a number of different approaches that can also be combined.

',5),n=[o];function s(c,p,l,h,u,d){return t(),i("div",null,n)}const f=e(r,[["render",s]]);export{g as __pageData,f as default}; diff --git a/assets/guide_security_index.md.0ab406c3.lean.js b/assets/guide_security_index.md.0ab406c3.lean.js new file mode 100644 index 000000000..6bd7487bc --- /dev/null +++ b/assets/guide_security_index.md.0ab406c3.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as i,Q as a}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Security","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/index.md","filePath":"guide/security/index.md","lastUpdated":1700563625000}'),r={name:"guide/security/index.md"},o=a("",5),n=[o];function s(c,p,l,h,u,d){return t(),i("div",null,n)}const f=e(r,[["render",s]]);export{g as __pageData,f as default}; diff --git a/assets/guide_security_operational-security.md.e0ab91bf.js b/assets/guide_security_operational-security.md.e0ab91bf.js new file mode 100644 index 000000000..8f40fdb73 --- /dev/null +++ b/assets/guide_security_operational-security.md.e0ab91bf.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as r,Q as t}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Operational Security","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/operational-security.md","filePath":"guide/security/operational-security.md","lastUpdated":1700563625000}'),i={name:"guide/security/operational-security.md"},o=t('

Operational Security

Operational Security (OPSEC) is a systematic approach to security and risk management, which is essentially a collection of strategies and advice adopted for specific use-cases with the aim of preventing unauthorized access and data leakage.

OPSEC is the standard practice for most companies to guarantee the availability and stability of their assets. This includes considering such factors as physical security (e.g., making sure that unattended post-it notes do not contain sensitive data), secure communication protocols (e.g., not sending sensitive data over unencrypted SMS), threat analysis (e.g., determining potential malicious parties, learning about the latest attack methods), personnel training (e.g., without employees following OPSEC measures, they will, sooner or later, prove to be ineffective), and risk mitigation (e.g., encrypting your hard drives and USB devices).

Since Iroha 2 is likely to be deployed as a financial ledger, OPSEC measures and practices must be taken seriously. This topic describes strategies and approaches that individuals and organizations using Iroha 2 in their operations should consider as part of their extensive security protocol.

Following and adopting the guidelines in this topic is a necessary step towards achieving total security, however, it is not sufficient on its own. To further improve your security, learn more throughout the rest of the Security section and specifically the following topics:

  • Stay vigilant. The most likely way in which one can lose their assets in a blockchain is by giving away their sensitive details.

  • Encrypt your disks. Encrypting boot devices allows them to protect your data even if an attacker have gained access to the hardware. Doing it for your portable devices is twice as important.

  • Use trusted software. Software that ships via reproducible binary builds, and that you build from source, is the most trustworthy. Proprietary or open-source software that hasn't been audited is a potential risk that must be taken seriously.

  • Never leave portable devices with sensitive data unattended. A split second is enough to steal your device.

  • Verify the signatures on binary packages. This is not too different from the public key cryptography used inside Iroha v2.

  • To prevent unauthorized access, always secure your laptop or personal computer when leaving it unattended. Use strong passwords, lock the screen, and follow best practices for securing your devices.

  • Establish a secure air-gapped location for your keys. First, encrypt the keys, then store them in an offline-only device, ideally with electromagnetic shielding installed. Hardware keys are specifically designed this purpose.

  • Always keep your software updated to their latest version across all devices, including computers and phones. Regular updates help patch vulnerabilities and minimise potential risks associated with outdated software, even before such vulnerabilities are disclosed.

  • Develop a routine for periodically updating passwords and cryptographic keys. This proactive approach significantly contributes to enhancing overall security posture, since it is much harder to hit a moving target.

Using Browsers

If an application connected to Iroha 2 features a web UI, your browser can either aid the security or pose a potential threat. It is essential to exercise caution, especially when it comes to the plugins you choose to install.

Consider the following measures to enhance your browsing security:

  • Avoid using browsers that are known for having bad security models and for leaking their users' data.

    You can look up privacy violations and security issues for any browser. For example, this article on browser privacy discusses a variety of browsers and how secure they are. Note that proprietary browsers (such as Chrome, Safari, Opera, Vivaldi, Edge, and others) are generally tremendously harder to audit due to their code being hidden from public, which means that you cannot be sure how secure they are.

  • Give preference to browsers with solid history of valuing and protecting their users' privacy and security:

    • Librewolf, Icecat, Firedragon, etc. — well established forks of Mozilla Firefox with added security features.
    • Ungoogled chromium — a highly audited open-source version of Google Chrome that is enhanced with additional security measures and has all of the Google-related web services removed.
    • Brave — a highly audited open-source version of Google Chromium that is enhanced with additional security measures; has a built-in VPN and ad blocker functionality.
    • Falkon — an open-source Qt-based web browser (built on QtWebEngine, a wrapper for Google Chromium) with known track record of being secure; has a number of extensions available for download from its KDE store page.
    • Qutebrowser — an open-source Qt-based web browser (built on QtWebEngine, a wrapper for Google Chromium) with known track record of being secure; has a unique keyboard-focused approach with minimalist GUI; considered to be a browser of choice for many security specialists.
  • Avoid enabling JavaScript unless necessary.

  • Use the browser's built-in confinement mechanism for plugins to restrict the access rights that the installed plugins have.

  • Clear cookies before and after important operations. Be mindful not to enable the Keep Me Signed In or Remember me feature. Keep in mind that some websites have this feature enabled by default.

  • Use an ad blocker. These not only block ads but also disable site tracking features. Depending on the browser you use, an ad blocker may not be a built-in feature.

  • Be mindful of lookalike characters (e.g., 0, θ, O, О, and ߀ are six different characters). Paying attention to details like this may save you from a phishing attack.

  • Avoid web UI email clients in favour of desktop clients. Before using it, set up your desktop email client to sign and verify GPG key signatures.

  • Avoid using web-based messaging services. For instance, Discord (built with the infamous electron framework) is susceptible to many of the same attacks as would a Google Chromium window with the web version of Discord open.

  • Update your browser to the latest version whenever possible. Updates often include critical security patches that address vulnerabilities.

  • Be cautious of what browser extensions you install. Only use well-known and trusted extensions from reputable sources. Rogue extensions can compromise your data and privacy.

  • Create separate browser profiles for various tasks. Use one profile for everyday browsing and another for activities involving high security and sensitive data. This way, extensions installed on the profile for everyday browsing cannot access the sensitive data from the secure one.

  • Use a portable version of your browser copied to a USB flash drive. This method ensures that even if a security bug grants one of the installed plugins with access to data between the profiles, your security-related profile remains on a separate and removable device.

  • Periodically clear your browser's cache and cookies to remove potentially sensitive data that may accidentally be stored on your device.

Recovery Plan

In the event of an emergency, such as losing a key or facing a security breach, a well-structured and prepared in advance recovery plan is an essential lifeline. Creating a clear set of steps to follow can help mitigate potential damage and promptly reinstate security.

Organizations should consider the following key aspects when developing their recovery plan:

  • Outline step-by-step procedures to be followed in case of key loss or other security incidents. Ensure that these steps are easily accessible and understandable by the users and/or employees.

  • Establish a communication channel that may be used to promptly report security breaches and potential threats, such as leaked or lost cryptographic keys and password.

  • If you utilize hardware keys (e.g., YubiKey or SoloKeys Solo) as a security measure, consider adopting redundancy strategy. Keep two keys: one for daily use and another stored in a secure location. This precaution ensures access even if the primary key is compromised or lost.

  • When security breaches or leaks are reported, react promptly by replacing or disabling affected keys and passwords. This proactive response minimizes the potential risks and damage.

  • Periodically review and update your recovery plan. This ensures that the plan remains relevant and effective as your security landscape evolves.

WARNING

Remember that a recovery plan is not just another document. Rather, it's a lifeline that helps navigate unexpected challenges. By anticipating potential scenarios and establishing a clear roadmap for action, you fortify your operational security and enhance your readiness to respond effectively to any security incident.

',17),s=[o];function n(l,c,d,p,h,u){return a(),r("div",null,s)}const g=e(i,[["render",n]]);export{f as __pageData,g as default}; diff --git a/assets/guide_security_operational-security.md.e0ab91bf.lean.js b/assets/guide_security_operational-security.md.e0ab91bf.lean.js new file mode 100644 index 000000000..d7eb8397b --- /dev/null +++ b/assets/guide_security_operational-security.md.e0ab91bf.lean.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as r,Q as t}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"Operational Security","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/operational-security.md","filePath":"guide/security/operational-security.md","lastUpdated":1700563625000}'),i={name:"guide/security/operational-security.md"},o=t("",17),s=[o];function n(l,c,d,p,h,u){return a(),r("div",null,s)}const g=e(i,[["render",n]]);export{f as __pageData,g as default}; diff --git a/assets/guide_security_password-security.md.99153c0f.js b/assets/guide_security_password-security.md.99153c0f.js new file mode 100644 index 000000000..e62bde24d --- /dev/null +++ b/assets/guide_security_password-security.md.99153c0f.js @@ -0,0 +1,3 @@ +import{_,C as u,o as g,c as f,k as e,H as s,w as a,a as t,Q as m}from"./chunks/framework.1293becd.js";const Rs=JSON.parse('{"title":"Password Security","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/password-security.md","filePath":"guide/security/password-security.md","lastUpdated":1700563625000}'),y={name:"guide/security/password-security.md"},w=m('

Password Security

In the realm of blockchain security, protecting passwords is paramount. To ensure your data and everything it represents remain impervious to unauthorized access, let's delve into the nuances of password security.

Password Strength

Likely enough, you may have previously encountered recommendations on how to come up with a strong password. These may entail such advice as minimum password length, addition of special characters, etc. Such recommendations aim to increase the strength of your password that hinges on entropy, i.e. randomness of the password.

So, what defines a strong password? A strong password is a password with high entropy.

To calculate the entropy of a password, we may follow the Entropy formula:

',6),b={class:"tip custom-block"},x=e("p",{class:"custom-block-title"},"Entropy formula",-1),k={class:"katex"},T={class:"katex-mathml"},v=e("span",{class:"katex-html","aria-hidden":"true"},[e("span",{class:"base"},[e("span",{class:"strut",style:{height:"0.6833em"}}),e("span",{class:"mord mathnormal"},"L")])],-1),S=e("br",null,null,-1),A={class:"katex"},P={class:"katex-mathml"},M=e("span",{class:"katex-html","aria-hidden":"true"},[e("span",{class:"base"},[e("span",{class:"strut",style:{height:"0.6833em"}}),e("span",{class:"mord mathnormal",style:{"margin-right":"0.05764em"}},"S")])],-1),I=e("br",null,null,-1),C={class:"katex"},z={class:"katex-mathml"},V=m('',1),L={class:"katex-block"},E={class:"katex-display"},N={class:"katex"},D={class:"katex-mathml"},R=m('',1),H=e("p",null,"The resulting number is the amount of entropy bits in a password. The higher the number, the harder the password is to crack.",-1),$=e("p",null,"Knowing the entropy value, the amount of attempts required to brute-force a password with said entropy can be derived by using the following formula:",-1),q={class:"katex-block"},K={class:"katex-display"},X={class:"katex"},G={class:"katex-mathml"},U=m('',1),B=e("p",null,[t("There is no universal answer as to how high the entropy of a password should be. For financial organizations, it is advised to keep the entropy of their passwords in the range from "),e("code",null,"64"),t(" to "),e("code",null,"127"),t(" bits ("),e("code",null,"128"),t(" bits or more is generally considered to be an overkill). However, keep in mind that "),e("abbr",{title:"Graphics Processing Unit"},"GPU"),t("s keep constantly evolving, and the time required for password cracking keeps decreasing over time.")],-1),F=e("p",null,"Following the entropy formula, let us compare the following two examples:",-1),j={class:"katex"},W={class:"katex-mathml"},J=m('',1),O=e("pre",null,[e("code",null,`$$Entropy=log_2(26^{16})=log_2(43,608,742,899,428,874,059,776)=75.20703...$$ +`)],-1),Q={start:"2"},Y={class:"katex"},Z={class:"katex-mathml"},ss=m('',1),as=e("pre",null,[e("code",null,`$$Entropy=log_2(96^{16})=log_2(52,040,292,466,647,269,602,037,015,248,896)=105.35940... $$ +`)],-1),ts={class:"katex"},es={class:"katex-mathml"},ns=m('',1),ls=m('

Additionally increasing the length of the password, will grow the number of possible combinations even further, therefore enhancing the entropy—strength—of the password.

However, instead of wrestling with complexities, we advise using a password manager program—like KeePassXC (for more details, see Adding a Password Manager Program and Configuring KeePassXC)—to generate and securely store your passwords.

TIP

Certain websites limit the maximum possible entropy of passwords, i.e., either limit the maximum password length or the set of accepted characters, or both.

Keep this in mind when using such websites and aim to periodically update your passwords.

Password Vulnerabilities

Passwords can fall victim to brute-force attacks, typically executed using powerful GPUs in conjunction with dictionaries or exhaustive iteration through all possibilities. To thwart such attempts, craft a unique password devoid of personal information like birthdays, addresses, phone numbers, or social security numbers. Avoid providing attackers with easily guessable clues.

So, how hard it is to crack a modern password? It really depends on who you ask.

With a setup like Kevin Mitnick's cluster setup housing 24 NVIDIA® GeForce RTX 4090's and 6 NVIDIA® GeForce RTX 2080's, all of them running Hashtopolis software, he used to crack passwords that supposed to take a year in mere half a month.

However, let's now compare it to a single RTX 4090, capable of processing through 300 H/s using NTLM and 200 H/s using bcrypt, as outlined in this tweet.

As an extension of our previous entropy calculations, let's now examine the following projected cracking times:

',9),ps={class:"katex"},rs={class:"katex-mathml"},os=m('',1),is=e("code",null,"NTLM",-1),cs={class:"katex"},ms={class:"katex-mathml"},hs=m('',1),us=e("abbr",{title:"Hashes per second"},"H/s",-1),ds={class:"katex"},_s={class:"katex-mathml"},gs=m('',1),fs=e("code",null,"NTLM",-1),ys=e("code",null,"bcrypt",-1),ws={class:"katex"},bs={class:"katex-mathml"},xs=m('',1),ks=e("abbr",{title:"Hashes per second"},"H/s",-1),Ts={class:"katex"},vs={class:"katex-mathml"},Ss=m('',1),As=e("p",null,[t("So, simply picking higher entropy raised the time it takes to crack a password to unfathomable numbers. Yes, the process may be sped up by using multiple GPUs, however this method pales in comparison with the "),e("a",{href:"https://xkcd.com/538/",target:"_blank",rel:"noreferrer"},"XKCD approach"),t(".")],-1),Ps=e("p",null,[t("It is important to note that an extensive character set isn't always necessary to reach high entropy. It can be obtained by using multi-word passwords, or lengthy sentences in particular. The classic "),e("a",{href:"https://xkcd.com/936/",target:"_blank",rel:"noreferrer"},"XKCD comic"),t(" illustrates this concept eloquently.")],-1),Ms=e("div",{class:"warning custom-block"},[e("p",{class:"custom-block-title"},"WARNING"),e("p",null,"Avoid writing your password down anywhere. Store your password recovery phrase securely. If the phrase is too long, you may write it down, ensuring that you can read it out and type it out later. Store the physical copy of the phrase in a secure location and/or container.")],-1);function Is(Cs,zs,Vs,Ls,Es,Ns){const l=u("mi"),r=u("mrow"),o=u("annotation"),i=u("semantics"),c=u("math"),h=u("msup"),p=u("mo"),n=u("mn"),d=u("msub");return g(),f("div",null,[w,e("div",b,[x,e("p",null,[e("span",k,[e("span",T,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(l,null,{default:a(()=>[t("L")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("L")]),_:1})]),_:1})]),_:1})]),v]),t(" — Password length; number of symbols in the password."),S,e("span",A,[e("span",P,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(l,null,{default:a(()=>[t("S")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("S")]),_:1})]),_:1})]),_:1})]),M]),t(" — Character set; size of the pool of unique possible symbols."),I,e("span",C,[e("span",z,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(h,null,{default:a(()=>[s(l,null,{default:a(()=>[t("S")]),_:1}),s(l,null,{default:a(()=>[t("L")]),_:1})]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("S^L")]),_:1})]),_:1})]),_:1})]),V]),t(" — Number of possible combinations.")]),e("p",L,[e("span",E,[e("span",N,[e("span",D,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(l,null,{default:a(()=>[t("E")]),_:1}),s(l,null,{default:a(()=>[t("n")]),_:1}),s(l,null,{default:a(()=>[t("t")]),_:1}),s(l,null,{default:a(()=>[t("r")]),_:1}),s(l,null,{default:a(()=>[t("o")]),_:1}),s(l,null,{default:a(()=>[t("p")]),_:1}),s(l,null,{default:a(()=>[t("y")]),_:1}),s(p,null,{default:a(()=>[t("=")]),_:1}),s(l,null,{default:a(()=>[t("l")]),_:1}),s(l,null,{default:a(()=>[t("o")]),_:1}),s(d,null,{default:a(()=>[s(l,null,{default:a(()=>[t("g")]),_:1}),s(n,null,{default:a(()=>[t("2")]),_:1})]),_:1}),s(p,{stretchy:"false"},{default:a(()=>[t("(")]),_:1}),s(h,null,{default:a(()=>[s(l,null,{default:a(()=>[t("S")]),_:1}),s(l,null,{default:a(()=>[t("L")]),_:1})]),_:1}),s(p,{stretchy:"false"},{default:a(()=>[t(")")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t(" Entropy=log_2(S^L) ")]),_:1})]),_:1})]),_:1})]),R])])]),H,$,e("p",q,[e("span",K,[e("span",X,[e("span",G,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(h,null,{default:a(()=>[s(l,null,{default:a(()=>[t("S")]),_:1}),s(l,null,{default:a(()=>[t("L")]),_:1})]),_:1}),s(p,null,{default:a(()=>[t("=")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("2")]),_:1}),s(l,null,{default:a(()=>[t("E")]),_:1})]),_:1}),s(l,null,{default:a(()=>[t("n")]),_:1}),s(l,null,{default:a(()=>[t("t")]),_:1}),s(l,null,{default:a(()=>[t("r")]),_:1}),s(l,null,{default:a(()=>[t("o")]),_:1}),s(l,null,{default:a(()=>[t("p")]),_:1}),s(l,null,{default:a(()=>[t("y")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t(" S^L=2^Entropy ")]),_:1})]),_:1})]),_:1})]),U])])]),B]),F,e("ol",null,[e("li",null,[t("A 16-character password with the character set utilizing only lowercase letters of the modern English alphabet (26 characters) yields approximately 43 sextillion ("),e("span",j,[e("span",W,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("43")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("43*10^21")]),_:1})]),_:1})]),_:1})]),J]),t(") possible combinations.")])]),O,e("ol",Q,[e("li",null,[t("A 16-character password with the character set expanded to 96, including uppercase letters and special symbols, inflates the number of possible combinations to a staggering 52 nonillion ("),e("span",Y,[e("span",Z,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("52")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("3")]),_:1})]),_:1}),s(n,null,{default:a(()=>[t("0")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("52*10^30")]),_:1})]),_:1})]),_:1})]),ss]),t("), improving entropy significantly.")])]),as,e("p",null,[t("As can be seen, even by only expanding the character set from 26 to 96 symbols, the number of possible combinations that a malicious party would need to bruteforce has expanded by "),e("span",ts,[e("span",es,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("1.1933")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("9")]),_:1})]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("1.1933*10^9")]),_:1})]),_:1})]),_:1})]),ns]),t(" times.")]),ls,e("ol",null,[e("li",null,[e("p",null,[t("There are "),e("span",ps,[e("span",rs,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("31")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("540")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("000")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("31,540,000")]),_:1})]),_:1})]),_:1})]),os]),t(" seconds in a regular non-leap year. Assuming the worst-case scenario with "),is,t(", at the speed of "),e("span",cs,[e("span",ms,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("300")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("9")]),_:1})]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("300*10^9")]),_:1})]),_:1})]),_:1})]),hs]),t(),us,t(", it would take a single RTX 4090 approximately "),e("span",ds,[e("span",_s,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("4")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("608.83")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("4,608.83")]),_:1})]),_:1})]),_:1})]),gs]),t(" years to crack a 16-character password with a character set of 26 letters of the modern English alphabet.")])]),e("li",null,[e("p",null,[t("If instead of "),fs,t(" we use "),ys,t(", therefore reducing the iteration speed to "),e("span",ws,[e("span",bs,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("200")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("3")]),_:1})]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("200*10^3")]),_:1})]),_:1})]),_:1})]),xs]),t(),ks,t(", while also expanding the character set to 96, including uppercase letters and special symbols, the time to crack soars to about "),e("span",Ts,[e("span",vs,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("8")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("249")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("887")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("835")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("549")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("662")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("270.456")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("8,249,887,835,549,662,270.456")]),_:1})]),_:1})]),_:1})]),Ss]),t(" years, far surpassing the age of the universe.")])])]),As,Ps,Ms])}const Hs=_(y,[["render",Is]]);export{Rs as __pageData,Hs as default}; diff --git a/assets/guide_security_password-security.md.99153c0f.lean.js b/assets/guide_security_password-security.md.99153c0f.lean.js new file mode 100644 index 000000000..8c1e75302 --- /dev/null +++ b/assets/guide_security_password-security.md.99153c0f.lean.js @@ -0,0 +1,3 @@ +import{_,C as u,o as g,c as f,k as e,H as s,w as a,a as t,Q as m}from"./chunks/framework.1293becd.js";const Rs=JSON.parse('{"title":"Password Security","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/password-security.md","filePath":"guide/security/password-security.md","lastUpdated":1700563625000}'),y={name:"guide/security/password-security.md"},w=m("",6),b={class:"tip custom-block"},x=e("p",{class:"custom-block-title"},"Entropy formula",-1),k={class:"katex"},T={class:"katex-mathml"},v=e("span",{class:"katex-html","aria-hidden":"true"},[e("span",{class:"base"},[e("span",{class:"strut",style:{height:"0.6833em"}}),e("span",{class:"mord mathnormal"},"L")])],-1),S=e("br",null,null,-1),A={class:"katex"},P={class:"katex-mathml"},M=e("span",{class:"katex-html","aria-hidden":"true"},[e("span",{class:"base"},[e("span",{class:"strut",style:{height:"0.6833em"}}),e("span",{class:"mord mathnormal",style:{"margin-right":"0.05764em"}},"S")])],-1),I=e("br",null,null,-1),C={class:"katex"},z={class:"katex-mathml"},V=m("",1),L={class:"katex-block"},E={class:"katex-display"},N={class:"katex"},D={class:"katex-mathml"},R=m("",1),H=e("p",null,"The resulting number is the amount of entropy bits in a password. The higher the number, the harder the password is to crack.",-1),$=e("p",null,"Knowing the entropy value, the amount of attempts required to brute-force a password with said entropy can be derived by using the following formula:",-1),q={class:"katex-block"},K={class:"katex-display"},X={class:"katex"},G={class:"katex-mathml"},U=m("",1),B=e("p",null,[t("There is no universal answer as to how high the entropy of a password should be. For financial organizations, it is advised to keep the entropy of their passwords in the range from "),e("code",null,"64"),t(" to "),e("code",null,"127"),t(" bits ("),e("code",null,"128"),t(" bits or more is generally considered to be an overkill). However, keep in mind that "),e("abbr",{title:"Graphics Processing Unit"},"GPU"),t("s keep constantly evolving, and the time required for password cracking keeps decreasing over time.")],-1),F=e("p",null,"Following the entropy formula, let us compare the following two examples:",-1),j={class:"katex"},W={class:"katex-mathml"},J=m("",1),O=e("pre",null,[e("code",null,`$$Entropy=log_2(26^{16})=log_2(43,608,742,899,428,874,059,776)=75.20703...$$ +`)],-1),Q={start:"2"},Y={class:"katex"},Z={class:"katex-mathml"},ss=m("",1),as=e("pre",null,[e("code",null,`$$Entropy=log_2(96^{16})=log_2(52,040,292,466,647,269,602,037,015,248,896)=105.35940... $$ +`)],-1),ts={class:"katex"},es={class:"katex-mathml"},ns=m("",1),ls=m("",9),ps={class:"katex"},rs={class:"katex-mathml"},os=m("",1),is=e("code",null,"NTLM",-1),cs={class:"katex"},ms={class:"katex-mathml"},hs=m("",1),us=e("abbr",{title:"Hashes per second"},"H/s",-1),ds={class:"katex"},_s={class:"katex-mathml"},gs=m("",1),fs=e("code",null,"NTLM",-1),ys=e("code",null,"bcrypt",-1),ws={class:"katex"},bs={class:"katex-mathml"},xs=m("",1),ks=e("abbr",{title:"Hashes per second"},"H/s",-1),Ts={class:"katex"},vs={class:"katex-mathml"},Ss=m("",1),As=e("p",null,[t("So, simply picking higher entropy raised the time it takes to crack a password to unfathomable numbers. Yes, the process may be sped up by using multiple GPUs, however this method pales in comparison with the "),e("a",{href:"https://xkcd.com/538/",target:"_blank",rel:"noreferrer"},"XKCD approach"),t(".")],-1),Ps=e("p",null,[t("It is important to note that an extensive character set isn't always necessary to reach high entropy. It can be obtained by using multi-word passwords, or lengthy sentences in particular. The classic "),e("a",{href:"https://xkcd.com/936/",target:"_blank",rel:"noreferrer"},"XKCD comic"),t(" illustrates this concept eloquently.")],-1),Ms=e("div",{class:"warning custom-block"},[e("p",{class:"custom-block-title"},"WARNING"),e("p",null,"Avoid writing your password down anywhere. Store your password recovery phrase securely. If the phrase is too long, you may write it down, ensuring that you can read it out and type it out later. Store the physical copy of the phrase in a secure location and/or container.")],-1);function Is(Cs,zs,Vs,Ls,Es,Ns){const l=u("mi"),r=u("mrow"),o=u("annotation"),i=u("semantics"),c=u("math"),h=u("msup"),p=u("mo"),n=u("mn"),d=u("msub");return g(),f("div",null,[w,e("div",b,[x,e("p",null,[e("span",k,[e("span",T,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(l,null,{default:a(()=>[t("L")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("L")]),_:1})]),_:1})]),_:1})]),v]),t(" — Password length; number of symbols in the password."),S,e("span",A,[e("span",P,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(l,null,{default:a(()=>[t("S")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("S")]),_:1})]),_:1})]),_:1})]),M]),t(" — Character set; size of the pool of unique possible symbols."),I,e("span",C,[e("span",z,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(h,null,{default:a(()=>[s(l,null,{default:a(()=>[t("S")]),_:1}),s(l,null,{default:a(()=>[t("L")]),_:1})]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("S^L")]),_:1})]),_:1})]),_:1})]),V]),t(" — Number of possible combinations.")]),e("p",L,[e("span",E,[e("span",N,[e("span",D,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(l,null,{default:a(()=>[t("E")]),_:1}),s(l,null,{default:a(()=>[t("n")]),_:1}),s(l,null,{default:a(()=>[t("t")]),_:1}),s(l,null,{default:a(()=>[t("r")]),_:1}),s(l,null,{default:a(()=>[t("o")]),_:1}),s(l,null,{default:a(()=>[t("p")]),_:1}),s(l,null,{default:a(()=>[t("y")]),_:1}),s(p,null,{default:a(()=>[t("=")]),_:1}),s(l,null,{default:a(()=>[t("l")]),_:1}),s(l,null,{default:a(()=>[t("o")]),_:1}),s(d,null,{default:a(()=>[s(l,null,{default:a(()=>[t("g")]),_:1}),s(n,null,{default:a(()=>[t("2")]),_:1})]),_:1}),s(p,{stretchy:"false"},{default:a(()=>[t("(")]),_:1}),s(h,null,{default:a(()=>[s(l,null,{default:a(()=>[t("S")]),_:1}),s(l,null,{default:a(()=>[t("L")]),_:1})]),_:1}),s(p,{stretchy:"false"},{default:a(()=>[t(")")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t(" Entropy=log_2(S^L) ")]),_:1})]),_:1})]),_:1})]),R])])]),H,$,e("p",q,[e("span",K,[e("span",X,[e("span",G,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(h,null,{default:a(()=>[s(l,null,{default:a(()=>[t("S")]),_:1}),s(l,null,{default:a(()=>[t("L")]),_:1})]),_:1}),s(p,null,{default:a(()=>[t("=")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("2")]),_:1}),s(l,null,{default:a(()=>[t("E")]),_:1})]),_:1}),s(l,null,{default:a(()=>[t("n")]),_:1}),s(l,null,{default:a(()=>[t("t")]),_:1}),s(l,null,{default:a(()=>[t("r")]),_:1}),s(l,null,{default:a(()=>[t("o")]),_:1}),s(l,null,{default:a(()=>[t("p")]),_:1}),s(l,null,{default:a(()=>[t("y")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t(" S^L=2^Entropy ")]),_:1})]),_:1})]),_:1})]),U])])]),B]),F,e("ol",null,[e("li",null,[t("A 16-character password with the character set utilizing only lowercase letters of the modern English alphabet (26 characters) yields approximately 43 sextillion ("),e("span",j,[e("span",W,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("43")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("2")]),_:1})]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("43*10^21")]),_:1})]),_:1})]),_:1})]),J]),t(") possible combinations.")])]),O,e("ol",Q,[e("li",null,[t("A 16-character password with the character set expanded to 96, including uppercase letters and special symbols, inflates the number of possible combinations to a staggering 52 nonillion ("),e("span",Y,[e("span",Z,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("52")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("3")]),_:1})]),_:1}),s(n,null,{default:a(()=>[t("0")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("52*10^30")]),_:1})]),_:1})]),_:1})]),ss]),t("), improving entropy significantly.")])]),as,e("p",null,[t("As can be seen, even by only expanding the character set from 26 to 96 symbols, the number of possible combinations that a malicious party would need to bruteforce has expanded by "),e("span",ts,[e("span",es,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("1.1933")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("9")]),_:1})]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("1.1933*10^9")]),_:1})]),_:1})]),_:1})]),ns]),t(" times.")]),ls,e("ol",null,[e("li",null,[e("p",null,[t("There are "),e("span",ps,[e("span",rs,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("31")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("540")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("000")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("31,540,000")]),_:1})]),_:1})]),_:1})]),os]),t(" seconds in a regular non-leap year. Assuming the worst-case scenario with "),is,t(", at the speed of "),e("span",cs,[e("span",ms,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("300")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("9")]),_:1})]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("300*10^9")]),_:1})]),_:1})]),_:1})]),hs]),t(),us,t(", it would take a single RTX 4090 approximately "),e("span",ds,[e("span",_s,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("4")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("608.83")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("4,608.83")]),_:1})]),_:1})]),_:1})]),gs]),t(" years to crack a 16-character password with a character set of 26 letters of the modern English alphabet.")])]),e("li",null,[e("p",null,[t("If instead of "),fs,t(" we use "),ys,t(", therefore reducing the iteration speed to "),e("span",ws,[e("span",bs,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("200")]),_:1}),s(p,null,{default:a(()=>[t("∗")]),_:1}),s(n,null,{default:a(()=>[t("1")]),_:1}),s(h,null,{default:a(()=>[s(n,null,{default:a(()=>[t("0")]),_:1}),s(n,null,{default:a(()=>[t("3")]),_:1})]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("200*10^3")]),_:1})]),_:1})]),_:1})]),xs]),t(),ks,t(", while also expanding the character set to 96, including uppercase letters and special symbols, the time to crack soars to about "),e("span",Ts,[e("span",vs,[s(c,{xmlns:"http://www.w3.org/1998/Math/MathML"},{default:a(()=>[s(i,null,{default:a(()=>[s(r,null,{default:a(()=>[s(n,null,{default:a(()=>[t("8")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("249")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("887")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("835")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("549")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("662")]),_:1}),s(p,{separator:"true"},{default:a(()=>[t(",")]),_:1}),s(n,null,{default:a(()=>[t("270.456")]),_:1})]),_:1}),s(o,{encoding:"application/x-tex"},{default:a(()=>[t("8,249,887,835,549,662,270.456")]),_:1})]),_:1})]),_:1})]),Ss]),t(" years, far surpassing the age of the universe.")])])]),As,Ps,Ms])}const Hs=_(y,[["render",Is]]);export{Rs as __pageData,Hs as default}; diff --git a/assets/guide_security_public-key-cryptography.md.04b4a080.js b/assets/guide_security_public-key-cryptography.md.04b4a080.js new file mode 100644 index 000000000..49b2c9c09 --- /dev/null +++ b/assets/guide_security_public-key-cryptography.md.04b4a080.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as a,Q as i}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Public Key Cryptography","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/public-key-cryptography.md","filePath":"guide/security/public-key-cryptography.md","lastUpdated":1700563625000}'),r={name:"guide/security/public-key-cryptography.md"},n=i('

Public Key Cryptography

Public key cryptography provides the means for secure communication and data protection, enabling activities such as secure online transactions, encrypted email communications, etc.

Public key cryptography employs a pair of cryptographic keys—a public key and a private key—to create a highly secure method of transmitting information over online networks.

It's easy to make a public key from a private key, but the opposite is rather difficult, if not impossible. This keeps things safe. You can freely share your public key without risking your private key, which remains secure.

Encryption and Signatures

Public key cryptography allows individuals to send encrypted messages and data that can only be deciphered by the intended recipient possessing their corresponding private key. In other words, the public key functions as a lock, and the private key serves as an actual unique key that unlocks the encrypted data.

This encryption process not only ensures the privacy and confidentiality of sensitive information but also establishes the authenticity of the sender. By combining the sender's private key with the public key, a digital signature is created. This signature serves as a digital stamp of approval, verifying the sender's identity and the validity of the transferred data. Anyone with your public key can verify that the person who initiated the transaction used your private key.

Keys on the Client Side

Since every transaction must be signed on behalf of a peer, every operation requires a private key that is kept secret (hence the name). Therefore, the client program must handle both the storage and secure signing of transactions.

WARNING

All clients are different, but iroha_client_cli is the least secure in this regard, as it stores a peer's private key in the multihash format saved to a plain text file that could be overridden with an environment variable.

This is currently a reference implementation that will not be a part of the production release.

One needs to register a user on behalf of another already registered user (just like you need to already have a pair of scissors to cut off the tag from a new one). Suppose that we want to register a user on behalf of mad_hatter@wonderland.

This entails generating a new private key, and sending its public key to the network so that said network can verify that it's indeed the trustworthy mad_hatter@wonderland, and not some impostor (e.g. mad_hatter@wünderbar). In this case, the client application must prompt you, the user, to provide a key pair and verify the authenticity of the transactions: belonging to mad_hatter@wonderland and having a signature derived from the appropriate public key.

For public key cryptography to work effectively, avoid re-using keys when you need to specify a new key. While there's nothing stopping you from doing that, the public keys are public, which means that if an attacker sees the same public key being used, they will know that the private keys are also identical.

Even though private keys operate on slightly different principles than passwords, the advice—to make them as random as possible, never store them unencrypted and never share them with anyone under any circumstances—applies.

',14),o=[n];function s(c,p,h,d,y,l){return t(),a("div",null,o)}const g=e(r,[["render",s]]);export{m as __pageData,g as default}; diff --git a/assets/guide_security_public-key-cryptography.md.04b4a080.lean.js b/assets/guide_security_public-key-cryptography.md.04b4a080.lean.js new file mode 100644 index 000000000..07d1a66b2 --- /dev/null +++ b/assets/guide_security_public-key-cryptography.md.04b4a080.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as a,Q as i}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Public Key Cryptography","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/public-key-cryptography.md","filePath":"guide/security/public-key-cryptography.md","lastUpdated":1700563625000}'),r={name:"guide/security/public-key-cryptography.md"},n=i("",14),o=[n];function s(c,p,h,d,y,l){return t(),a("div",null,o)}const g=e(r,[["render",s]]);export{m as __pageData,g as default}; diff --git a/assets/guide_security_security-principles.md.7b1fddfa.js b/assets/guide_security_security-principles.md.7b1fddfa.js new file mode 100644 index 000000000..9613d4269 --- /dev/null +++ b/assets/guide_security_security-principles.md.7b1fddfa.js @@ -0,0 +1 @@ +import{_ as e,o as i,c as t,Q as a}from"./chunks/framework.1293becd.js";const y=JSON.parse('{"title":"Security Principles","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/security-principles.md","filePath":"guide/security/security-principles.md","lastUpdated":1700563625000}'),s={name:"guide/security/security-principles.md"},r=a('

Security Principles

Organisations and individual users need to work together to ensure secure interactions with Iroha installations. This topic explains the basic principles behind this cooperation.

General Security Principles

  1. Use a Virtual Private Network (VPN):

    • Whenever accessing sensitive data or resources, especially over public networks, use a VPN to establish a secure connection that safeguards your information.
  2. Use a firewall for network protection:

    • Strengthen home and/or office networks by setting up a firewall that helps to counter unauthorized access and protect the connected devices from viruses and malware.
  3. Secure physical and digital information:

    • Safeguard physical documents containing sensitive information in a secure location, and ensure digital documents are encrypted and stored in password-protected folders.
  4. Keep Regular Data Backups:

    • Always have copies of your important information saved somewhere safe. This way, if you lose your data or something goes wrong, you can quickly get everything back on track. Keep these backups in a different secure place from where you usually keep your data.

Security Principles for Individual Users

  1. Adopt robust authentication rules:

    • Utilise strong and unique passwords for all accounts.

    • Never reuse passwords.

    • Set up 2FA whenever possible. 2FA improves the overall security by not only requiring a password, but also an additional factor such as an OTP, fingerprint, or a third-party app-based authentication (e.g., Google Authenticator).

    • Avoid using SMS authentication as the second factor. There is no guarantee that malicious software is not monitoring all of your SMS messages. For example, Android applications cannot be limited to only accessing the messages intended specifically for them.

  2. Exercise caution in digital communication:

    • Set up an email client to sign and verify signatures of all the received emails. While it is possible to impersonate the sender's address and even pose as a bank, it is not possible to fake a signature.

    • Disable both HTML messages and loading of external resources from unknown or unverified addresses.

    • Learn about common phishing techniques to recognise and avoid suspicious emails, links, and requests for personal information.

    • Set up an email client to sign and verify signatures of all the received emails. While it is possible to impersonate the sender's address and even pose as a bank, it is not possible to fake a signature.

  3. Safeguard personal information:

    • When communicating with unfamiliar individuals, especially on the phone or online, be careful about sharing private information.

    • Consider independently researching the individuals or organizations you are communicating with to confirm the legitimacy of their identity.

    • Be mindful of the personal information you share on social media platforms, as malicious parties can exploit this information.

Security Principles for Organisations

  1. Establish clear security policies and procedures:

    • Develop well-defined security policies and protocols for all employees dealing with sensitive data. Thoroughly train employees to adhere to these guidelines, mitigating the risk of negligent actions.

    • Ensure that security policies are accessible to all employees and are regularly reviewed and updated to reflect changing security landscapes.

    • Provide the security policies with examples and scenarios to make them more relatable and actionable for employees.

  2. Cultivate employee awareness:

    • Educate employees about data and operational security measures. Heightened awareness and comprehensive training are pivotal in fortifying organizational security.

    • Encourage employees to report any suspicious activities or security concerns promptly.

  3. Protect physical infrastructure:

    • Restrict physical entry to servers and infrastructure. Set up access controls that only allow authorised personnel to enter restricted areas.

    • Ensure that access control measures are regularly reviewed and updated to align with evolving security needs.

    • Consider implementing biometric access controls for sensitive areas to enhance physical security.

  4. Deploy security monitoring:

    • Enforce a comprehensive security monitoring system that scrutinizes activities and identifies potential security breaches.

    • Implement automated alerts to promptly notify security personnel of any unusual or unauthorized activities.

    • Consider using machine learning algorithms to enhance the system's ability to detect anomalies and potential threats.

    • Employ staff or designate personnel to oversee database security, identify, track and address software vulnerabilities, and conduct regular checks on critical machines for the presence of unauthorized software not included in the approved list.

  5. Conduct recurring security audits:

    • Perform routine security audits to evaluate vulnerabilities and confirm that established security measures align with the commonly-accepted standards and regulations.

    • Consider hiring external security experts for periodic assessments to gain an impartial evaluation of your organization's security condition.

  6. Implement an access control system:

    • Set up a role-based access control system to ensure that employees only have access to the resources and information necessary for their roles.
  7. Embrace Continuous Improvement:

    • Recognize that security is a continuous process. Maintain ongoing assessment of security measures and proactively enhance them to address emerging threats and challenges.

    • Consider establishing a feedback loop that encourages employees to contribute security improvement suggestions, fostering the culture of continuous enhancement.

',8),n=[r];function o(l,c,p,u,d,h){return i(),t("div",null,n)}const f=e(s,[["render",o]]);export{y as __pageData,f as default}; diff --git a/assets/guide_security_security-principles.md.7b1fddfa.lean.js b/assets/guide_security_security-principles.md.7b1fddfa.lean.js new file mode 100644 index 000000000..aa3dc5f62 --- /dev/null +++ b/assets/guide_security_security-principles.md.7b1fddfa.lean.js @@ -0,0 +1 @@ +import{_ as e,o as i,c as t,Q as a}from"./chunks/framework.1293becd.js";const y=JSON.parse('{"title":"Security Principles","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/security-principles.md","filePath":"guide/security/security-principles.md","lastUpdated":1700563625000}'),s={name:"guide/security/security-principles.md"},r=a("",8),n=[r];function o(l,c,p,u,d,h){return i(),t("div",null,n)}const f=e(s,[["render",o]]);export{y as __pageData,f as default}; diff --git a/assets/guide_security_storing-cryptographic-keys.md.213ac358.js b/assets/guide_security_storing-cryptographic-keys.md.213ac358.js new file mode 100644 index 000000000..29aa0c724 --- /dev/null +++ b/assets/guide_security_storing-cryptographic-keys.md.213ac358.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as s,Q as a}from"./chunks/framework.1293becd.js";const r="/iroha-2-docs/assets/KeePassXC.31ae0839.png",o="/iroha-2-docs/assets/keepassxc_ssh_agent.214b8ff0.png",i="/iroha-2-docs/assets/keepassxc_private_key.5eed6c6e.png",n="/iroha-2-docs/assets/keepassxc_pk_agent.fbdff330.png",b=JSON.parse('{"title":"Storing Cryptographic Keys","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/storing-cryptographic-keys.md","filePath":"guide/security/storing-cryptographic-keys.md","lastUpdated":1700563625000}'),c={name:"guide/security/storing-cryptographic-keys.md"},h=a('

Storing Cryptographic Keys

Your sensitive data only remains private if you adopt OPSEC practices to protect the cryptographic keys. Social engineering threats, where someone posing as a figure with authority tries to manipulate you into giving them your private cryptographic key, are real. Always be cautious and avoid sharing your private key, treating it as you would your apartment keys—reserved for trusted individuals only.

For more information on OPSEC and its best practices, see Operational Security.

Storing Cryptographic Keys Digitally

When it comes to protecting cryptographic keys digitally, mainly only two approaches—SSH and GPG—are available. These methods provide layers of security to prevent unauthorized access to your cryptographic keys.

Many of Iroha 2's architectural decisions have been influenced by the principles of the Secure Shell (SSH) protocol, which is why this section primarily focuses on the SSH approach, offering instructions on how to effectively implement the protocol for storing your cryptographic keys within the Iroha 2 ecosystem.

Using SSH and SSH Agent

Secure Shell Protocol (SSH) is a cryptographic network protocol that serves as a virtual gateway, enabling secure access to remote machines via potentially not-so-secure networks by using SSH keys—access credentials. It provides an efficient way to remotely interact with systems without the necessity of physical presence. In this context, SSH offers two primary authentication mechanisms: the conventional password-based approach and the more secure public-private key pair method.

For more information on SSH, see the related SSH Academy topic.

To streamline the login process and bypass the need for repetitive input, it is possible to pair the SSH keys with the SSH Agent (ssh-agent)—the assistant program that remembers your SSH keys and/or password for the duration of a session. This setup permits the SSH gateway to effortlessly access the keys whenever it connects to other machines.

The workflow here is as follows: you have your public key stored on a remote system and keep your private key secure. Whenever you want to access a remote system, the ssh-agent steps in to communicate your public key to the accessed system. The remote system then sends back a challenge that only your private key can properly respond to. Your ssh-agent handles this challenge by using your private key and sends the correct response back to the remote system. If the response matches what the system expected, you're granted access.

The beauty of the ssh-agent is that it holds onto your private key during your session, so there is no need to keep entering your password or private key passphrase every time you connect to a remote system.

For more information on the ssh-agent, see the related SSH Academy topic.

Note

For a detailed overview of the SSH protocol and the ssh-agent tool, see the following SSH Academy topics:

Adding a Password Manager Program

It is recommended to enhance the security of your SSH keys by protecting them with a password, which acts as an additional obstacle in the way of malicious parties aiming to obtain your sensitive information.

A variety of password managers can be used to store user passwords and SSH keys temporarily. For the sake of clarity, KeePass is used as an example password manager, specifically, the KeePassXC port running on Linux-based operating systems.

For instructions on how to set up KeePassXC see the Configuring KeePassXC section below.

KeePassXC:  screen UI

KeePassXC offers enhanced security, flexibility, and control. It not only stores passwords but also the SSH keys. When used for key storage, this password manager provides the ssh-agent with the stored keys, which are then promptly removed from its memory once the KeePassXC window is closed.

TIP

Theoretically, any of the KeePass ports listed on the official website can be utilized for the key storage purposes. We recommend any of the following: KeePassX or KeePassXC.

Configuring KeePassXC

To configure KeePassXC, perform the following steps:

  1. Launch KeePassXC, then go to Tools > Settings, or select the Gear button from the top UI panel.

  2. In the Application Settings tab that appears, select SSH Agent from the left menu, and then select the Enable SSH Agent integration checkbox.

    Show reference screenshot

    KeePassXC  tab: Enabling SSH Agent

  3. Create a new KeePassXC Database. For instructions, see KeePassXC User Guide > Creating Your First Database.

  4. For every key that you would like to store in the KeePassXC Database you created, perform the following steps:

    • Add a new entry in the database. For instructions, see KeePassXC User Guide > Creating Your First Database.

    • When adding a new entry, attach the file containing the key by doing the following: select Advanced from the left menu, then select Add in the Attachments section, choose the required file in the Select files window that appears.

    • When adding a new entry, select SSH Agent from the left menu, then select the key file you added from the Attachment menu in the Private key section; then select the following checkboxes:

      • Add key to agent when database is opened/unlocked

      • Remove key from agent when database is closed/locked

      • Require user confirmation when this key is used

    • If necessary, make other changes to the entry.

    • When ready, select OK to save the entry.

    Show reference screenshots

    KeePassXC  tab: Adding a private key attachment

    KeePassXC  tab: Adding a private key attachment

Expected Results
  • Cryptographic and shh keys are stored as entries in a KeePassXC Database that can be accessed while the KeePassXC window is open.

  • Stored cryptographic and ssh keys can be used whenever they are required for authorization.

  • Stored cryptographic and ssh keys are removed from the ssh-agent once the KeePassXC window is closed.

Note

Without enabling the Require user confirmation when this key is used option, the ssh-agent may not monitor the process that provided it with a key. In the event that the password manager process is terminated by malware or a system service through a SIGKILL signal, the key is likely to remain in the ssh-agent, as Unix system programs cannot intercept SIGKILL.

Storing Cryptographic Keys Physically

For those who seek the highest level of offline security, the option of storing cryptographic keys physically ensures that the keys remain completely disconnected from digital networks, thus minimizing the risk of unauthorized access. Acknowledging the physical option underscores our commitment to catering to diverse security needs.

Using a Hardware Key

Our team considers hardware keys to be one of the best safety measures. A hardware key—a compact device that connects via a USB port and has a size of a typical flash drive—only processes security-related events when it is connected to a machine. This allows you to easily disconnect the device in case of a security breach, or simply reconnect it to a different machine whenever it is required.

However, since there are many brands of hardware keys—each with their unique APIs—it is important to research the market to find the key that best suits your needs.

So far, our team has enternally tested the YubiKey 5C hardware key that proved to have many positive features, including versatile API functionality.

However, there's a potential drawback to consider. Implementing the HMAC challenge-response authentication and storing a corresponding private key for this response could create a vulnerability. This setup might inadvertently enable attackers to make educated guesses about the information stored within the YubiKey 5C's memory, thereby compromising the overall security.

Luckily, this vulnerability can be mitigated by adopting an alternative approach to utilizing the YubiKey 5C. The idea is to use YubiKey 5C to securely access a KeePassXC database storing your cryptographic and SSH keys. This method can even be considered beneficial, since it surpasses the security of most passwords and makes it necessary for the malicious party to be in possession of your hardware key in case the KeePassXC database is leaked.

INFO

To read more about the method above, see the answer by one of the KeePassXC developers—Janek Bevendorff—to the following StackExchange question:

Is it reasonable to use KeePassXC with YubiKey?

Using a Mnemonic Phrase

Alternatively, you can memorize a private key as a series of words, known as a mnemonic phrase. This method, used in many wallets, requires remembering around 25 specific words. You can generate these words using the XKCD password generator.

',38),p=[h];function l(d,g,y,u,m,f){return t(),s("div",null,p)}const w=e(c,[["render",l]]);export{b as __pageData,w as default}; diff --git a/assets/guide_security_storing-cryptographic-keys.md.213ac358.lean.js b/assets/guide_security_storing-cryptographic-keys.md.213ac358.lean.js new file mode 100644 index 000000000..fc7f3a11a --- /dev/null +++ b/assets/guide_security_storing-cryptographic-keys.md.213ac358.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as s,Q as a}from"./chunks/framework.1293becd.js";const r="/iroha-2-docs/assets/KeePassXC.31ae0839.png",o="/iroha-2-docs/assets/keepassxc_ssh_agent.214b8ff0.png",i="/iroha-2-docs/assets/keepassxc_private_key.5eed6c6e.png",n="/iroha-2-docs/assets/keepassxc_pk_agent.fbdff330.png",b=JSON.parse('{"title":"Storing Cryptographic Keys","description":"","frontmatter":{},"headers":[],"relativePath":"guide/security/storing-cryptographic-keys.md","filePath":"guide/security/storing-cryptographic-keys.md","lastUpdated":1700563625000}'),c={name:"guide/security/storing-cryptographic-keys.md"},h=a("",38),p=[h];function l(d,g,y,u,m,f){return t(),s("div",null,p)}const w=e(c,[["render",l]]);export{b as __pageData,w as default}; diff --git a/assets/guide_support.md.b4280b53.js b/assets/guide_support.md.b4280b53.js new file mode 100644 index 000000000..989c835bc --- /dev/null +++ b/assets/guide_support.md.b4280b53.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as r,Q as o}from"./chunks/framework.1293becd.js";const _=JSON.parse('{"title":"Receive support","description":"","frontmatter":{},"headers":[],"relativePath":"guide/support.md","filePath":"guide/support.md","lastUpdated":1700563625000}'),a={name:"guide/support.md"},s=o('

Receive support

From time to time, you may have questions about Iroha that you would like to discuss in detail with others. There are three ways to quickly get in touch with our community: Telegram, Discord, and GitHub.

A large part of the community currently uses Telegram for communication. The Hyperledger part of the team prefers Discord, with two dedicated channels: iroha and iroha-2-contributors. The Discord and Telegram channels are synchronized, so users of both media see your messages.

Finally, you can create a GitHub issue, whether it's a request to update documentation, a suggestion for the core team, or a bug you have found.

',4),i=[s];function c(n,u,d,h,p,l){return t(),r("div",null,i)}const g=e(a,[["render",c]]);export{_ as __pageData,g as default}; diff --git a/assets/guide_support.md.b4280b53.lean.js b/assets/guide_support.md.b4280b53.lean.js new file mode 100644 index 000000000..a68d6cd21 --- /dev/null +++ b/assets/guide_support.md.b4280b53.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as r,Q as o}from"./chunks/framework.1293becd.js";const _=JSON.parse('{"title":"Receive support","description":"","frontmatter":{},"headers":[],"relativePath":"guide/support.md","filePath":"guide/support.md","lastUpdated":1700563625000}'),a={name:"guide/support.md"},s=o("",4),i=[s];function c(n,u,d,h,p,l){return t(),r("div",null,i)}const g=e(a,[["render",c]]);export{_ as __pageData,g as default}; diff --git a/assets/guide_troubleshooting_configuration-issues.md.5529b4ac.js b/assets/guide_troubleshooting_configuration-issues.md.5529b4ac.js new file mode 100644 index 000000000..d9347616e --- /dev/null +++ b/assets/guide_troubleshooting_configuration-issues.md.5529b4ac.js @@ -0,0 +1 @@ +import{_ as e,o,c as t,Q as i}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Troubleshooting Configuration Issues","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/configuration-issues.md","filePath":"guide/troubleshooting/configuration-issues.md","lastUpdated":1700563625000}'),s={name:"guide/troubleshooting/configuration-issues.md"},a=i('

Troubleshooting Configuration Issues

This section offers troubleshooting tips for issues with Iroha 2 configuration. Make sure you checked the keys first, as it is the most common source of issues in Iroha.

If the issue you are experiencing is not described here, contact us via Telegram.

Outdated genesis on a Docker-compose setup

When you are using the Docker-compose version of Iroha, you might encounter the issue of one of the peer containers failing with the Failed to deserialize raw genesis block error. This happens if there is a mismatch between Iroha versions, meaning an Iroha peer cannot be initialized with the given genesis file.

If one of the peers is failing and it's been a while since you pulled the Iroha code for the first time, it's safe to assume the outdated genesis file is the cause. Here is how you can make sure Iroha is working incorrectly for exactly this reason:

  1. Use docker ps to check the current containers. Depending on the version, you will see either hyperledger/iroha2:dev or hyperledger/iroha2:lts containers. Check the number of Iroha peer containers in the docker ps output. By default, there are 4 peers configured in docker-compose.yml for Iroha, although you may have changed that value. You will see that the first container that should have been running Iroha just exited with an error, while three other containers remain active.

  2. Check the logs and look for the Failed to deserialize raw genesis block error. If you started your Iroha in daemon mode with docker compose up -d, use docker compose logs command.

The way to troubleshoot such an issue depends on the use of Iroha.

If this is a basic demo and you don't need the peer data to be restored, you can simply reset the genesis file to its latest state. To do this, use the git checkout configs/peer/genesis.json command.

If you need to restore the Iroha instance data, do the following:

  1. Connect the second Iroha peer that will copy the data from the first (failed) peer.
  2. Wait for the new peer to synchronize the data with the first peer.
  3. Leave the new peer active.
  4. Update the genesis file of the first peer.

INFO

The features needed to monitor the copying progress between peers and a migration tool to update the genesis file are to be implemented in future releases.

Multihash Format of Private and Public Keys

If you look at the client configuration, you will notice that the keys there are given in multi-hash format.

If you've never worked with multi-hash before, it is natural to assume that the right-hand-side is not a hexadecimal representation of the key bytes (two symbols per byte), but rather the bytes encoded as ASCII (or UTF-8), and call from_hex on the string literal in both the public_key and private_key instantiation.

It is also natural to assume that calling PrivateKey::try_from_str on the string literal would yield only the correct key. So if you get the number of bits in the key wrong, e.g. 32 bytes vs 64, that it would raise an error message.

Both of these assumptions are wrong. Unfortunately, the error messages don't help in de-bugging this particular kind of failure.

How to fix: use hex_literal. This will also turn an ugly string of characters into a nice small table of obviously hexadecimal numbers.

WARNING

Even the try_from_str implementation cannot verify if a given string is a valid PrivateKey and warn you if it isn't.

It will catch some obvious errors, e.g. if the string contains an invalid symbol. However, since we aim to support many key formats, it can't do much else. It cannot tell if the key is the correct private key for the given account either, unless you submit an instruction.

These sorts of subtle mistakes can be avoided, for example, by deserialising directly from string literals, or by generating a fresh key-pair in places where it makes sense.

',20),r=[a];function n(h,c,l,d,u,p){return o(),t("div",null,r)}const g=e(s,[["render",n]]);export{m as __pageData,g as default}; diff --git a/assets/guide_troubleshooting_configuration-issues.md.5529b4ac.lean.js b/assets/guide_troubleshooting_configuration-issues.md.5529b4ac.lean.js new file mode 100644 index 000000000..c6197861e --- /dev/null +++ b/assets/guide_troubleshooting_configuration-issues.md.5529b4ac.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as t,Q as i}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Troubleshooting Configuration Issues","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/configuration-issues.md","filePath":"guide/troubleshooting/configuration-issues.md","lastUpdated":1700563625000}'),s={name:"guide/troubleshooting/configuration-issues.md"},a=i("",20),r=[a];function n(h,c,l,d,u,p){return o(),t("div",null,r)}const g=e(s,[["render",n]]);export{m as __pageData,g as default}; diff --git a/assets/guide_troubleshooting_deployment-issues.md.30145f6f.js b/assets/guide_troubleshooting_deployment-issues.md.30145f6f.js new file mode 100644 index 000000000..87cbcef8a --- /dev/null +++ b/assets/guide_troubleshooting_deployment-issues.md.30145f6f.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as o,Q as s}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Troubleshooting Deployment Issues","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/deployment-issues.md","filePath":"guide/troubleshooting/deployment-issues.md","lastUpdated":1700563625000}'),r={name:"guide/troubleshooting/deployment-issues.md"},a=s('

Troubleshooting Deployment Issues

This section offers troubleshooting tips for issues with Iroha 2 deployment. If the issue you are experiencing is not described here, contact us via Telegram.

Docker

TBD

Kubernetes

TBD

',6),n=[a];function i(l,u,d,h,c,p){return t(),o("div",null,n)}const b=e(r,[["render",i]]);export{m as __pageData,b as default}; diff --git a/assets/guide_troubleshooting_deployment-issues.md.30145f6f.lean.js b/assets/guide_troubleshooting_deployment-issues.md.30145f6f.lean.js new file mode 100644 index 000000000..be6025f1a --- /dev/null +++ b/assets/guide_troubleshooting_deployment-issues.md.30145f6f.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as o,Q as s}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Troubleshooting Deployment Issues","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/deployment-issues.md","filePath":"guide/troubleshooting/deployment-issues.md","lastUpdated":1700563625000}'),r={name:"guide/troubleshooting/deployment-issues.md"},a=s("",6),n=[a];function i(l,u,d,h,c,p){return t(),o("div",null,n)}const b=e(r,[["render",i]]);export{m as __pageData,b as default}; diff --git a/assets/guide_troubleshooting_installation-issues.md.687c65b4.js b/assets/guide_troubleshooting_installation-issues.md.687c65b4.js new file mode 100644 index 000000000..4425ca4e5 --- /dev/null +++ b/assets/guide_troubleshooting_installation-issues.md.687c65b4.js @@ -0,0 +1,17 @@ +import{_ as s,o as a,c as o,Q as n}from"./chunks/framework.1293becd.js";const e="/iroha-2-docs/assets/install-troubles.36e7cc47.png",F=JSON.parse('{"title":"Troubleshooting Installation Issues","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/installation-issues.md","filePath":"guide/troubleshooting/installation-issues.md","lastUpdated":1700563625000}'),l={name:"guide/troubleshooting/installation-issues.md"},p=n('

Troubleshooting Installation Issues

This section offers troubleshooting tips for issues with Iroha 2 installation. If the issue you are experiencing is not described here, contact us via Telegram.

Troubleshooting Rust Toolchain

Sometimes, things don’t go as planned. Especially if you had rust on your system a while ago, but didn’t upgrade. A similar problem can occur in Python: XKCD has a famous example of what that might look like:

Untitled

Check Rust version

In the interest of preserving both your and our sanity, make sure that you have the right version of cargo paired with the right version of rustc (1.57 and 1.57) respectively. To show the versions, do

bash
$ cargo -V
+$ cargo 1.60.0 (d1fd9fe 2022-03-01)
$ cargo -V
+$ cargo 1.60.0 (d1fd9fe 2022-03-01)

and then

bash
$ rustc --version
+$ rustc 1.60.0 (7737e0b5c 2022-04-04)
$ rustc --version
+$ rustc 1.60.0 (7737e0b5c 2022-04-04)

If you have higher versions, you're fine. If you have lower versions, you can run the following command to update it:

bash
$ rustup toolchain update stable
$ rustup toolchain update stable

Check installation location

If you get lower version numbers and you updated the toolchain and it didn’t work… let’s just say it’s a common problem, but it doesn’t have a common solution.

Firstly, you should establish where the version that you want to use is installed:

bash
$ rustup which rustc
+$ rustup which cargo
$ rustup which rustc
+$ rustup which cargo

User installations of the toolchains are usually in ~/.rustup/toolchains/stable-*/bin/. If that is the case, you should be able to run

bash
$ rustup toolchain update stable
$ rustup toolchain update stable

and that should fix your problems.

Check the default Rust version

Another option is that you have the up-to-date stable toolchain, but it is not set as the default. Run:

bash
$ rustup default stable
$ rustup default stable

This can happen if you installed a nightly version, or set a specific Rust version, but forgot to un-set it.

Check if there are other Rust versions

Continuing down the troubleshooting rabbit-hole, we could have shell aliases:

bash
$ type rustc
+$ type cargo
$ type rustc
+$ type cargo

If these point to locations other than the one you saw when running rustup which *, then you have a problem. Note that it’s not enough to just

bash
$ alias rustc "~/.rustup/toolchains/stable-*/bin/rustc"
+$ alias cargo "~/.rustup/toolchains/stable-*/bin/cargo"
$ alias rustc "~/.rustup/toolchains/stable-*/bin/rustc"
+$ alias cargo "~/.rustup/toolchains/stable-*/bin/cargo"

because there is an internal logic that could break, regardless of how you re-arrange your shell aliases.

The simplest solution would be to remove the versions that you don’t use.

It’s easier said than done, however, since it entails tracking all the versions of rustup installed and available to you. Usually, there are only two: the system package manager version and the one that got installed into the standard location in your home folder when you ran the command in the beginning of this tutorial. For the former, consult your (Linux) distribution’s manual, (apt remove rust). For the latter, run:

bash
$ rustup toolchain list
$ rustup toolchain list

And then, for every <toolchain> (without the angle brackets of course):

bash
$ rustup remove <toolchain>
$ rustup remove <toolchain>

After that, make sure that

bash
$ cargo --help
$ cargo --help

results in a command-not-found error, i.e. that you have no active Rust toolchain installed. Then, run:

bash
$ rustup toolchain install stable
$ rustup toolchain install stable

Troubleshooting Python toolchain

When you install the Python Wheel package using pip on the "client setup" step, you may encounter an error like: "iroha_python-*.whl is not a supported wheel on this platform".

This error means that pip is outdated, so you need to update it. First of all, it is recommended to check your OS for updates and perform a system upgrade.

If this doesn't work, you can try updating pip for your user directory.

python -m pip install --upgrade pip

Make sure that pip that is installed in your home directory. To do this, run whereis pip and check if /home/username/.local/bin/pip is among the paths. If not, update your shell's PATH variable.

If the issue persists, please contact us and report the outputs.

python --version
+python3 --version
+pip --version
+pip3 --version
python --version
+python3 --version
+pip --version
+pip3 --version
`,46),t=[p];function c(r,i,h,y,u,d){return a(),o("div",null,t)}const g=s(l,[["render",c]]);export{F as __pageData,g as default}; diff --git a/assets/guide_troubleshooting_installation-issues.md.687c65b4.lean.js b/assets/guide_troubleshooting_installation-issues.md.687c65b4.lean.js new file mode 100644 index 000000000..7cbbb2a02 --- /dev/null +++ b/assets/guide_troubleshooting_installation-issues.md.687c65b4.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as o,Q as n}from"./chunks/framework.1293becd.js";const e="/iroha-2-docs/assets/install-troubles.36e7cc47.png",F=JSON.parse('{"title":"Troubleshooting Installation Issues","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/installation-issues.md","filePath":"guide/troubleshooting/installation-issues.md","lastUpdated":1700563625000}'),l={name:"guide/troubleshooting/installation-issues.md"},p=n("",46),t=[p];function c(r,i,h,y,u,d){return a(),o("div",null,t)}const g=s(l,[["render",c]]);export{F as __pageData,g as default}; diff --git a/assets/guide_troubleshooting_integration-issues.md.4e503ab9.js b/assets/guide_troubleshooting_integration-issues.md.4e503ab9.js new file mode 100644 index 000000000..1b26a4939 --- /dev/null +++ b/assets/guide_troubleshooting_integration-issues.md.4e503ab9.js @@ -0,0 +1 @@ +import{_ as s,o,c as i,k as e,a as t}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Troubleshooting Integration Issues","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/integration-issues.md","filePath":"guide/troubleshooting/integration-issues.md","lastUpdated":1700563625000}'),n={name:"guide/troubleshooting/integration-issues.md"},r=e("h1",{id:"troubleshooting-integration-issues",tabindex:"-1"},[t("Troubleshooting Integration Issues "),e("a",{class:"header-anchor",href:"#troubleshooting-integration-issues","aria-label":'Permalink to "Troubleshooting Integration Issues"'},"​")],-1),a=e("p",null,[t("This section offers troubleshooting tips for issues with Iroha 2 integration. If the issue you are experiencing is not described here, contact us via "),e("a",{href:"https://t.me/hyperledgeriroha",target:"_blank",rel:"noreferrer"},"Telegram"),t(".")],-1),l=e("p",null,[e("strong",null,"TBD")],-1),u=[r,a,l];function h(c,g,d,_,p,f){return o(),i("div",null,u)}const I=s(n,[["render",h]]);export{m as __pageData,I as default}; diff --git a/assets/guide_troubleshooting_integration-issues.md.4e503ab9.lean.js b/assets/guide_troubleshooting_integration-issues.md.4e503ab9.lean.js new file mode 100644 index 000000000..1b26a4939 --- /dev/null +++ b/assets/guide_troubleshooting_integration-issues.md.4e503ab9.lean.js @@ -0,0 +1 @@ +import{_ as s,o,c as i,k as e,a as t}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Troubleshooting Integration Issues","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/integration-issues.md","filePath":"guide/troubleshooting/integration-issues.md","lastUpdated":1700563625000}'),n={name:"guide/troubleshooting/integration-issues.md"},r=e("h1",{id:"troubleshooting-integration-issues",tabindex:"-1"},[t("Troubleshooting Integration Issues "),e("a",{class:"header-anchor",href:"#troubleshooting-integration-issues","aria-label":'Permalink to "Troubleshooting Integration Issues"'},"​")],-1),a=e("p",null,[t("This section offers troubleshooting tips for issues with Iroha 2 integration. If the issue you are experiencing is not described here, contact us via "),e("a",{href:"https://t.me/hyperledgeriroha",target:"_blank",rel:"noreferrer"},"Telegram"),t(".")],-1),l=e("p",null,[e("strong",null,"TBD")],-1),u=[r,a,l];function h(c,g,d,_,p,f){return o(),i("div",null,u)}const I=s(n,[["render",h]]);export{m as __pageData,I as default}; diff --git a/assets/guide_troubleshooting_overview.md.639dbf2e.js b/assets/guide_troubleshooting_overview.md.639dbf2e.js new file mode 100644 index 000000000..f336a1f22 --- /dev/null +++ b/assets/guide_troubleshooting_overview.md.639dbf2e.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as s,Q as o}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Troubleshooting","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/overview.md","filePath":"guide/troubleshooting/overview.md","lastUpdated":1700563625000}'),i={name:"guide/troubleshooting/overview.md"},r=o('

Troubleshooting

This section is intended to help if you encounter issues while working with Iroha. If something goes wrong, please check the keys first. If that doesn't help, check the troubleshooting instructions for each stage:

If the issue you are experiencing is not described here, contact us via Telegram.

Check the keys

Most issues arise as a result of unmatched keys. This is why we recommend to follow this rule: If something goes wrong, check the keys first.

Here's a quick explanation: It is not possible to differentiate the error messages that arise when peers' keys do not match the keys in the array of trusted peers, because it would expose the peers' public key. As such, if you have Helm charts or K8s deployed with keys defined via the environment variables, you should check for Key definitions.

If in doubt, generate a new pair of keys.

',8),a=[r];function n(h,l,c,u,d,p){return t(),s("div",null,a)}const k=e(i,[["render",n]]);export{g as __pageData,k as default}; diff --git a/assets/guide_troubleshooting_overview.md.639dbf2e.lean.js b/assets/guide_troubleshooting_overview.md.639dbf2e.lean.js new file mode 100644 index 000000000..5bc942910 --- /dev/null +++ b/assets/guide_troubleshooting_overview.md.639dbf2e.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as s,Q as o}from"./chunks/framework.1293becd.js";const g=JSON.parse('{"title":"Troubleshooting","description":"","frontmatter":{},"headers":[],"relativePath":"guide/troubleshooting/overview.md","filePath":"guide/troubleshooting/overview.md","lastUpdated":1700563625000}'),i={name:"guide/troubleshooting/overview.md"},r=o("",8),a=[r];function n(h,l,c,u,d,p){return t(),s("div",null,a)}const k=e(i,[["render",n]]);export{g as __pageData,k as default}; diff --git a/assets/index.md.c38d1cfb.js b/assets/index.md.c38d1cfb.js new file mode 100644 index 000000000..36eb487a5 --- /dev/null +++ b/assets/index.md.c38d1cfb.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as n}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"Iroha 2 Documentation","tagline":"Fully-featured blockchain ledger"},"features":[{"icon":"🚀","title":"Get Started","details":"Install and build Iroha, follow step-by-step SDK tutorials to start working with Iroha","link":"/guide/get-started/"},{"icon":"📖","title":"Guide","details":"Learn about Iroha functionality in great details and understand the underlying principles","link":"/guide/introduction"},{"icon":"💻","title":"Reference","details":"Consult reference documentation for extensive information about available functionality","link":"/reference/torii-endpoints"}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1700563625000}'),a={name:"index.md"};function i(o,r,l,d,s,c){return t(),n("div")}const p=e(a,[["render",i]]);export{f as __pageData,p as default}; diff --git a/assets/index.md.c38d1cfb.lean.js b/assets/index.md.c38d1cfb.lean.js new file mode 100644 index 000000000..36eb487a5 --- /dev/null +++ b/assets/index.md.c38d1cfb.lean.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as n}from"./chunks/framework.1293becd.js";const f=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"Iroha 2 Documentation","tagline":"Fully-featured blockchain ledger"},"features":[{"icon":"🚀","title":"Get Started","details":"Install and build Iroha, follow step-by-step SDK tutorials to start working with Iroha","link":"/guide/get-started/"},{"icon":"📖","title":"Guide","details":"Learn about Iroha functionality in great details and understand the underlying principles","link":"/guide/introduction"},{"icon":"💻","title":"Reference","details":"Consult reference documentation for extensive information about available functionality","link":"/reference/torii-endpoints"}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1700563625000}'),a={name:"index.md"};function i(o,r,l,d,s,c){return t(),n("div")}const p=e(a,[["render",i]]);export{f as __pageData,p as default}; diff --git a/assets/install-troubles.36e7cc47.png b/assets/install-troubles.36e7cc47.png new file mode 100644 index 000000000..031335ad5 Binary files /dev/null and b/assets/install-troubles.36e7cc47.png differ diff --git a/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2 b/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2 new file mode 100644 index 000000000..2a6872967 Binary files /dev/null and b/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2 differ diff --git a/assets/inter-italic-cyrillic.ea42a392.woff2 b/assets/inter-italic-cyrillic.ea42a392.woff2 new file mode 100644 index 000000000..f64035158 Binary files /dev/null and b/assets/inter-italic-cyrillic.ea42a392.woff2 differ diff --git a/assets/inter-italic-greek-ext.4fbe9427.woff2 b/assets/inter-italic-greek-ext.4fbe9427.woff2 new file mode 100644 index 000000000..002189603 Binary files /dev/null and b/assets/inter-italic-greek-ext.4fbe9427.woff2 differ diff --git a/assets/inter-italic-greek.8f4463c4.woff2 b/assets/inter-italic-greek.8f4463c4.woff2 new file mode 100644 index 000000000..71c265f85 Binary files /dev/null and b/assets/inter-italic-greek.8f4463c4.woff2 differ diff --git a/assets/inter-italic-latin-ext.bd8920cc.woff2 b/assets/inter-italic-latin-ext.bd8920cc.woff2 new file mode 100644 index 000000000..9c1b9440e Binary files /dev/null and b/assets/inter-italic-latin-ext.bd8920cc.woff2 differ diff --git a/assets/inter-italic-latin.bd3b6f56.woff2 b/assets/inter-italic-latin.bd3b6f56.woff2 new file mode 100644 index 000000000..01fcf2072 Binary files /dev/null and b/assets/inter-italic-latin.bd3b6f56.woff2 differ diff --git a/assets/inter-italic-vietnamese.6ce511fb.woff2 b/assets/inter-italic-vietnamese.6ce511fb.woff2 new file mode 100644 index 000000000..e4f788ee0 Binary files /dev/null and b/assets/inter-italic-vietnamese.6ce511fb.woff2 differ diff --git a/assets/inter-roman-cyrillic-ext.e75737ce.woff2 b/assets/inter-roman-cyrillic-ext.e75737ce.woff2 new file mode 100644 index 000000000..28593ccb8 Binary files /dev/null and b/assets/inter-roman-cyrillic-ext.e75737ce.woff2 differ diff --git a/assets/inter-roman-cyrillic.5f2c6c8c.woff2 b/assets/inter-roman-cyrillic.5f2c6c8c.woff2 new file mode 100644 index 000000000..a20adc161 Binary files /dev/null and b/assets/inter-roman-cyrillic.5f2c6c8c.woff2 differ diff --git a/assets/inter-roman-greek-ext.ab0619bc.woff2 b/assets/inter-roman-greek-ext.ab0619bc.woff2 new file mode 100644 index 000000000..e3b0be76d Binary files /dev/null and b/assets/inter-roman-greek-ext.ab0619bc.woff2 differ diff --git a/assets/inter-roman-greek.d5a6d92a.woff2 b/assets/inter-roman-greek.d5a6d92a.woff2 new file mode 100644 index 000000000..f790e047d Binary files /dev/null and b/assets/inter-roman-greek.d5a6d92a.woff2 differ diff --git a/assets/inter-roman-latin-ext.0030eebd.woff2 b/assets/inter-roman-latin-ext.0030eebd.woff2 new file mode 100644 index 000000000..715bd903b Binary files /dev/null and b/assets/inter-roman-latin-ext.0030eebd.woff2 differ diff --git a/assets/inter-roman-latin.2ed14f66.woff2 b/assets/inter-roman-latin.2ed14f66.woff2 new file mode 100644 index 000000000..a540b7afe Binary files /dev/null and b/assets/inter-roman-latin.2ed14f66.woff2 differ diff --git a/assets/inter-roman-vietnamese.14ce25a6.woff2 b/assets/inter-roman-vietnamese.14ce25a6.woff2 new file mode 100644 index 000000000..5a9f9cb9c Binary files /dev/null and b/assets/inter-roman-vietnamese.14ce25a6.woff2 differ diff --git a/assets/iroha_java_commits.a73a8bf6.png b/assets/iroha_java_commits.a73a8bf6.png new file mode 100644 index 000000000..33fe2c869 Binary files /dev/null and b/assets/iroha_java_commits.a73a8bf6.png differ diff --git a/assets/iroha_java_hash.c677e97a.png b/assets/iroha_java_hash.c677e97a.png new file mode 100644 index 000000000..c82346c18 Binary files /dev/null and b/assets/iroha_java_hash.c677e97a.png differ diff --git a/assets/keepassxc_pk_agent.fbdff330.png b/assets/keepassxc_pk_agent.fbdff330.png new file mode 100644 index 000000000..fdd07dadf Binary files /dev/null and b/assets/keepassxc_pk_agent.fbdff330.png differ diff --git a/assets/keepassxc_private_key.5eed6c6e.png b/assets/keepassxc_private_key.5eed6c6e.png new file mode 100644 index 000000000..297f94409 Binary files /dev/null and b/assets/keepassxc_private_key.5eed6c6e.png differ diff --git a/assets/keepassxc_ssh_agent.214b8ff0.png b/assets/keepassxc_ssh_agent.214b8ff0.png new file mode 100644 index 000000000..9ae656270 Binary files /dev/null and b/assets/keepassxc_ssh_agent.214b8ff0.png differ diff --git a/assets/reference_compatibility-matrix.md.592c45f9.js b/assets/reference_compatibility-matrix.md.592c45f9.js new file mode 100644 index 000000000..0853bd6f8 --- /dev/null +++ b/assets/reference_compatibility-matrix.md.592c45f9.js @@ -0,0 +1 @@ +import{_ as n,C as o,o as r,c as l,k as t,a as e,H as i}from"./chunks/framework.1293becd.js";const C=JSON.parse('{"title":"Compatibility Matrix","description":"","frontmatter":{},"headers":[],"relativePath":"reference/compatibility-matrix.md","filePath":"reference/compatibility-matrix.md","lastUpdated":1700563625000}'),c={name:"reference/compatibility-matrix.md"},m=t("h1",{id:"compatibility-matrix",tabindex:"-1"},[e("Compatibility Matrix "),t("a",{class:"header-anchor",href:"#compatibility-matrix","aria-label":'Permalink to "Compatibility Matrix"'},"​")],-1),d=t("p",null,[e("In our continuous efforts to provide clear documentation and to ensure seamless compatibility across multiple SDKs, we present the "),t("strong",null,"SDK Compatibility Matrix"),e(". This matrix provides an instantaneous overview of how different stories, sourced from TestOps API, fare across varying SDKs.")],-1),p=t("p",null,"The matrix consists of:",-1),u=t("li",null,[t("strong",null,"Stories"),e(": Represented in the first column of the matrix, these are directly fetched from the TestOps API.")],-1),h=t("li",null,[t("strong",null,"SDKs"),e(': Each subsequent column represents an SDK, such as "Java/Kotlin", "JavaScript", "Swift", etc.')],-1),f=t("strong",null,"Status Symbols",-1),b=t("div",{class:"info custom-block"},[t("p",{class:"custom-block-title"},"INFO"),t("p",null,[e("The data for this matrix is retrieved dynamically from our "),t("a",{href:"https://github.com/soramitsu/iroha2-docs-compat-matrix-service",target:"_blank",rel:"noreferrer"},"backend service"),e(", balancing the latest information with a swift response for documentation readers.")])],-1);function _(x,y,v,g,k,S){const a=o("CompatibilityMatrixTableIcon"),s=o("CompatibilityMatrixTable");return r(),l("div",null,[m,d,p,t("ul",null,[u,h,t("li",null,[f,e(": The status of each story for an SDK is denoted with: "),t("ul",null,[t("li",null,[i(a,{status:"ok",class:"inline-block relative -top-0.5"}),e(" indicating the story passed.")]),t("li",null,[i(a,{status:"failed",class:"inline-block relative -top-0.5"}),e(" indicating the story failed to pass.")]),t("li",null,[i(a,{status:"no-data",class:"inline-block relative -top-0.5"}),e(" indicating the data is missing.")])])])]),i(s),b])}const M=n(c,[["render",_]]);export{C as __pageData,M as default}; diff --git a/assets/reference_compatibility-matrix.md.592c45f9.lean.js b/assets/reference_compatibility-matrix.md.592c45f9.lean.js new file mode 100644 index 000000000..0853bd6f8 --- /dev/null +++ b/assets/reference_compatibility-matrix.md.592c45f9.lean.js @@ -0,0 +1 @@ +import{_ as n,C as o,o as r,c as l,k as t,a as e,H as i}from"./chunks/framework.1293becd.js";const C=JSON.parse('{"title":"Compatibility Matrix","description":"","frontmatter":{},"headers":[],"relativePath":"reference/compatibility-matrix.md","filePath":"reference/compatibility-matrix.md","lastUpdated":1700563625000}'),c={name:"reference/compatibility-matrix.md"},m=t("h1",{id:"compatibility-matrix",tabindex:"-1"},[e("Compatibility Matrix "),t("a",{class:"header-anchor",href:"#compatibility-matrix","aria-label":'Permalink to "Compatibility Matrix"'},"​")],-1),d=t("p",null,[e("In our continuous efforts to provide clear documentation and to ensure seamless compatibility across multiple SDKs, we present the "),t("strong",null,"SDK Compatibility Matrix"),e(". This matrix provides an instantaneous overview of how different stories, sourced from TestOps API, fare across varying SDKs.")],-1),p=t("p",null,"The matrix consists of:",-1),u=t("li",null,[t("strong",null,"Stories"),e(": Represented in the first column of the matrix, these are directly fetched from the TestOps API.")],-1),h=t("li",null,[t("strong",null,"SDKs"),e(': Each subsequent column represents an SDK, such as "Java/Kotlin", "JavaScript", "Swift", etc.')],-1),f=t("strong",null,"Status Symbols",-1),b=t("div",{class:"info custom-block"},[t("p",{class:"custom-block-title"},"INFO"),t("p",null,[e("The data for this matrix is retrieved dynamically from our "),t("a",{href:"https://github.com/soramitsu/iroha2-docs-compat-matrix-service",target:"_blank",rel:"noreferrer"},"backend service"),e(", balancing the latest information with a swift response for documentation readers.")])],-1);function _(x,y,v,g,k,S){const a=o("CompatibilityMatrixTableIcon"),s=o("CompatibilityMatrixTable");return r(),l("div",null,[m,d,p,t("ul",null,[u,h,t("li",null,[f,e(": The status of each story for an SDK is denoted with: "),t("ul",null,[t("li",null,[i(a,{status:"ok",class:"inline-block relative -top-0.5"}),e(" indicating the story passed.")]),t("li",null,[i(a,{status:"failed",class:"inline-block relative -top-0.5"}),e(" indicating the story failed to pass.")]),t("li",null,[i(a,{status:"no-data",class:"inline-block relative -top-0.5"}),e(" indicating the data is missing.")])])])]),i(s),b])}const M=n(c,[["render",_]]);export{C as __pageData,M as default}; diff --git a/assets/reference_data-model-schema.md.ea74a419.js b/assets/reference_data-model-schema.md.ea74a419.js new file mode 100644 index 000000000..45696aec3 --- /dev/null +++ b/assets/reference_data-model-schema.md.ea74a419.js @@ -0,0 +1 @@ +import{_ as t,o as e,c as a,Q as d}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Data Model Schema","description":"","frontmatter":{},"headers":[],"relativePath":"reference/data-model-schema.md","filePath":"reference/data-model-schema.md","lastUpdated":1700563625000}'),r={name:"reference/data-model-schema.md"},l=d('

Data Model Schema

TODO: explain what this page contains

Account

Type: Struct

Declarations:

Field nameField value
idAccountId
assetsSortedMap<AssetId, Asset>
signatoriesSortedVec<PublicKey>
signature_check_conditionSignatureCheckCondition
metadataMetadata
rolesSortedVec<RoleId>

AccountEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AssetAssetEvent0
CreatedAccount1
DeletedAccountId2
AuthenticationAddedAccountId3
AuthenticationRemovedAccountId4
PermissionAddedAccountPermissionChanged5
PermissionRemovedAccountPermissionChanged6
RoleRevokedAccountRoleChanged7
RoleGrantedAccountRoleChanged8
MetadataInsertedMetadataChanged<AccountId>9
MetadataRemovedMetadataChanged<AccountId>10

AccountEventFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ByCreated0
ByDeleted1
ByAuthenticationAdded2
ByAuthenticationRemoved3
ByPermissionAdded4
ByPermissionRemoved5
ByRoleRevoked6
ByRoleGranted7
ByMetadataInserted8
ByMetadataRemoved9
ByAssetFilterOpt<AssetFilter>10

AccountFilter

Type: Struct

Declarations:

Field nameField value
origin_filterFilterOpt<OriginFilter<AccountEvent>>
event_filterFilterOpt<AccountEventFilter>

AccountId

Type: Struct

Declarations:

Field nameField value
nameName
domain_idDomainId

AccountPermissionChanged

Type: Struct

Declarations:

Field nameField value
account_idAccountId
permission_idName

AccountRoleChanged

Type: Struct

Declarations:

Field nameField value
account_idAccountId
role_idRoleId

Action<TriggeringFilterBox, Executable>

Type: Struct

Declarations:

Field nameField value
executableExecutable
repeatsRepeats
authorityAccountId
filterTriggeringFilterBox
metadataMetadata

Action<TriggeringFilterBox, OptimizedExecutable>

Type: Struct

Declarations:

Field nameField value
executableOptimizedExecutable
repeatsRepeats
authorityAccountId
filterTriggeringFilterBox
metadataMetadata

Add

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<NumericValue>
rightEvaluatesTo<NumericValue>

Algorithm

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Ed255190
Secp256k11
BlsNormal2
BlsSmall3

And

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<bool>
rightEvaluatesTo<bool>

Array<Interval<u16>, 8>

Type: Array

Length: 8

Value: Interval<u16>

Array<Interval<u8>, 4>

Type: Array

Length: 4

Value: Interval<u8>

Array<u16, 8>

Type: Array

Length: 8

Value: u16

Array<u8, 32>

Type: Array

Length: 32

Value: u8

Array<u8, 4>

Type: Array

Length: 4

Value: u8

Asset

Type: Struct

Declarations:

Field nameField value
idAssetId
valueAssetValue

AssetChanged

Type: Struct

Declarations:

Field nameField value
asset_idAssetId
amountAssetValue

AssetDefinition

Type: Struct

Declarations:

Field nameField value
idAssetDefinitionId
value_typeAssetValueType
mintableMintable
logoOption<IpfsPath>
metadataMetadata
owned_byAccountId

AssetDefinitionEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
CreatedAssetDefinition0
MintabilityChangedAssetDefinitionId1
OwnerChangedAssetDefinitionOwnerChanged2
DeletedAssetDefinitionId3
MetadataInsertedMetadataChanged<AssetDefinitionId>4
MetadataRemovedMetadataChanged<AssetDefinitionId>5
TotalQuantityChangedAssetDefinitionTotalQuantityChanged6

AssetDefinitionEventFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ByCreated0
ByMintabilityChanged1
ByOwnerChanged2
ByDeleted3
ByMetadataInserted4
ByMetadataRemoved5
ByTotalQuantityChanged6

AssetDefinitionFilter

Type: Struct

Declarations:

Field nameField value
origin_filterFilterOpt<OriginFilter<AssetDefinitionEvent>>
event_filterFilterOpt<AssetDefinitionEventFilter>

AssetDefinitionId

Type: Struct

Declarations:

Field nameField value
nameName
domain_idDomainId

AssetDefinitionOwnerChanged

Type: Struct

Declarations:

Field nameField value
asset_definition_idAssetDefinitionId
new_ownerAccountId

AssetDefinitionTotalQuantityChanged

Type: Struct

Declarations:

Field nameField value
asset_definition_idAssetDefinitionId
total_amountNumericValue

AssetEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
CreatedAsset0
DeletedAssetId1
AddedAssetChanged2
RemovedAssetChanged3
MetadataInsertedMetadataChanged<AssetId>4
MetadataRemovedMetadataChanged<AssetId>5

AssetEventFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ByCreated0
ByDeleted1
ByAdded2
ByRemoved3
ByMetadataInserted4
ByMetadataRemoved5

AssetFilter

Type: Struct

Declarations:

Field nameField value
origin_filterFilterOpt<OriginFilter<AssetEvent>>
event_filterFilterOpt<AssetEventFilter>

AssetId

Type: Struct

Declarations:

Field nameField value
definition_idAssetDefinitionId
account_idAccountId

AssetValue

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Quantityu320
BigQuantityu1281
FixedFixed2
StoreMetadata3

AssetValueType

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Quantity0
BigQuantity1
Fixed2
Store3

AtIndex

Type: Struct

Declarations:

Field nameField value
indexu32
predicateValuePredicate

BatchedResponse<Value>

Type: Struct

Declarations:

Field nameField value
batchValue
cursorForwardCursor

BatchedResponse<Vec<VersionedSignedTransaction>>

Type: Struct

Declarations:

Field nameField value
batchVec<VersionedSignedTransaction>
cursorForwardCursor

BinaryOpIncompatibleNumericValueTypesError

Type: Struct

Declarations:

Field nameField value
leftNumericValue
rightNumericValue

BlockHeader

Type: Struct

Declarations:

Field nameField value
heightu64
timestamp_msu64
previous_block_hashOption<HashOf<VersionedSignedBlock>>
transactions_hashOption<HashOf<MerkleTree<VersionedSignedTransaction>>>
commit_topologyVec<PeerId>
view_change_indexu64
consensus_estimation_msu64

BlockMessage

Type: Alias

To: VersionedSignedBlock

BlockPayload

Type: Struct

Declarations:

Field nameField value
headerBlockHeader
transactionsVec<TransactionValue>
event_recommendationsVec<Event>

BlockRejectionReason

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ConsensusBlockRejection0

BlockSubscriptionRequest

Type: Alias

To: NonZero<u64>

BurnBox

Type: Struct

Declarations:

Field nameField value
objectEvaluatesTo<Value>
destination_idEvaluatesTo<IdBox>

Conditional

Type: Struct

Declarations:

Field nameField value
conditionEvaluatesTo<bool>
thenInstructionBox
otherwiseOption<InstructionBox>

ConfigurationEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ChangedParameterId0
CreatedParameterId1
DeletedParameterId2

Container

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AnyValuePredicate0
AllValuePredicate1
AtIndexAtIndex2
ValueOfKeyValueOfKey3
HasKeyName4

Contains

Type: Struct

Declarations:

Field nameField value
collectionEvaluatesTo<Vec<Value>>
elementEvaluatesTo<Value>

ContainsAll

Type: Struct

Declarations:

Field nameField value
collectionEvaluatesTo<Vec<Value>>
elementsEvaluatesTo<Vec<Value>>

ContainsAny

Type: Struct

Declarations:

Field nameField value
collectionEvaluatesTo<Vec<Value>>
elementsEvaluatesTo<Vec<Value>>

ContextValue

Type: Struct

Declarations:

Field nameField value
value_nameName

DataEntityFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ByPeerFilterOpt<PeerFilter>0
ByDomainFilterOpt<DomainFilter>1
ByAccountFilterOpt<AccountFilter>2
ByAssetDefinitionFilterOpt<AssetDefinitionFilter>3
ByAssetFilterOpt<AssetFilter>4
ByTriggerFilterOpt<TriggerFilter>5
ByRoleFilterOpt<RoleFilter>6

DataEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
PeerPeerEvent0
DomainDomainEvent1
AccountAccountEvent2
AssetDefinitionAssetDefinitionEvent3
AssetAssetEvent4
TriggerTriggerEvent5
RoleRoleEvent6
PermissionTokenPermissionTokenSchemaUpdateEvent7
ConfigurationConfigurationEvent8
ValidatorValidatorEvent9

Divide

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<NumericValue>
rightEvaluatesTo<NumericValue>

Domain

Type: Struct

Declarations:

Field nameField value
idDomainId
accountsSortedMap<AccountId, Account>
asset_definitionsSortedMap<AssetDefinitionId, AssetDefinition>
asset_total_quantitiesSortedMap<AssetDefinitionId, NumericValue>
logoOption<IpfsPath>
metadataMetadata

DomainEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AccountAccountEvent0
AssetDefinitionAssetDefinitionEvent1
CreatedDomain2
DeletedDomainId3
MetadataInsertedMetadataChanged<DomainId>4
MetadataRemovedMetadataChanged<DomainId>5

DomainEventFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ByCreated0
ByDeleted1
ByMetadataInserted2
ByMetadataRemoved3
ByAccountFilterOpt<AccountFilter>4
ByAssetDefinitionFilterOpt<AssetDefinitionFilter>5

DomainFilter

Type: Struct

Declarations:

Field nameField value
origin_filterFilterOpt<OriginFilter<DomainEvent>>
event_filterFilterOpt<DomainEventFilter>

DomainId

Type: Struct

Declarations:

Field nameField value
nameName

Duration

Type: Tuple

Values: (u64, u32)

Equal

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<Value>
rightEvaluatesTo<Value>

EvaluatesTo<AccountId>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<AssetDefinitionId>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<AssetId>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<DomainId>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<HashOf<VersionedSignedBlock>>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<HashOf<VersionedSignedTransaction>>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<IdBox>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<Level>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<Name>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<NumericValue>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<Parameter>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<RegistrableBox>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<RoleId>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<String>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<TriggerId>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<UpgradableBox>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<Value>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<Vec<Value>>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluatesTo<bool>

Type: Struct

Declarations:

Field nameField value
expressionExpression

EvaluationError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
MathMathError0
ValidationValidationFail1
FindString2
ConversionString3

Event

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
PipelinePipelineEvent0
DataDataEvent1
TimeTimeEvent2
ExecuteTriggerExecuteTriggerEvent3
NotificationNotificationEvent4

EventMessage

Type: Alias

To: Event

EventSubscriptionRequest

Type: Alias

To: FilterBox

Executable

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
InstructionsVec<InstructionBox>0
WasmWasmSmartContract1

ExecuteTriggerBox

Type: Struct

Declarations:

Field nameField value
trigger_idEvaluatesTo<TriggerId>

ExecuteTriggerEvent

Type: Struct

Declarations:

Field nameField value
trigger_idTriggerId
authorityAccountId

ExecuteTriggerEventFilter

Type: Struct

Declarations:

Field nameField value
trigger_idTriggerId
authorityAccountId

ExecutionTime

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
PreCommit0
ScheduleSchedule1

Expression

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AddAdd0
SubtractSubtract1
MultiplyMultiply2
DivideDivide3
ModMod4
RaiseToRaiseTo5
GreaterGreater6
LessLess7
EqualEqual8
NotNot9
AndAnd10
OrOr11
IfIf12
RawValue13
QueryQueryBox14
ContainsContains15
ContainsAllContainsAll16
ContainsAnyContainsAny17
WhereWhere18
ContextValueContextValue19

FailBox

Type: Struct

Declarations:

Field nameField value
messageString

FilterBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
PipelinePipelineEventFilter0
DataFilterOpt<DataEntityFilter>1
TimeTimeEventFilter2
ExecuteTriggerExecuteTriggerEventFilter3
NotificationNotificationEventFilter4

FilterOpt<AccountEventFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeAccountEventFilter1

FilterOpt<AccountFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeAccountFilter1

FilterOpt<AssetDefinitionEventFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeAssetDefinitionEventFilter1

FilterOpt<AssetDefinitionFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeAssetDefinitionFilter1

FilterOpt<AssetEventFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeAssetEventFilter1

FilterOpt<AssetFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeAssetFilter1

FilterOpt<DataEntityFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeDataEntityFilter1

FilterOpt<DomainEventFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeDomainEventFilter1

FilterOpt<DomainFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeDomainFilter1

FilterOpt<OriginFilter<AccountEvent>>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeOriginFilter<AccountEvent>1

FilterOpt<OriginFilter<AssetDefinitionEvent>>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeOriginFilter<AssetDefinitionEvent>1

FilterOpt<OriginFilter<AssetEvent>>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeOriginFilter<AssetEvent>1

FilterOpt<OriginFilter<DomainEvent>>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeOriginFilter<DomainEvent>1

FilterOpt<OriginFilter<PeerEvent>>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeOriginFilter<PeerEvent>1

FilterOpt<OriginFilter<RoleEvent>>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeOriginFilter<RoleEvent>1

FilterOpt<OriginFilter<TriggerEvent>>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeOriginFilter<TriggerEvent>1

FilterOpt<PeerEventFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomePeerEventFilter1

FilterOpt<PeerFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomePeerFilter1

FilterOpt<RoleEventFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeRoleEventFilter1

FilterOpt<RoleFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeRoleFilter1

FilterOpt<TriggerEventFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeTriggerEventFilter1

FilterOpt<TriggerFilter>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
BySomeTriggerFilter1

FindAccountById

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AccountId>

FindAccountKeyValueByIdAndKey

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AccountId>
keyEvaluatesTo<Name>

FindAccountsByDomainId

Type: Struct

Declarations:

Field nameField value
domain_idEvaluatesTo<DomainId>

FindAccountsByName

Type: Struct

Declarations:

Field nameField value
nameEvaluatesTo<Name>

FindAccountsWithAsset

Type: Struct

Declarations:

Field nameField value
asset_definition_idEvaluatesTo<AssetDefinitionId>

FindAllAccounts

Type: Zero-Size Type (unit type, null type)

FindAllActiveTriggerIds

Type: Zero-Size Type (unit type, null type)

FindAllAssets

Type: Zero-Size Type (unit type, null type)

FindAllAssetsDefinitions

Type: Zero-Size Type (unit type, null type)

FindAllBlockHeaders

Type: Zero-Size Type (unit type, null type)

FindAllBlocks

Type: Zero-Size Type (unit type, null type)

FindAllDomains

Type: Zero-Size Type (unit type, null type)

FindAllParameters

Type: Zero-Size Type (unit type, null type)

FindAllPeers

Type: Zero-Size Type (unit type, null type)

FindAllRoleIds

Type: Zero-Size Type (unit type, null type)

FindAllRoles

Type: Zero-Size Type (unit type, null type)

FindAllTransactions

Type: Zero-Size Type (unit type, null type)

FindAssetById

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AssetId>

FindAssetDefinitionById

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AssetDefinitionId>

FindAssetDefinitionKeyValueByIdAndKey

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AssetDefinitionId>
keyEvaluatesTo<Name>

FindAssetKeyValueByIdAndKey

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AssetId>
keyEvaluatesTo<Name>

FindAssetQuantityById

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AssetId>

FindAssetsByAccountId

Type: Struct

Declarations:

Field nameField value
account_idEvaluatesTo<AccountId>

FindAssetsByAssetDefinitionId

Type: Struct

Declarations:

Field nameField value
asset_definition_idEvaluatesTo<AssetDefinitionId>

FindAssetsByDomainId

Type: Struct

Declarations:

Field nameField value
domain_idEvaluatesTo<DomainId>

FindAssetsByDomainIdAndAssetDefinitionId

Type: Struct

Declarations:

Field nameField value
domain_idEvaluatesTo<DomainId>
asset_definition_idEvaluatesTo<AssetDefinitionId>

FindAssetsByName

Type: Struct

Declarations:

Field nameField value
nameEvaluatesTo<Name>

FindBlockHeaderByHash

Type: Struct

Declarations:

Field nameField value
hashEvaluatesTo<HashOf<VersionedSignedBlock>>

FindDomainById

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<DomainId>

FindDomainKeyValueByIdAndKey

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<DomainId>
keyEvaluatesTo<Name>

FindError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AssetAssetId0
AssetDefinitionAssetDefinitionId1
AccountAccountId2
DomainDomainId3
MetadataKeyName4
BlockHashOf<VersionedSignedBlock>5
TransactionHashOf<VersionedSignedTransaction>6
PeerPeerId7
TriggerTriggerId8
RoleRoleId9
PermissionTokenName10
ParameterParameterId11
PublicKeyPublicKey12

FindPermissionTokenSchema

Type: Zero-Size Type (unit type, null type)

FindPermissionTokensByAccountId

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AccountId>

FindRoleByRoleId

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<RoleId>

FindRolesByAccountId

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AccountId>

FindTotalAssetQuantityByAssetDefinitionId

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<AssetDefinitionId>

FindTransactionByHash

Type: Struct

Declarations:

Field nameField value
hashEvaluatesTo<HashOf<VersionedSignedTransaction>>

FindTransactionsByAccountId

Type: Struct

Declarations:

Field nameField value
account_idEvaluatesTo<AccountId>

FindTriggerById

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<TriggerId>

FindTriggerKeyValueByIdAndKey

Type: Struct

Declarations:

Field nameField value
idEvaluatesTo<TriggerId>
keyEvaluatesTo<Name>

FindTriggersByDomainId

Type: Struct

Declarations:

Field nameField value
domain_idEvaluatesTo<DomainId>

Fixed

Type: Alias

To: FixedPoint<i64>

FixedPoint<i64>

Type: Fixed Point

Base: i64

Decimal places: 9

ForwardCursor

Type: Struct

Declarations:

Field nameField value
query_idOption<String>
cursorOption<NonZero<u64>>

GenericPredicateBox<ValuePredicate>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AndNonTrivial<GenericPredicateBox<ValuePredicate>>0
OrNonTrivial<GenericPredicateBox<ValuePredicate>>1
NotGenericPredicateBox<ValuePredicate>2
RawValuePredicate3

GrantBox

Type: Struct

Declarations:

Field nameField value
objectEvaluatesTo<Value>
destination_idEvaluatesTo<IdBox>

Greater

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<NumericValue>
rightEvaluatesTo<NumericValue>

Hash

Type: Alias

To: Array<u8, 32>

HashOf<MerkleTree<VersionedSignedTransaction>>

Type: Alias

To: Hash

HashOf<VersionedSignedBlock>

Type: Alias

To: Hash

HashOf<VersionedSignedTransaction>

Type: Alias

To: Hash

HashOf<WasmSmartContract>

Type: Alias

To: Hash

HashValue

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
TransactionHashOf<VersionedSignedTransaction>0
BlockHashOf<VersionedSignedBlock>1

IdBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
DomainIdDomainId0
AccountIdAccountId1
AssetDefinitionIdAssetDefinitionId2
AssetIdAssetId3
PeerIdPeerId4
TriggerIdTriggerId5
RoleIdRoleId6
PermissionTokenIdName7
ParameterIdParameterId8

IdentifiableBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
NewDomainNewDomain0
NewAccountNewAccount1
NewAssetDefinitionNewAssetDefinition2
NewRoleNewRole3
PeerPeer4
DomainDomain5
AccountAccount6
AssetDefinitionAssetDefinition7
AssetAsset8
TriggerTriggerBox9
RoleRole10
ParameterParameter11

If

Type: Struct

Declarations:

Field nameField value
conditionEvaluatesTo<bool>
thenEvaluatesTo<Value>
otherwiseEvaluatesTo<Value>

InstructionBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
RegisterRegisterBox0
UnregisterUnregisterBox1
MintMintBox2
BurnBurnBox3
TransferTransferBox4
IfConditional5
PairPair6
SequenceSequenceBox7
SetKeyValueSetKeyValueBox8
RemoveKeyValueRemoveKeyValueBox9
GrantGrantBox10
RevokeRevokeBox11
ExecuteTriggerExecuteTriggerBox12
SetParameterSetParameterBox13
NewParameterNewParameterBox14
UpgradeUpgradeBox15
LogLogBox16
FailFailBox17

InstructionEvaluationError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ExpressionEvaluationError0
UnsupportedInstructionType1
PermissionParameterString2
TypeTypeError3

InstructionExecutionError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
EvaluateInstructionEvaluationError0
QueryQueryExecutionFail1
ConversionString2
FindFindError3
RepetitionRepetitionError4
MintabilityMintabilityError5
MathMathError6
MetadataMetadataError7
FailString8
InvalidParameterInvalidParameterError9
InvariantViolationString10

InstructionExecutionFail

Type: Struct

Declarations:

Field nameField value
instructionInstructionBox
reasonString

InstructionType

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Register0
Unregister1
Mint2
Burn3
Transfer4
If5
Pair6
Sequence7
SetKeyValue8
RemoveKeyValue9
Grant10
Revoke11
ExecuteTrigger12
SetParameter13
NewParameter14
Upgrade15
Log16
Fail17

Interval<u16>

Type: Struct

Declarations:

Field nameField value
startu16
limitu16

Interval<u8>

Type: Struct

Declarations:

Field nameField value
startu8
limitu8

InvalidParameterError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
WasmString0
NameLength1

IpfsPath

Type: Alias

To: String

Ipv4Addr

Type: Alias

To: Array<u8, 4>

Ipv4Predicate

Type: Alias

To: Array<Interval<u8>, 4>

Ipv6Addr

Type: Alias

To: Array<u16, 8>

Ipv6Predicate

Type: Alias

To: Array<Interval<u16>, 8>

IsAssetDefinitionOwner

Type: Struct

Declarations:

Field nameField value
asset_definition_idEvaluatesTo<AssetDefinitionId>
account_idEvaluatesTo<AccountId>

LengthLimits

Type: Struct

Declarations:

Field nameField value
minu32
maxu32

Less

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<NumericValue>
rightEvaluatesTo<NumericValue>

Level

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
TRACE0
DEBUG1
INFO2
WARN3
ERROR4

Limits

Type: Struct

Declarations:

Field nameField value
max_lenu32
max_entry_byte_sizeu32

LogBox

Type: Struct

Declarations:

Field nameField value
levelEvaluatesTo<Level>
msgEvaluatesTo<String>

MathError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Overflow0
NotEnoughQuantity1
DivideByZero2
NegativeValue3
DomainViolation4
Unknown5
BinaryOpIncompatibleNumericValueTypesBinaryOpIncompatibleNumericValueTypesError6
FixedPointConversionString7

MerkleTree<VersionedSignedTransaction>

Type: Vec

Value: HashOf<VersionedSignedTransaction>

Metadata

Type: Struct

Declarations:

Field nameField value
mapSortedMap<Name, Value>

MetadataChanged<AccountId>

Type: Struct

Declarations:

Field nameField value
target_idAccountId
keyName
valueValue

MetadataChanged<AssetDefinitionId>

Type: Struct

Declarations:

Field nameField value
target_idAssetDefinitionId
keyName
valueValue

MetadataChanged<AssetId>

Type: Struct

Declarations:

Field nameField value
target_idAssetId
keyName
valueValue

MetadataChanged<DomainId>

Type: Struct

Declarations:

Field nameField value
target_idDomainId
keyName
valueValue

MetadataError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
EntryTooBigSizeError0
OverallSizeSizeError1
EmptyPath2
MissingSegmentName3
InvalidSegmentName4

MintBox

Type: Struct

Declarations:

Field nameField value
objectEvaluatesTo<Value>
destination_idEvaluatesTo<IdBox>

MintabilityError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
MintUnmintable0
ForbidMintOnMintable1

Mintable

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Infinitely0
Once1
Not2

Mismatch<AssetDefinitionId>

Type: Struct

Declarations:

Field nameField value
expectedAssetDefinitionId
actualAssetDefinitionId

Mismatch<AssetValueType>

Type: Struct

Declarations:

Field nameField value
expectedAssetValueType
actualAssetValueType

Mismatch<Value>

Type: Struct

Declarations:

Field nameField value
expectedValue
actualValue

Mod

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<NumericValue>
rightEvaluatesTo<NumericValue>

Multiply

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<NumericValue>
rightEvaluatesTo<NumericValue>

Name

Type: Alias

To: String

NewAccount

Type: Struct

Declarations:

Field nameField value
idAccountId
signatoriesSortedVec<PublicKey>
metadataMetadata

NewAssetDefinition

Type: Struct

Declarations:

Field nameField value
idAssetDefinitionId
value_typeAssetValueType
mintableMintable
logoOption<IpfsPath>
metadataMetadata

NewDomain

Type: Struct

Declarations:

Field nameField value
idDomainId
logoOption<IpfsPath>
metadataMetadata

NewParameterBox

Type: Struct

Declarations:

Field nameField value
parameterEvaluatesTo<Parameter>

NewRole

Type: Struct

Declarations:

Field nameField value
innerRole

NonTrivial<GenericPredicateBox<ValuePredicate>>

Type: Alias

To: Vec<GenericPredicateBox<ValuePredicate>>

NonZero<u32>

Type: Alias

To: u32

NonZero<u64>

Type: Alias

To: u64

Not

Type: Struct

Declarations:

Field nameField value
expressionEvaluatesTo<bool>

NotificationEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
TriggerCompletedTriggerCompletedEvent0

NotificationEventFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AcceptAll0
TriggerCompletedTriggerCompletedEventFilter1

NumericValue

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
U32u320
U64u641
U128u1282
FixedFixed3

OptimizedExecutable

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
WasmInternalReprWasmInternalRepr0
InstructionsVec<InstructionBox>1

Option<DomainId>

Type: Option

Some: DomainId

Option<Duration>

Type: Option

Some: Duration

Option<Hash>

Type: Option

Some: Hash

Option<HashOf<MerkleTree<VersionedSignedTransaction>>>

Type: Option

Some: HashOf<MerkleTree<VersionedSignedTransaction>>

Option<HashOf<VersionedSignedBlock>>

Type: Option

Some: HashOf<VersionedSignedBlock>

Option<InstructionBox>

Type: Option

Some: InstructionBox

Option<IpfsPath>

Type: Option

Some: IpfsPath

Option<NonZero<u32>>

Type: Option

Some: NonZero<u32>

Option<NonZero<u64>>

Type: Option

Some: NonZero<u64>

Option<PipelineEntityKind>

Type: Option

Some: PipelineEntityKind

Option<PipelineStatusKind>

Type: Option

Some: PipelineStatusKind

Option<String>

Type: Option

Some: String

Option<TimeInterval>

Type: Option

Some: TimeInterval

Option<TransactionRejectionReason>

Type: Option

Some: TransactionRejectionReason

Option<TriggerCompletedOutcomeType>

Type: Option

Some: TriggerCompletedOutcomeType

Option<TriggerId>

Type: Option

Some: TriggerId

Or

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<bool>
rightEvaluatesTo<bool>

OriginFilter<AccountEvent>

Type: Alias

To: AccountId

OriginFilter<AssetDefinitionEvent>

Type: Alias

To: AssetDefinitionId

OriginFilter<AssetEvent>

Type: Alias

To: AssetId

OriginFilter<DomainEvent>

Type: Alias

To: DomainId

OriginFilter<PeerEvent>

Type: Alias

To: PeerId

OriginFilter<RoleEvent>

Type: Alias

To: RoleId

OriginFilter<TriggerEvent>

Type: Alias

To: TriggerId

Pair

Type: Struct

Declarations:

Field nameField value
left_instructionInstructionBox
right_instructionInstructionBox

Parameter

Type: Struct

Declarations:

Field nameField value
idParameterId
valValue

ParameterId

Type: Struct

Declarations:

Field nameField value
nameName

Peer

Type: Struct

Declarations:

Field nameField value
idPeerId

PeerEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AddedPeerId0
RemovedPeerId1

PeerEventFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ByAdded0
ByRemoved1

PeerFilter

Type: Struct

Declarations:

Field nameField value
origin_filterFilterOpt<OriginFilter<PeerEvent>>
event_filterFilterOpt<PeerEventFilter>

PeerId

Type: Struct

Declarations:

Field nameField value
addressSocketAddr
public_keyPublicKey

PermissionRemoved

Type: Struct

Declarations:

Field nameField value
role_idRoleId
permission_token_idName

PermissionToken

Type: Struct

Declarations:

Field nameField value
definition_idName
payloadStringWithJson

PermissionTokenSchema

Type: Struct

Declarations:

Field nameField value
token_idsVec<Name>
schemaString

PermissionTokenSchemaUpdateEvent

Type: Struct

Declarations:

Field nameField value
old_schemaPermissionTokenSchema
new_schemaPermissionTokenSchema

PipelineEntityKind

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Block0
Transaction1

PipelineEvent

Type: Struct

Declarations:

Field nameField value
entity_kindPipelineEntityKind
statusPipelineStatus
hashHash

PipelineEventFilter

Type: Struct

Declarations:

Field nameField value
entity_kindOption<PipelineEntityKind>
status_kindOption<PipelineStatusKind>
hashOption<Hash>

PipelineRejectionReason

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
BlockBlockRejectionReason0
TransactionTransactionRejectionReason1

PipelineStatus

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Validating0
RejectedPipelineRejectionReason1
Committed2

PipelineStatusKind

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Validating0
Rejected1
Committed2

PublicKey

Type: Struct

Declarations:

Field nameField value
digest_functionAlgorithm
payloadVec<u8>

QueryBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
FindAllAccountsFindAllAccounts0
FindAccountByIdFindAccountById1
FindAccountKeyValueByIdAndKeyFindAccountKeyValueByIdAndKey2
FindAccountsByNameFindAccountsByName3
FindAccountsByDomainIdFindAccountsByDomainId4
FindAccountsWithAssetFindAccountsWithAsset5
FindAllAssetsFindAllAssets6
FindAllAssetsDefinitionsFindAllAssetsDefinitions7
FindAssetByIdFindAssetById8
FindAssetDefinitionByIdFindAssetDefinitionById9
FindAssetsByNameFindAssetsByName10
FindAssetsByAccountIdFindAssetsByAccountId11
FindAssetsByAssetDefinitionIdFindAssetsByAssetDefinitionId12
FindAssetsByDomainIdFindAssetsByDomainId13
FindAssetsByDomainIdAndAssetDefinitionIdFindAssetsByDomainIdAndAssetDefinitionId14
FindAssetQuantityByIdFindAssetQuantityById15
FindTotalAssetQuantityByAssetDefinitionIdFindTotalAssetQuantityByAssetDefinitionId16
IsAssetDefinitionOwnerIsAssetDefinitionOwner17
FindAssetKeyValueByIdAndKeyFindAssetKeyValueByIdAndKey18
FindAssetDefinitionKeyValueByIdAndKeyFindAssetDefinitionKeyValueByIdAndKey19
FindAllDomainsFindAllDomains20
FindDomainByIdFindDomainById21
FindDomainKeyValueByIdAndKeyFindDomainKeyValueByIdAndKey22
FindAllPeersFindAllPeers23
FindAllBlocksFindAllBlocks24
FindAllBlockHeadersFindAllBlockHeaders25
FindBlockHeaderByHashFindBlockHeaderByHash26
FindAllTransactionsFindAllTransactions27
FindTransactionsByAccountIdFindTransactionsByAccountId28
FindTransactionByHashFindTransactionByHash29
FindPermissionTokensByAccountIdFindPermissionTokensByAccountId30
FindPermissionTokenSchemaFindPermissionTokenSchema31
FindAllActiveTriggerIdsFindAllActiveTriggerIds32
FindTriggerByIdFindTriggerById33
FindTriggerKeyValueByIdAndKeyFindTriggerKeyValueByIdAndKey34
FindTriggersByDomainIdFindTriggersByDomainId35
FindAllRolesFindAllRoles36
FindAllRoleIdsFindAllRoleIds37
FindRoleByRoleIdFindRoleByRoleId38
FindRolesByAccountIdFindRolesByAccountId39
FindAllParametersFindAllParameters40

QueryExecutionFail

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
SignatureString0
EvaluateString1
FindFindError2
ConversionString3
Unauthorized4

QueryPayload

Type: Struct

Declarations:

Field nameField value
authorityAccountId
queryQueryBox
filterGenericPredicateBox<ValuePredicate>

RaiseTo

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<NumericValue>
rightEvaluatesTo<NumericValue>

RawGenesisBlock

Type: Struct

Declarations:

Field nameField value
transactionsVec<Vec<InstructionBox>>
validatorValidatorMode

RegisterBox

Type: Struct

Declarations:

Field nameField value
objectEvaluatesTo<RegistrableBox>

RegistrableBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
PeerPeer0
DomainNewDomain1
AccountNewAccount2
AssetDefinitionNewAssetDefinition3
AssetAsset4
TriggerTrigger<TriggeringFilterBox, Executable>5
RoleNewRole6

RemoveKeyValueBox

Type: Struct

Declarations:

Field nameField value
object_idEvaluatesTo<IdBox>
keyEvaluatesTo<Name>

Repeats

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Indefinitely0
Exactlyu321

RepetitionError

Type: Struct

Declarations:

Field nameField value
instruction_typeInstructionType
idIdBox

RevokeBox

Type: Struct

Declarations:

Field nameField value
objectEvaluatesTo<Value>
destination_idEvaluatesTo<IdBox>

Role

Type: Struct

Declarations:

Field nameField value
idRoleId
permissionsSortedVec<PermissionToken>

RoleEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
CreatedRole0
DeletedRoleId1
PermissionRemovedPermissionRemoved2

RoleEventFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ByCreated0
ByDeleted1
ByPermissionRemoved2

RoleFilter

Type: Struct

Declarations:

Field nameField value
origin_filterFilterOpt<OriginFilter<RoleEvent>>
event_filterFilterOpt<RoleEventFilter>

RoleId

Type: Struct

Declarations:

Field nameField value
nameName

Schedule

Type: Struct

Declarations:

Field nameField value
startDuration
periodOption<Duration>

SemiInterval<Fixed>

Type: Struct

Declarations:

Field nameField value
startFixed
limitFixed

SemiInterval<u128>

Type: Struct

Declarations:

Field nameField value
startu128
limitu128

SemiInterval<u32>

Type: Struct

Declarations:

Field nameField value
startu32
limitu32

SemiRange

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
U32SemiInterval<u32>0
U128SemiInterval<u128>1
FixedSemiInterval<Fixed>2

SequenceBox

Type: Struct

Declarations:

Field nameField value
instructionsVec<InstructionBox>

SetKeyValueBox

Type: Struct

Declarations:

Field nameField value
object_idEvaluatesTo<IdBox>
keyEvaluatesTo<Name>
valueEvaluatesTo<Value>

SetParameterBox

Type: Struct

Declarations:

Field nameField value
parameterEvaluatesTo<Parameter>

Signature

Type: Struct

Declarations:

Field nameField value
public_keyPublicKey
payloadVec<u8>

SignatureCheckCondition

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AnyAccountSignatureOrVec<PublicKey>0
AllAccountSignaturesAndVec<PublicKey>1

SignatureOf<BlockPayload>

Type: Alias

To: Signature

SignatureOf<QueryPayload>

Type: Alias

To: Signature

SignatureOf<TransactionPayload>

Type: Alias

To: Signature

SignaturesOf<BlockPayload>

Type: Struct

Declarations:

Field nameField value
signaturesSortedVec<SignatureOf<BlockPayload>>

SignaturesOf<TransactionPayload>

Type: Struct

Declarations:

Field nameField value
signaturesSortedVec<SignatureOf<TransactionPayload>>

SignedBlock

Type: Struct

Declarations:

Field nameField value
signaturesSignaturesOf<BlockPayload>
payloadBlockPayload

SignedQuery

Type: Struct

Declarations:

Field nameField value
signatureSignatureOf<QueryPayload>
payloadQueryPayload

SignedTransaction

Type: Struct

Declarations:

Field nameField value
signaturesSignaturesOf<TransactionPayload>
payloadTransactionPayload

SizeError

Type: Struct

Declarations:

Field nameField value
limitsLimits
actualu64

SocketAddr

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Ipv4SocketAddrV40
Ipv6SocketAddrV61
HostSocketAddrHost2

SocketAddrHost

Type: Struct

Declarations:

Field nameField value
hostString
portu16

SocketAddrV4

Type: Struct

Declarations:

Field nameField value
ipIpv4Addr
portu16

SocketAddrV6

Type: Struct

Declarations:

Field nameField value
ipIpv6Addr
portu16

SortedMap<AccountId, Account>

Type: Map

Key: AccountId

Value: Account

SortedMap<AssetDefinitionId, AssetDefinition>

Type: Map

Key: AssetDefinitionId

Value: AssetDefinition

SortedMap<AssetDefinitionId, NumericValue>

Type: Map

Key: AssetDefinitionId

Value: NumericValue

SortedMap<AssetId, Asset>

Type: Map

Key: AssetId

Value: Asset

SortedMap<Name, EvaluatesTo<Value>>

Type: Map

Key: Name

Value: EvaluatesTo<Value>

SortedMap<Name, Value>

Type: Map

Key: Name

Value: Value

SortedVec<PermissionToken>

Type: Vec

Value: PermissionToken

SortedVec<PublicKey>

Type: Vec

Value: PublicKey

SortedVec<RoleId>

Type: Vec

Value: RoleId

SortedVec<SignatureOf<BlockPayload>>

Type: Vec

Value: SignatureOf<BlockPayload>

SortedVec<SignatureOf<TransactionPayload>>

Type: Vec

Value: SignatureOf<TransactionPayload>

String

Type: Alias

To: String

StringPredicate

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ContainsString0
StartsWithString1
EndsWithString2
IsString3

StringWithJson

Type: Alias

To: String

Subtract

Type: Struct

Declarations:

Field nameField value
leftEvaluatesTo<NumericValue>
rightEvaluatesTo<NumericValue>

TimeEvent

Type: Struct

Declarations:

Field nameField value
prev_intervalOption<TimeInterval>
intervalTimeInterval

TimeEventFilter

Type: Alias

To: ExecutionTime

TimeInterval

Type: Struct

Declarations:

Field nameField value
sinceDuration
lengthDuration

TransactionLimitError

Type: Struct

Declarations:

Field nameField value
reasonString

TransactionLimits

Type: Struct

Declarations:

Field nameField value
max_instruction_numberu64
max_wasm_size_bytesu64

TransactionPayload

Type: Struct

Declarations:

Field nameField value
creation_time_msu64
authorityAccountId
instructionsExecutable
time_to_live_msOption<NonZero<u64>>
nonceOption<NonZero<u32>>
metadataSortedMap<Name, Value>

TransactionQueryOutput

Type: Struct

Declarations:

Field nameField value
transactionTransactionValue
block_hashHashOf<VersionedSignedBlock>

TransactionRejectionReason

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AccountDoesNotExistFindError0
LimitCheckTransactionLimitError1
ValidationValidationFail2
InstructionExecutionInstructionExecutionFail3
WasmExecutionWasmExecutionFail4
Expired5

TransactionValue

Type: Struct

Declarations:

Field nameField value
valueVersionedSignedTransaction
errorOption<TransactionRejectionReason>

TransferBox

Type: Struct

Declarations:

Field nameField value
source_idEvaluatesTo<IdBox>
objectEvaluatesTo<Value>
destination_idEvaluatesTo<IdBox>

Trigger<TriggeringFilterBox, Executable>

Type: Struct

Declarations:

Field nameField value
idTriggerId
actionAction<TriggeringFilterBox, Executable>

Trigger<TriggeringFilterBox, OptimizedExecutable>

Type: Struct

Declarations:

Field nameField value
idTriggerId
actionAction<TriggeringFilterBox, OptimizedExecutable>

TriggerBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
RawTrigger<TriggeringFilterBox, Executable>0
OptimizedTrigger<TriggeringFilterBox, OptimizedExecutable>1

TriggerCompletedEvent

Type: Struct

Declarations:

Field nameField value
trigger_idTriggerId
outcomeTriggerCompletedOutcome

TriggerCompletedEventFilter

Type: Struct

Declarations:

Field nameField value
trigger_idOption<TriggerId>
outcome_typeOption<TriggerCompletedOutcomeType>

TriggerCompletedOutcome

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Success0
FailureString1

TriggerCompletedOutcomeType

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Success0
Failure1

TriggerEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
CreatedTriggerId0
DeletedTriggerId1
ExtendedTriggerNumberOfExecutionsChanged2
ShortenedTriggerNumberOfExecutionsChanged3

TriggerEventFilter

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ByCreated0
ByDeleted1
ByExtended2
ByShortened3

TriggerFilter

Type: Struct

Declarations:

Field nameField value
origin_filterFilterOpt<OriginFilter<TriggerEvent>>
event_filterFilterOpt<TriggerEventFilter>

TriggerId

Type: Struct

Declarations:

Field nameField value
nameName
domain_idOption<DomainId>

TriggerNumberOfExecutionsChanged

Type: Struct

Declarations:

Field nameField value
trigger_idTriggerId
byu32

TriggeringFilterBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
PipelinePipelineEventFilter0
DataFilterOpt<DataEntityFilter>1
TimeTimeEventFilter2
ExecuteTriggerExecuteTriggerEventFilter3

TypeError

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
AssetValueTypeMismatch<AssetValueType>0
ParameterValueTypeMismatch<Value>1
AssetDefinitionIdMismatch<AssetDefinitionId>2

UnregisterBox

Type: Struct

Declarations:

Field nameField value
object_idEvaluatesTo<IdBox>

UpgradableBox

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
ValidatorValidator0

UpgradeBox

Type: Struct

Declarations:

Field nameField value
objectEvaluatesTo<UpgradableBox>

ValidationFail

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
NotPermittedString0
InstructionFailedInstructionExecutionError1
QueryFailedQueryExecutionFail2
TooComplex3
InternalError4

Validator

Type: Struct

Declarations:

Field nameField value
wasmWasmSmartContract

ValidatorEvent

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Upgraded0

ValidatorMode

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
PathString0
InlineValidator1

Value

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
Boolbool0
StringString1
NameName2
VecVec<Value>3
LimitedMetadataMetadata4
MetadataLimitsLimits5
TransactionLimitsTransactionLimits6
LengthLimitsLengthLimits7
IdIdBox8
IdentifiableIdentifiableBox9
PublicKeyPublicKey10
SignatureCheckConditionSignatureCheckCondition11
TransactionQueryOutputTransactionQueryOutput12
PermissionTokenPermissionToken13
PermissionTokenSchemaPermissionTokenSchema14
HashHashValue15
BlockVersionedSignedBlock16
BlockHeaderBlockHeader17
Ipv4AddrIpv4Addr18
Ipv6AddrIpv6Addr19
NumericNumericValue20
ValidatorValidator21
LogLevelLevel22

ValueOfKey

Type: Struct

Declarations:

Field nameField value
keyName
predicateValuePredicate

ValuePredicate

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
IdentifiableStringPredicate0
ContainerContainer1
DisplayStringPredicate2
NumericalSemiRange3
TimeStampSemiInterval<u128>4
Ipv4AddrIpv4Predicate5
Ipv6AddrIpv6Predicate6
Pass7

Vec<Event>

Type: Vec

Value: Event

Vec<GenericPredicateBox<ValuePredicate>>

Type: Vec

Value: GenericPredicateBox<ValuePredicate>

Vec<InstructionBox>

Type: Vec

Value: InstructionBox

Vec<Name>

Type: Vec

Value: Name

Vec<PeerId>

Type: Vec

Value: PeerId

Vec<PublicKey>

Type: Vec

Value: PublicKey

Vec<TransactionValue>

Type: Vec

Value: TransactionValue

Vec<Value>

Type: Vec

Value: Value

Vec<Vec<InstructionBox>>

Type: Vec

Value: Vec<InstructionBox>

Vec<VersionedSignedTransaction>

Type: Vec

Value: VersionedSignedTransaction

Vec<u8>

Type: Vec

Value: u8

VersionedBatchedResponse<Value>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
V1BatchedResponse<Value>1

VersionedBatchedResponse<Vec<VersionedSignedTransaction>>

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
V1BatchedResponse<Vec<VersionedSignedTransaction>>1

VersionedSignedBlock

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
V1SignedBlock1

VersionedSignedQuery

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
V1SignedQuery1

VersionedSignedTransaction

Type: Enum

Variants:

Variant nameVariant valueDiscriminant
V1SignedTransaction1

WasmExecutionFail

Type: Struct

Declarations:

Field nameField value
reasonString

WasmInternalRepr

Type: Struct

Declarations:

Field nameField value
serializedVec<u8>
blob_hashHashOf<WasmSmartContract>

WasmSmartContract

Type: Alias

To: Vec<u8>

Where

Type: Struct

Declarations:

Field nameField value
expressionEvaluatesTo<Value>
valuesSortedMap<Name, EvaluatesTo<Value>>

bool

Type: Alias

To: bool

i64

Type: Int

Kind: FixedWidth

u128

Type: Int

Kind: FixedWidth

u16

Type: Int

Kind: FixedWidth

u32

Type: Int

Kind: FixedWidth

u64

Type: Int

Kind: FixedWidth

u8

Type: Int

Kind: FixedWidth

',1402),o=[l];function i(n,s,c,g,h,y){return e(),a("div",null,o)}const f=t(r,[["render",i]]);export{u as __pageData,f as default}; diff --git a/assets/reference_data-model-schema.md.ea74a419.lean.js b/assets/reference_data-model-schema.md.ea74a419.lean.js new file mode 100644 index 000000000..6ddb7b4e5 --- /dev/null +++ b/assets/reference_data-model-schema.md.ea74a419.lean.js @@ -0,0 +1 @@ +import{_ as t,o as e,c as a,Q as d}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Data Model Schema","description":"","frontmatter":{},"headers":[],"relativePath":"reference/data-model-schema.md","filePath":"reference/data-model-schema.md","lastUpdated":1700563625000}'),r={name:"reference/data-model-schema.md"},l=d("",1402),o=[l];function i(n,s,c,g,h,y){return e(),a("div",null,o)}const f=t(r,[["render",i]]);export{u as __pageData,f as default}; diff --git a/assets/reference_ffi.md.4ec628c3.js b/assets/reference_ffi.md.4ec628c3.js new file mode 100644 index 000000000..f682e94f3 --- /dev/null +++ b/assets/reference_ffi.md.4ec628c3.js @@ -0,0 +1,31 @@ +import{_ as s,o as e,c as a,Q as n}from"./chunks/framework.1293becd.js";const o="/iroha-2-docs/assets/ffi.5bfc24bc.png",f=JSON.parse('{"title":"Foreign Function Interfaces (FFI)","description":"","frontmatter":{},"headers":[],"relativePath":"reference/ffi.md","filePath":"reference/ffi.md","lastUpdated":1700563625000}'),l={name:"reference/ffi.md"},t=n(`

Foreign Function Interfaces (FFI)

To reduce the sizes of smartcontracts, we provide a dynamic library in the execution environment. We shall detail how to link against that library and use the functions at a later date, but for now, let's explore how to include functions and trait implementations into that library.

Why FFI

A function is a rather abstract entity, and while most languages agree on what a function should do, the way in which said functions are represented is very different. Moreover, in some languages (like Rust), the consequences of calling a function, and the things that it is allowed to do are different. Because one can use any language to create a WASM smartcontract, we need to level the playing field. This is where the concept of foreign function interface (FFI) comes in.

The main standard used today is the C application binary interface. It's simple, it's guaranteed to be available even in languages which can't compile to WASM, and it's stable. In principle, you could do everything manually, but Iroha provides you with a crate iroha_ffi which contains all you need to generate FFI-compliant functions out of your existing Rust API.

You can, of course, do this your way. The iroha_ffi crate merely generates the code that you would need to generate anyway. Writing the necessary boilerplate requires quite a bit of diligence and discipline. Every function call over the FFI boundary is unsafe with a potential to cause undefined behaviour. The method by which we managed to solve it, revolves around using robust repr(C) types.

INFO

The only exception are pointers. The null check and the validity cannot be enforced globally, so raw pointers (as always) are only used in exceptional cases. Given that we provide wrappers around almost every instance of an object in the Iroha data model, you shouldn't have to use raw pointers at all.

Example

Here is an example of generating a binding:

rust
#[derive(FfiType)]
+struct DaysSinceEquinox(u32);
+
+#[ffi_export]
+impl DaysSinceEquinox {
+    pub fn update_value(&mut self, a: &u8) {
+        self.0 = *a as u32;
+    }
+}
#[derive(FfiType)]
+struct DaysSinceEquinox(u32);
+
+#[ffi_export]
+impl DaysSinceEquinox {
+    pub fn update_value(&mut self, a: &u8) {
+        self.0 = *a as u32;
+    }
+}

The example above will generate the following binding with DaysSinceEquinox represented as an opaque pointer:

rust
pub extern fn DaysSinceEquinox__update_value(handle: *mut DaysSinceEquinox, a: *const u8) -> FfiReturn {
+    // function implementation
+}
pub extern fn DaysSinceEquinox__update_value(handle: *mut DaysSinceEquinox, a: *const u8) -> FfiReturn {
+    // function implementation
+}

FFI Binding Generation

The iroha_ffi crate is used to generate functions that are callable via FFI. Given Rust structs and methods, they generate the unsafe code that you would need in order to cross the linking boundary.

A Rust type is converted into a robust repr(C) type that can cross the FFI boundary with FfiType::into_ffi. This goes the other way around as well: FFI ReprC type is converted into a Rust type via FfiType::try_from_ffi.

WARNING

Note that the opposite conversion is fallible and can cause undefined behaviour. While we can make the best effort to avoid the most obvious mistakes, you must ensure the program's correctness on your end.

The diagram below uses the creation of a new domain as an example to show the conversion process (more on the name mangling semantics in a separate section).

Untitled

The main traits that enable binding generation are ReprC, FfiType and FfiConvert

TraitDescription
ReprCThis trait represents a robust type that conforms to C ABI. The type can be safely shared across FFI boundaries.
FfiTypeThis trait defines a corresponding ReprC type for a given Rust type. The defined ReprC type is used in place of the Rust type in the API of the generated FFI function.
FfiConvertThis trait defines two methods into_ffi and try_from_ffi that are used to perform the conversion of the Rust type to or from ReprC type.

Note that there is no ownership transfer over FFI except for opaque pointer types. All other types that carry ownership, such as Vec<T>, are cloned.

Name Mangling

Note the use of double underscores in generated names of FFI objects:

  • For the inherent_fn method defined on the StructName struct, the FFI name would be StructName__inherent_fn.

  • For the MethodName method from the TraitName trait in the StructName struct, the FFI name would be StructName__TraitName__MethodName.

  • To set the field_name field in the StructName struct, the FFI function name would be StructName__set_field_name.

  • To get the field_name field in the StructName struct, the FFI function name would be StructName__field_name.

  • To get the mutable field_name field in the StructName struct, the FFI function name would be StrucuName__field_name_mut.

  • For the freestanding module_name::fn_name, the FFI name would be module_name::__fn_name.

  • For the traits that are not generic and allow sharing their implementation in the FFI (see Clone below), the FFI name would be module_name::__clone.

    rust
    impl Clone for Type1 {
    +    fn clone(&self) -> Self;
    +}
    +impl Clone for Type2 {
    +    fn clone(&self) -> Self;
    +}
    impl Clone for Type1 {
    +    fn clone(&self) -> Self;
    +}
    +impl Clone for Type2 {
    +    fn clone(&self) -> Self;
    +}
`,24),p=[t];function c(r,i,d,y,E,u){return e(),a("div",null,p)}const F=s(l,[["render",c]]);export{f as __pageData,F as default}; diff --git a/assets/reference_ffi.md.4ec628c3.lean.js b/assets/reference_ffi.md.4ec628c3.lean.js new file mode 100644 index 000000000..e6e73c3a5 --- /dev/null +++ b/assets/reference_ffi.md.4ec628c3.lean.js @@ -0,0 +1 @@ +import{_ as s,o as e,c as a,Q as n}from"./chunks/framework.1293becd.js";const o="/iroha-2-docs/assets/ffi.5bfc24bc.png",f=JSON.parse('{"title":"Foreign Function Interfaces (FFI)","description":"","frontmatter":{},"headers":[],"relativePath":"reference/ffi.md","filePath":"reference/ffi.md","lastUpdated":1700563625000}'),l={name:"reference/ffi.md"},t=n("",24),p=[t];function c(r,i,d,y,E,u){return e(),a("div",null,p)}const F=s(l,[["render",c]]);export{f as __pageData,F as default}; diff --git a/assets/reference_glossary.md.b751955f.js b/assets/reference_glossary.md.b751955f.js new file mode 100644 index 000000000..5b7a91d59 --- /dev/null +++ b/assets/reference_glossary.md.b751955f.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as i,Q as r}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Glossary","description":"","frontmatter":{},"headers":[],"relativePath":"reference/glossary.md","filePath":"reference/glossary.md","lastUpdated":1700563625000}'),t={name:"reference/glossary.md"},o=r('

Glossary

Here you can find definitions of all Iroha-related entities.

Blockchain ledgers

Blockchain ledgers are digital record-keeping systems that use blockchain technology to keep financial records. These are named after old-fashioned books that were used for financial records such as prices, news, and transaction information.

During medieval times, ledger books were open for public viewing and accuracy verification. This idea is reflected in the blockchain-based systems that can check the stored data for validity.

Peer

A peer in Iroha means an Iroha process instance to which other Iroha processes and client applications can connect. A single machine can host several Iroha peers. Peers are equal regarding their resources and capabilities, with an important exception: only one of the peers runs the genesis block at the bootstrapping stage of the Iroha network.

Other blockchains may refer to the same concept as a node or a validator.

A peer can be a process on its host system. It also can be contained in a Docker container and a Kubernetes pod.

Asset

In the context of blockchains, an asset is the representation of a valuable object on the blockchain.

Additional information on assets is available here.

Fungible assets

Such assets can be easily swapped for other assets of the same type because they are interchangeable.

As an example, all units of the same currency are equal in their value and can be used to purchase goods. Typically, fungible assets are identical in appearance, aside from the wear of banknotes and coins.

Non-fungible assets

Non-fungible assets are unique and valuable due to their specific characteristics and rarity; their value cannot be compared to other assets.

  • A painting's value can vary based on the artist, the time period it was painted, and the public's interest in it.
  • Two houses on the same street may have differing levels of maintenance.
  • Jewellery manufacturers typically offer a range of different designs.

Mintable assets

An asset is mintable if more of the same type can be issued.

Non-mintable assets

If the initial amount of an asset is specified once and doesn't change, it is considered non-mintable.

The Genesis block sets this information for the Iroha configuration.

Byzantine fault-tolerance (BFT)

The property of being able to properly function with a network containing a certain percentage of malicious actors. Iroha is capable of functioning with up to 33% malicious actors in its peer-to-peer network.

Iroha Components

Rust modules containing Iroha functionality.

Sumeragi (Emperor)

The Iroha module responsible for consensus.

Torii (Gate)

Module with the incoming request handling logic for the peer. It is used to receive, accept and route incoming instructions, and HTTP queries, as well as run-time configuration updates.

Kura (Warehouse)

Persistence-related logic. It handles storing the blocks, log rotation, block storage folder rotation, etc.

Kagami(Teacher and Exemplar and/or looking glass)

Generator for commonly used data. It can generate cryptographic key pairs, genesis blocks, documentation, etc.

Merkle tree (hash tree)

A data structure used to validate and verify the state at each block height. Iroha's current implementation is a binary tree. See Wikipedia for more details.

Smart contracts

Smart contracts are blockchain-based programs that run when a specific set of conditions is met. In Iroha smart contracts are implemented using core Iroha special instructions.

Triggers

An event type that allows invoking an Iroha special instruction at specific block commit, time (with some caveats), etc. More on triggers here.

Versioning

Each request is labelled with the API version to which it belongs. It allows a combination of different binary versions of Iroha client/peer software to interoperate, which in turn allows software upgrades in the Iroha network.

Hijiri (peer reputation system)

Iroha's reputation system. It allows prioritising communication with peers that have a good track-record, and reducing the harm that can be caused by malicious peers.

Iroha Modules

Third party extensions to Iroha that provide custom functionality.

Iroha Special Instructions (ISI)

A library of smart contracts provided with Iroha. These can be invoked via either transactions or registered event listeners. More on ISI here.

Utility Iroha Special Instructions

This set of isi contains logical instructions like If, I/O related like Notify and compositions like Sequence. They are mostly used as custom instructions.

Core Iroha Special Instructions

Special instructions provided with every Iroha deployment. These include some domain-specific as well as utility instructions.

Domain-specific Iroha Special Instructions

Instructions related to domain-specific activities: assets, accounts, domains, peer management). These provide the tools necessary to make changes to the World State View in a secure and safe manner.

Custom Iroha Special Instruction

Instructions provided in Iroha Modules, by clients or 3rd parties. These can only be built using the Core Instructions. Forking and modifying the Iroha source code is not recommended, as special instructions not agreed-upon by peers in an Iroha deployment will be treated as faults, thus peers running a modified instance will have their access revoked.

Iroha Query

A request to read the World State View without modifying said view. More on queries here.

View change

A process that takes place in case of a failed attempt at consensus. Usually this entails the election of a new Leader.

World state view (WSV)

In-memory representation of the current blockchain state. This includes all currently loaded blocks, with all of their contents, as well as peers elected for the current epoch.

Leader

In an iroha network, a peer is selected randomly and granted the special privilege of forming the next block. This privilege can be revoked in networks that achieve Byzantine fault-torelance via view change.

',66),s=[o];function n(l,h,c,d,u,p){return a(),i("div",null,s)}const b=e(t,[["render",n]]);export{m as __pageData,b as default}; diff --git a/assets/reference_glossary.md.b751955f.lean.js b/assets/reference_glossary.md.b751955f.lean.js new file mode 100644 index 000000000..fb29e1320 --- /dev/null +++ b/assets/reference_glossary.md.b751955f.lean.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as i,Q as r}from"./chunks/framework.1293becd.js";const m=JSON.parse('{"title":"Glossary","description":"","frontmatter":{},"headers":[],"relativePath":"reference/glossary.md","filePath":"reference/glossary.md","lastUpdated":1700563625000}'),t={name:"reference/glossary.md"},o=r("",66),s=[o];function n(l,h,c,d,u,p){return a(),i("div",null,s)}const b=e(t,[["render",n]]);export{m as __pageData,b as default}; diff --git a/assets/reference_instructions.md.6acb88a4.js b/assets/reference_instructions.md.6acb88a4.js new file mode 100644 index 000000000..4ac4bb328 --- /dev/null +++ b/assets/reference_instructions.md.6acb88a4.js @@ -0,0 +1 @@ +import{_ as a,C as r,o,c as s,k as e,H as n,Q as i}from"./chunks/framework.1293becd.js";const I=JSON.parse('{"title":"Iroha Special Instructions","description":"","frontmatter":{},"headers":[],"relativePath":"reference/instructions.md","filePath":"reference/instructions.md","lastUpdated":1700563625000}'),c={name:"reference/instructions.md"},A=i('

Iroha Special Instructions

The following instructions are supported in Iroha 2:

InstructionDescriptions
Register/UnregisterGive an ID to a new entity on the blockchain.
Mint/BurnMint/burn assets, triggers, or permission tokens.
SetKeyValue/RemoveKeyValueUpdate blockchain object metadata.
NewParameter/SetParameterCreate/set a chain-wide config parameter.
Grant/RevokeGive or remove certain permissions from accounts.
TransferTransfer assets between accounts.
ExecuteTriggerExecute triggers.
If, Pair, SequenceUse to create composite instructions.
',3),u={class:"details custom-block"},l=e("summary",null,"Diagram: Iroha Special Instructions",-1);function d(B,x,h,g,m,f){const t=r("MermaidRenderWrap");return o(),s("div",null,[A,e("details",u,[l,n(t,{id:"mermaid_ed2856e2f3a15f4a487319a2418ef404bba5a0dac5df0f42a043a82e9151599b85f73422c959b6bc3b0a5bbb39538fba27abaddf4e4077d492e2301eec56d75f",text:"classDiagram%0A%0Adirection%20LR%0A%0Aclass%20Instruction%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Register(RegisterBox)%0A%20%20%20%20Unregister(UnregisterBox)%0A%20%20%20%20Mint(MintBox)%0A%20%20%20%20Burn(BurnBox)%0A%20%20%20%20Transfer(TransferBox)%0A%20%20%20%20If(Box~If~)%0A%20%20%20%20Pair(Box~Pair~)%0A%20%20%20%20Sequence(SequenceBox)%0A%20%20%20%20Fail(FailBox)%0A%20%20%20%20SetKeyValue(SetKeyValueBox)%0A%20%20%20%20RemoveKeyValue(RemoveKeyValueBox)%0A%20%20%20%20Grant(GrantBox)%0A%20%20%20%20Revoke(RevokeBox)%0A%20%20%20%20ExecuteTrigger(ExecuteTriggerBox)%0A%7D%0A%0Aclass%20SetKeyValueBox%20%7B%0A%20%20%20%20object_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%20%20%20%20key%3A%20EvaluatesTo~Name~%20%20%20%20%0A%20%20%20%20value%3A%20EvaluatesTo~Value~%20%20%20%20%0A%7D%0A%0Aclass%20RemoveKeyValueBox%20%7B%0A%20%20%20%20object_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%20%20%20%20key%3A%20EvaluatesTo~Name~%20%20%20%20%0A%7D%0A%0A%0Aclass%20RegisterBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~RegistrableBox~%20%20%20%20%0A%7D%0A%0Aclass%20UnregisterBox%20%7B%0A%20%20%20%20object_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0A%0Aclass%20MintBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0Aclass%20BurnBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0Aclass%20TransferBox%20%7B%0A%20%20%20%20source_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0A%0Aclass%20SequenceBox%20%7B%0A%20%20%20%20instructions%3A%20Vec~Instruction~%20%20%20%20%0A%7D%0A%0Aclass%20GrantBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0Aclass%20RevokeBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0Aclass%20ExecuteTriggerBox%20%7B%0A%20%20%20%20trigger_id%3A%20TriggerId%0A%7D%0A%0Aclass%20SetKeyValue~SetKeyValueBox~%0Aclass%20RemoveKeyValue~RemoveKeyValueBox~%0Aclass%20Register~RegisterBox~%0Aclass%20Unregister~UnregisterBox~%0Aclass%20Mint~MintBox~%0Aclass%20Burn~BurnBox~%0Aclass%20Transfer~TransferBox~%0Aclass%20Grant~GrantBox~%0Aclass%20Revoke~RevokeBox~%0A%0A%0AInstruction%20--%3E%20SetKeyValue%0AInstruction%20--%3E%20RemoveKeyValue%0AInstruction%20--%3E%20Register%0AInstruction%20--%3E%20Unregister%0AInstruction%20--%3E%20Mint%0AInstruction%20--%3E%20Burn%0AInstruction%20--%3E%20Transfer%0AInstruction%20--%3E%20Grant%0AInstruction%20--%3E%20Revoke%0AInstruction%20--%3E%20ExecuteTrigger%0AInstruction%20--%3E%20Sequence%0A%0ASetKeyValue%20..%20SetKeyValueBox%0ARemoveKeyValue%20..%20RemoveKeyValueBox%0ARegister%20..%20RegisterBox%0AUnregister%20..%20UnregisterBox%0AMint%20..%20MintBox%0ABurn%20..%20BurnBox%0ATransfer%20..%20TransferBox%0AGrant%20..%20GrantBox%0ARevoke%20..%20RevokeBox%0AExecuteTrigger%20..%20ExecuteTriggerBox%0ASequence%20..%20SequenceBox%0A%0Aclass%20If%20%7B%0A%20%20%20%20condition%3A%20EvaluatesTo~bool~%20%20%20%20%0A%20%20%20%20then%3A%20Instruction%20%20%20%20%0A%20%20%20%20otherwise%3A%20Option~Instruction~%20%20%20%20%0A%7D%0A%0Aclass%20Pair%20%7B%0A%20%20%20%20left_instruction%3A%20Instruction%20%20%20%20%0A%20%20%20%20right_instruction%3A%20Instruction%20%20%20%20%0A%7D%0A%0AInstruction%20--%3E%20If%0AInstruction%20--%3E%20Pair"})])])}const E=a(c,[["render",d]]);export{I as __pageData,E as default}; diff --git a/assets/reference_instructions.md.6acb88a4.lean.js b/assets/reference_instructions.md.6acb88a4.lean.js new file mode 100644 index 000000000..8bbae9f13 --- /dev/null +++ b/assets/reference_instructions.md.6acb88a4.lean.js @@ -0,0 +1 @@ +import{_ as a,C as r,o,c as s,k as e,H as n,Q as i}from"./chunks/framework.1293becd.js";const I=JSON.parse('{"title":"Iroha Special Instructions","description":"","frontmatter":{},"headers":[],"relativePath":"reference/instructions.md","filePath":"reference/instructions.md","lastUpdated":1700563625000}'),c={name:"reference/instructions.md"},A=i("",3),u={class:"details custom-block"},l=e("summary",null,"Diagram: Iroha Special Instructions",-1);function d(B,x,h,g,m,f){const t=r("MermaidRenderWrap");return o(),s("div",null,[A,e("details",u,[l,n(t,{id:"mermaid_ed2856e2f3a15f4a487319a2418ef404bba5a0dac5df0f42a043a82e9151599b85f73422c959b6bc3b0a5bbb39538fba27abaddf4e4077d492e2301eec56d75f",text:"classDiagram%0A%0Adirection%20LR%0A%0Aclass%20Instruction%20%7B%0A%20%20%20%20%3C%3Cenumeration%3E%3E%0A%20%20%20%20Register(RegisterBox)%0A%20%20%20%20Unregister(UnregisterBox)%0A%20%20%20%20Mint(MintBox)%0A%20%20%20%20Burn(BurnBox)%0A%20%20%20%20Transfer(TransferBox)%0A%20%20%20%20If(Box~If~)%0A%20%20%20%20Pair(Box~Pair~)%0A%20%20%20%20Sequence(SequenceBox)%0A%20%20%20%20Fail(FailBox)%0A%20%20%20%20SetKeyValue(SetKeyValueBox)%0A%20%20%20%20RemoveKeyValue(RemoveKeyValueBox)%0A%20%20%20%20Grant(GrantBox)%0A%20%20%20%20Revoke(RevokeBox)%0A%20%20%20%20ExecuteTrigger(ExecuteTriggerBox)%0A%7D%0A%0Aclass%20SetKeyValueBox%20%7B%0A%20%20%20%20object_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%20%20%20%20key%3A%20EvaluatesTo~Name~%20%20%20%20%0A%20%20%20%20value%3A%20EvaluatesTo~Value~%20%20%20%20%0A%7D%0A%0Aclass%20RemoveKeyValueBox%20%7B%0A%20%20%20%20object_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%20%20%20%20key%3A%20EvaluatesTo~Name~%20%20%20%20%0A%7D%0A%0A%0Aclass%20RegisterBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~RegistrableBox~%20%20%20%20%0A%7D%0A%0Aclass%20UnregisterBox%20%7B%0A%20%20%20%20object_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0A%0Aclass%20MintBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0Aclass%20BurnBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0Aclass%20TransferBox%20%7B%0A%20%20%20%20source_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0A%0Aclass%20SequenceBox%20%7B%0A%20%20%20%20instructions%3A%20Vec~Instruction~%20%20%20%20%0A%7D%0A%0Aclass%20GrantBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0Aclass%20RevokeBox%20%7B%0A%20%20%20%20object%3A%20EvaluatesTo~Value~%20%20%20%20%0A%20%20%20%20destination_id%3A%20EvaluatesTo~IdBox~%20%20%20%20%0A%7D%0A%0Aclass%20ExecuteTriggerBox%20%7B%0A%20%20%20%20trigger_id%3A%20TriggerId%0A%7D%0A%0Aclass%20SetKeyValue~SetKeyValueBox~%0Aclass%20RemoveKeyValue~RemoveKeyValueBox~%0Aclass%20Register~RegisterBox~%0Aclass%20Unregister~UnregisterBox~%0Aclass%20Mint~MintBox~%0Aclass%20Burn~BurnBox~%0Aclass%20Transfer~TransferBox~%0Aclass%20Grant~GrantBox~%0Aclass%20Revoke~RevokeBox~%0A%0A%0AInstruction%20--%3E%20SetKeyValue%0AInstruction%20--%3E%20RemoveKeyValue%0AInstruction%20--%3E%20Register%0AInstruction%20--%3E%20Unregister%0AInstruction%20--%3E%20Mint%0AInstruction%20--%3E%20Burn%0AInstruction%20--%3E%20Transfer%0AInstruction%20--%3E%20Grant%0AInstruction%20--%3E%20Revoke%0AInstruction%20--%3E%20ExecuteTrigger%0AInstruction%20--%3E%20Sequence%0A%0ASetKeyValue%20..%20SetKeyValueBox%0ARemoveKeyValue%20..%20RemoveKeyValueBox%0ARegister%20..%20RegisterBox%0AUnregister%20..%20UnregisterBox%0AMint%20..%20MintBox%0ABurn%20..%20BurnBox%0ATransfer%20..%20TransferBox%0AGrant%20..%20GrantBox%0ARevoke%20..%20RevokeBox%0AExecuteTrigger%20..%20ExecuteTriggerBox%0ASequence%20..%20SequenceBox%0A%0Aclass%20If%20%7B%0A%20%20%20%20condition%3A%20EvaluatesTo~bool~%20%20%20%20%0A%20%20%20%20then%3A%20Instruction%20%20%20%20%0A%20%20%20%20otherwise%3A%20Option~Instruction~%20%20%20%20%0A%7D%0A%0Aclass%20Pair%20%7B%0A%20%20%20%20left_instruction%3A%20Instruction%20%20%20%20%0A%20%20%20%20right_instruction%3A%20Instruction%20%20%20%20%0A%7D%0A%0AInstruction%20--%3E%20If%0AInstruction%20--%3E%20Pair"})])])}const E=a(c,[["render",d]]);export{I as __pageData,E as default}; diff --git a/assets/reference_naming.md.ce9dbc7c.js b/assets/reference_naming.md.ce9dbc7c.js new file mode 100644 index 000000000..f8c922c28 --- /dev/null +++ b/assets/reference_naming.md.ce9dbc7c.js @@ -0,0 +1 @@ +import{_ as e,o,c as n,Q as a}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Naming Conventions","description":"","frontmatter":{},"headers":[],"relativePath":"reference/naming.md","filePath":"reference/naming.md","lastUpdated":1700563625000}'),c={name:"reference/naming.md"},t=a('

Naming Conventions

When you are naming accounts, domains, or assets, you have to keep in mind the following conventions used in Iroha 2:

  1. There is a number of reserved characters that are used for specific types of constructs:

    • @ is reserved for account@domain constructs
    • # is reserved for asset#domain constructs
    • $ is reserved for trigger$domain constructs
    • % is reserved for validator%account constructs
  2. The maximum number of characters (including UTF-8 characters) a name can have is limited by two factors: [0, u32::MAX] and the currently allocated stack space.

',3),r=[t];function i(s,d,l,m,u,_){return o(),n("div",null,r)}const p=e(c,[["render",i]]);export{h as __pageData,p as default}; diff --git a/assets/reference_naming.md.ce9dbc7c.lean.js b/assets/reference_naming.md.ce9dbc7c.lean.js new file mode 100644 index 000000000..f589d8424 --- /dev/null +++ b/assets/reference_naming.md.ce9dbc7c.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as n,Q as a}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Naming Conventions","description":"","frontmatter":{},"headers":[],"relativePath":"reference/naming.md","filePath":"reference/naming.md","lastUpdated":1700563625000}'),c={name:"reference/naming.md"},t=a("",3),r=[t];function i(s,d,l,m,u,_){return o(),n("div",null,r)}const p=e(c,[["render",i]]);export{h as __pageData,p as default}; diff --git a/assets/reference_permissions.md.73888c63.js b/assets/reference_permissions.md.73888c63.js new file mode 100644 index 000000000..42003ca1a --- /dev/null +++ b/assets/reference_permissions.md.73888c63.js @@ -0,0 +1,351 @@ +import{_ as s,o as n,c as a,Q as o}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Permissions","description":"","frontmatter":{},"headers":[],"relativePath":"reference/permissions.md","filePath":"reference/permissions.md","lastUpdated":1700563625000}'),l={name:"reference/permissions.md"},p=o(`

Permissions

This section provides details about pre-configured permission tokens in Iroha 2. For more general information on permission tokens and permission groups (roles), refer to the Permissions chapter in Guide.

Permission Tokens

The following permission tokens are pre-configured in Iroha 2:

Permission TokenCategoryOperation
CanSetKeyValueInUserMetadataAccountSet key value
CanRemoveKeyValueInUserMetadataAccountRemove key value
CanBurnUserAssetsAssetBurn
CanSetKeyValueInUserAssetsAssetSet key value
CanRemoveKeyValueInUserAssetsAssetRemove key value
CanTransferUserAssetsAssetTransfer
CanTransferOnlyFixedNumberOfTimesPerPeriodAssetTransfer
CanMintUserAssetDefinitionsAsset DefinitionMint
CanBurnAssetWithDefinitionAsset DefinitionBurn
CanUnregisterAssetWithDefinitionAsset DefinitionUnregister
CanSetKeyValueInAssetDefinitionAsset DefinitionSet key value
CanRemoveKeyValueInAssetDefinitionAsset DefinitionRemove key value
CanRegisterDomainsDomainRegister

INFO

The way permission work in Iroha 2 is subject to change. Note that there won't be pre-configured permissions in the future.

CanMintUserAssetDefinitions

With CanMintUserAssetDefinitions, a user can register and mint assets with the corresponding asset definition.

rust
let mut genesis = RawGenesisBlock::new(
+    "alice".parse(),
+    "wonderland".parse(),
+    get_key_pair().public_key().clone(),
+);
+let rose_definition_id =
+    AssetDefinitionId::from_str("rose#wonderland")?;
+let alice_id =
+    AccountId::from_str("alice@wonderland")?;
+
+// Create a new \`CanMintUserAssetDefinitions\` permission token
+// to mint rose assets (\`rose_definition_id\`)
+let mint_rose_permission: PermissionToken =
+    CanMintUserAssetDefinitions::new(rose_definition_id).into();
+
+// Grant Alice permission to mint rose assets
+genesis.transactions[0]
+    .isi
+    .push(GrantBox::new(mint_rose_permission, alice_id).into());
let mut genesis = RawGenesisBlock::new(
+    "alice".parse(),
+    "wonderland".parse(),
+    get_key_pair().public_key().clone(),
+);
+let rose_definition_id =
+    AssetDefinitionId::from_str("rose#wonderland")?;
+let alice_id =
+    AccountId::from_str("alice@wonderland")?;
+
+// Create a new \`CanMintUserAssetDefinitions\` permission token
+// to mint rose assets (\`rose_definition_id\`)
+let mint_rose_permission: PermissionToken =
+    CanMintUserAssetDefinitions::new(rose_definition_id).into();
+
+// Grant Alice permission to mint rose assets
+genesis.transactions[0]
+    .isi
+    .push(GrantBox::new(mint_rose_permission, alice_id).into());

CanBurnAssetWithDefinition

With CanBurnAssetWithDefinition permission token, a user can burn and unregister assets with the corresponding asset definition.

rust
let mut genesis = RawGenesisBlock::new(
+    "alice".parse(),
+    "wonderland".parse(),
+    get_key_pair().public_key().clone(),
+);
+let rose_definition_id =
+    AssetDefinitionId::from_str("rose#wonderland")?;
+let alice_id =
+    AccountId::from_str("alice@wonderland")?;
+
+// Create a new \`CanBurnAssetWithDefinition\` permission token
+// to burn rose assets (\`rose_definition_id\`)
+let burn_rose_permission: PermissionToken =
+    CanBurnAssetWithDefinition::new(rose_definition_id).into();
+
+// Grant Alice permission to burn rose assets
+genesis.transactions[0]
+    .isi
+    .push(GrantBox::new(burn_rose_permission, alice_id).into());
let mut genesis = RawGenesisBlock::new(
+    "alice".parse(),
+    "wonderland".parse(),
+    get_key_pair().public_key().clone(),
+);
+let rose_definition_id =
+    AssetDefinitionId::from_str("rose#wonderland")?;
+let alice_id =
+    AccountId::from_str("alice@wonderland")?;
+
+// Create a new \`CanBurnAssetWithDefinition\` permission token
+// to burn rose assets (\`rose_definition_id\`)
+let burn_rose_permission: PermissionToken =
+    CanBurnAssetWithDefinition::new(rose_definition_id).into();
+
+// Grant Alice permission to burn rose assets
+genesis.transactions[0]
+    .isi
+    .push(GrantBox::new(burn_rose_permission, alice_id).into());

CanBurnUserAssets

With CanBurnUserAssets permission token, a user can burn the specified asset.

rust
let alice_id = AccountId::from_str("alice@test")?;
+let bob_id = AccountId::from_str("bob@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanBurnUserAssets\` permission token
+// that allows burning \`alice_xor_id\` asset
+let permission_token_to_alice: PermissionToken =
+    burn::CanBurnUserAssets::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission to burn \`alice_xor_id\` asset
+let grant = Instruction::Grant(GrantBox::new(
+    permission_token_to_alice,
+    IdBox::AccountId(bob_id),
+));
let alice_id = AccountId::from_str("alice@test")?;
+let bob_id = AccountId::from_str("bob@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanBurnUserAssets\` permission token
+// that allows burning \`alice_xor_id\` asset
+let permission_token_to_alice: PermissionToken =
+    burn::CanBurnUserAssets::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission to burn \`alice_xor_id\` asset
+let grant = Instruction::Grant(GrantBox::new(
+    permission_token_to_alice,
+    IdBox::AccountId(bob_id),
+));

CanUnregisterAssetWithDefinition

With CanUnregisterAssetWithDefinition permission token, a user can unregister assets with the corresponding asset definition.

rust
let alice_id = AccountId::from_str("alice@test")?;
+let bob_id = AccountId::from_str("bob@test")?;
+let xor_id = AssetDefinitionId::from_str("xor#test")?;
+
+// Create a new \`CanUnregisterAssetWithDefinition\` permission token
+// that allows unregistering \`xor_id\` asset
+let permission_token_to_alice: PermissionToken =
+    unregister::CanUnregisterAssetWithDefinition::new(xor_id).into();
+
+// Create an instruction that grants Bob permission to unregister \`xor_id\` asset
+let grant = Instruction::Grant(GrantBox {
+    permission_token_to_alice.into(),
+    IdBox::AccountId(bob_id).into(),
+});
let alice_id = AccountId::from_str("alice@test")?;
+let bob_id = AccountId::from_str("bob@test")?;
+let xor_id = AssetDefinitionId::from_str("xor#test")?;
+
+// Create a new \`CanUnregisterAssetWithDefinition\` permission token
+// that allows unregistering \`xor_id\` asset
+let permission_token_to_alice: PermissionToken =
+    unregister::CanUnregisterAssetWithDefinition::new(xor_id).into();
+
+// Create an instruction that grants Bob permission to unregister \`xor_id\` asset
+let grant = Instruction::Grant(GrantBox {
+    permission_token_to_alice.into(),
+    IdBox::AccountId(bob_id).into(),
+});

CanTransferUserAssets

With CanTransferUserAssets permission token, a user can transfer the specified asset.

rust
let alice_id = AccountId::from_str("alice@test")?;
+let bob_id = AccountId::from_str("bob@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanTransferUserAssets\` permission token
+// that allows to transfer \`alice_xor_id\` asset
+let permission_token_to_alice: PermissionToken =
+    transfer::CanTransferUserAssets::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission to transfer \`alice_xor_id\` asset
+let grant = Instruction::Grant(GrantBox::new(
+    permission_token_to_alice,
+    IdBox::AccountId(bob_id),
+));
let alice_id = AccountId::from_str("alice@test")?;
+let bob_id = AccountId::from_str("bob@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanTransferUserAssets\` permission token
+// that allows to transfer \`alice_xor_id\` asset
+let permission_token_to_alice: PermissionToken =
+    transfer::CanTransferUserAssets::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission to transfer \`alice_xor_id\` asset
+let grant = Instruction::Grant(GrantBox::new(
+    permission_token_to_alice,
+    IdBox::AccountId(bob_id),
+));

CanTransferOnlyFixedNumberOfTimesPerPeriod

With CanTransferOnlyFixedNumberOfTimesPerPeriod permission token, a user can transfer assets only a fixed number of times per some time period.

rust
const PERIOD_MS: u64 = 5000;
+const COUNT: u32 = 2;
+
+// Create a new \`CanTransferOnlyFixedNumberOfTimesPerPeriod\` permission token
+// that limits the number of transfer to 2 per 5000 ms.
+let permission_token =
+    transfer::CanTransferOnlyFixedNumberOfTimesPerPeriod::new(PERIOD_MS.into(), COUNT);
const PERIOD_MS: u64 = 5000;
+const COUNT: u32 = 2;
+
+// Create a new \`CanTransferOnlyFixedNumberOfTimesPerPeriod\` permission token
+// that limits the number of transfer to 2 per 5000 ms.
+let permission_token =
+    transfer::CanTransferOnlyFixedNumberOfTimesPerPeriod::new(PERIOD_MS.into(), COUNT);

CanSetKeyValueInUserAssets

With CanSetKeyValueInUserAssets permission token, a user can set key value in the specified asset.

rust
let bob_id = AccountId::from_str("bob@test")?;
+let alice_id = AccountId::from_str("alice@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanSetKeyValueInUserAssets\` permission token
+// that allows to set key value in \`alice_xor_id\` asset
+let permission_to_set_key_value_in_xor: PermissionToken =
+    key_value::CanSetKeyValueInUserAssets::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission
+// to set key value in \`alice_xor_id\` asset
+let grant = Instruction::Grant(GrantBox::new(
+    permission_to_set_key_value_in_xor,
+    IdBox::AccountId(bob_id),
+));
let bob_id = AccountId::from_str("bob@test")?;
+let alice_id = AccountId::from_str("alice@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanSetKeyValueInUserAssets\` permission token
+// that allows to set key value in \`alice_xor_id\` asset
+let permission_to_set_key_value_in_xor: PermissionToken =
+    key_value::CanSetKeyValueInUserAssets::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission
+// to set key value in \`alice_xor_id\` asset
+let grant = Instruction::Grant(GrantBox::new(
+    permission_to_set_key_value_in_xor,
+    IdBox::AccountId(bob_id),
+));

CanRemoveKeyValueInUserAssets

With CanRemoveKeyValueInUserAssets permission token, a user can remove key value in the specified asset.

rust
let bob_id = AccountId::from_str("bob@test")?;
+let alice_id = AccountId::from_str("alice@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanRemoveKeyValueInUserAssets\` permission token
+// that allows to remove key value from \`alice_xor_id\` asset
+let permission_to_set_key_value_in_xor: PermissionToken =
+    key_value::CanRemoveKeyValueInUserAssets::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission
+// to remove key value from \`alice_xor_id\` asset
+let grant = Instruction::Grant(GrantBox::new(
+    permission_to_set_key_value_in_xor,
+    IdBox::AccountId(bob_id),
let bob_id = AccountId::from_str("bob@test")?;
+let alice_id = AccountId::from_str("alice@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanRemoveKeyValueInUserAssets\` permission token
+// that allows to remove key value from \`alice_xor_id\` asset
+let permission_to_set_key_value_in_xor: PermissionToken =
+    key_value::CanRemoveKeyValueInUserAssets::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission
+// to remove key value from \`alice_xor_id\` asset
+let grant = Instruction::Grant(GrantBox::new(
+    permission_to_set_key_value_in_xor,
+    IdBox::AccountId(bob_id),

CanSetKeyValueInUserMetadata

With CanSetKeyValueInUserMetadata permission token, a user can set a key value pair in the metadata for the specified account.

rust
let mouse_id = AccountId::from_str("mouse@wonderland")?;
+
+// Create a new \`CanSetKeyValueInUserMetadata\` token that, when granted to another account,
+// allows it to set key value to the metadata in Mouse's account
+let permission_to_set_key_value_in_mouse_metadata: PermissionToken =
+    key_value::CanSetKeyValueInUserMetadata::new(mouse_id).into();
let mouse_id = AccountId::from_str("mouse@wonderland")?;
+
+// Create a new \`CanSetKeyValueInUserMetadata\` token that, when granted to another account,
+// allows it to set key value to the metadata in Mouse's account
+let permission_to_set_key_value_in_mouse_metadata: PermissionToken =
+    key_value::CanSetKeyValueInUserMetadata::new(mouse_id).into();

CanRemoveKeyValueInUserMetadata

With CanRemoveKeyValueInUserMetadata permission token, a user can remove a key value pair in the metadata for the specified account.

rust
let mouse_id = AccountId::from_str("mouse@wonderland")?;
+
+// Create a new \`CanRemoveKeyValueInUserMetadata\` token that, when granted to another account,
+// allows it to remove key value from the metadata in Mouse's account
+let permission_to_remove_key_value_in_mouse_metadata: PermissionToken =
+    key_value::CanRemoveKeyValueInUserMetadata::new(mouse_id).into();
let mouse_id = AccountId::from_str("mouse@wonderland")?;
+
+// Create a new \`CanRemoveKeyValueInUserMetadata\` token that, when granted to another account,
+// allows it to remove key value from the metadata in Mouse's account
+let permission_to_remove_key_value_in_mouse_metadata: PermissionToken =
+    key_value::CanRemoveKeyValueInUserMetadata::new(mouse_id).into();

CanSetKeyValueInAssetDefinition

With CanSetKeyValueInAssetDefinition permission token, a user can set key value in the corresponding asset definition.

rust
let bob_id = AccountId::from_str("bob@test")?;
+let alice_id = AccountId::from_str("alice@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanSetKeyValueInAssetDefinition\` permission token
+// that allows to set key value in \`alice_xor_id\` asset definition
+let permission_to_set_key_value_in_xor: PermissionToken =
+    key_value::CanSetKeyValueInAssetDefinition::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission
+// to set key value in \`alice_xor_id\` asset definition
+let grant = Instruction::Grant(GrantBox::new(
+    permission_to_set_key_value_in_xor,
+    IdBox::AccountId(bob_id),
+));
let bob_id = AccountId::from_str("bob@test")?;
+let alice_id = AccountId::from_str("alice@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanSetKeyValueInAssetDefinition\` permission token
+// that allows to set key value in \`alice_xor_id\` asset definition
+let permission_to_set_key_value_in_xor: PermissionToken =
+    key_value::CanSetKeyValueInAssetDefinition::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission
+// to set key value in \`alice_xor_id\` asset definition
+let grant = Instruction::Grant(GrantBox::new(
+    permission_to_set_key_value_in_xor,
+    IdBox::AccountId(bob_id),
+));

CanRemoveKeyValueInAssetDefinition

With CanRemoveKeyValueInAssetDefinition permission token, a user can remove key value in the corresponding asset definition.

rust
let bob_id = AccountId::from_str("bob@test")?;
+let alice_id = AccountId::from_str("alice@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanRemoveKeyValueInAssetDefinition\` permission token
+// that allows to remove key value from \`alice_xor_id\` asset definition
+let permission_to_remove_key_value_from_xor: PermissionToken =
+    key_value::CanRemoveKeyValueInAssetDefinition::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission
+// to remove key value from \`alice_xor_id\` asset definition
+let grant = Instruction::Grant(GrantBox::new(
+    permission_to_remove_key_value_from_xor,
+    IdBox::AccountId(bob_id),
+));
let bob_id = AccountId::from_str("bob@test")?;
+let alice_id = AccountId::from_str("alice@test")?;
+let alice_xor_id = AssetId::new(
+    AssetDefinitionId::from_str("xor#test")?,
+    AccountId::from_str("alice@test")?,
+);
+
+// Create a new \`CanRemoveKeyValueInAssetDefinition\` permission token
+// that allows to remove key value from \`alice_xor_id\` asset definition
+let permission_to_remove_key_value_from_xor: PermissionToken =
+    key_value::CanRemoveKeyValueInAssetDefinition::new(alice_xor_id).into();
+
+// Create an instruction that grants Bob permission
+// to remove key value from \`alice_xor_id\` asset definition
+let grant = Instruction::Grant(GrantBox::new(
+    permission_to_remove_key_value_from_xor,
+    IdBox::AccountId(bob_id),
+));

CanRegisterDomains

With CanRegisterDomains permission token, a user can register domains.

rust
let alice_id = AccountId::from_str("alice@test0")?;
+let mut alice = Account::new(alice_id, []).build();
+
+// Create a new \`CanRegisterDomains\` permission token
+// that allows registering new domains
+let permission_token_to_register_domains: PermissionToken =
+    register::CanRegisterDomains::new().into();
+
+// Create an instruction that grants Alice permission to register new domains
+let grant = Instruction::Grant(GrantBox::new(
+    permission_token_to_register_domains,
+    IdBox::AccountId(alice_id),
let alice_id = AccountId::from_str("alice@test0")?;
+let mut alice = Account::new(alice_id, []).build();
+
+// Create a new \`CanRegisterDomains\` permission token
+// that allows registering new domains
+let permission_token_to_register_domains: PermissionToken =
+    register::CanRegisterDomains::new().into();
+
+// Create an instruction that grants Alice permission to register new domains
+let grant = Instruction::Grant(GrantBox::new(
+    permission_token_to_register_domains,
+    IdBox::AccountId(alice_id),
`,45),e=[p];function t(r,c,y,i,E,F){return n(),a("div",null,e)}const A=s(l,[["render",t]]);export{u as __pageData,A as default}; diff --git a/assets/reference_permissions.md.73888c63.lean.js b/assets/reference_permissions.md.73888c63.lean.js new file mode 100644 index 000000000..457a10eef --- /dev/null +++ b/assets/reference_permissions.md.73888c63.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as o}from"./chunks/framework.1293becd.js";const u=JSON.parse('{"title":"Permissions","description":"","frontmatter":{},"headers":[],"relativePath":"reference/permissions.md","filePath":"reference/permissions.md","lastUpdated":1700563625000}'),l={name:"reference/permissions.md"},p=o("",45),e=[p];function t(r,c,y,i,E,F){return n(),a("div",null,e)}const A=s(l,[["render",t]]);export{u as __pageData,A as default}; diff --git a/assets/reference_queries.md.e4f73f68.js b/assets/reference_queries.md.e4f73f68.js new file mode 100644 index 000000000..d580d4316 --- /dev/null +++ b/assets/reference_queries.md.e4f73f68.js @@ -0,0 +1,29 @@ +import{_ as o,C as i,o as l,c as r,k as e,H as n,Q as a,a as t}from"./chunks/framework.1293becd.js";const Z=JSON.parse('{"title":"Queries","description":"","frontmatter":{},"headers":[],"relativePath":"reference/queries.md","filePath":"reference/queries.md","lastUpdated":1700563625000}'),d={name:"reference/queries.md"},c=a('

Queries

In the following section we mirror the module structure of the queries and present to you what they do. You can learn

INFO

The results of queries can be sorted, paginated and filtered peer-side all at once.

Conventions

Expand to learn about the conventions used in the descriptions below
  1. In the Details section of each query, we use gets, returns, searches with the following precise meanings:

    NotationMeaning
    getsThe query already has the data readily available and the data is trivial. Use these queries at will.
    returns or findsThe query has the data readily available, just as with gets, but the data is not trivial. You can still use these queries, but be mindful of the performance impact.
    searchesFor this query, the data must be actively collected and neither the return type nor the collection process is cheap. Use with great care.
  2. The queries are provided with just one data type as input, and parameterised by the type of the output.

  3. For the FindZByXAndY queries, their descriptions have a Parameters and a Returns section. The parameters can either be single or multiple types, while the output is almost always either one type, or a Vec<Type> kind of construction:

    NotationMeaning
    Parameters: (X, Y)In Rust source code, you need to construct the query as let query = FindZByXAndY::new(x: X, y: Y);, where x and y are variables of type X and Y respectively. In the reference below we provide you with information about each type.
    Returns: Vec<Z>The return value is a collection of more than one element of type Z. Depending on the SDK implementation this can be a type native to the language (e.g. JavaScript) or a thin wrapper around the Rust Vec structure.

Role

An optional feature. By default, it is present on all Iroha 2 deployments when they're compiled in the private blockchain configuration.

You can learn more about roles in a dedicated section.

FindAllRoles

',9),p=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),u=e("p",null,[e("strong",null,"Details"),t(": Returns all roles registered as "),e("em",null,"global"),t(" (as opposed to "),e("em",null,"domain-scoped"),t(") in the blockchain.")],-1),h=a('

FindAllRoleIds

  • Returns: Vec<Roles>

  • Details: Returns IDs of all the roles registered as global (as opposed to domain-scoped) in the blockchain.

    Note that it does not return its values, which contain permission tokens, only IDs.

FindRoleByRoleId

  • Parameters: RoleId

  • Returns: Vec<Roles>

  • Details: Returns the role that has the provided role ID.

    For example, given the name of the role admin, it will return all of the admin-level permission tokens.

FindRolesByAccountId

  • Parameters: AccountId

  • Returns: Vec<RoleId>

  • Details: Returns all of the role IDs that are attached to the given account.

    Note that unlike FindAllRoles, it does not return the roles themselves.

Permission

A semi-optional feature. You have permissions in both public and private blockchains but the use cases are different:

  • In a public blockchain, most accounts have the same common-sense permissions.
  • In a private blockchain, most accounts are assumed not to be able to do anything outside of their own account or domain unless explicitly granted said permission.

We talk about permissions in more detail in a dedicated chapter.

FindAllPermissionTokenDefinitions

  • Returns: Vec<PermissionTokenDefinition>

  • Details: Finds all registered permission token definitions.

FindPermissionTokensByAccountId

  • Parameters: AccountId

  • Returns: Vec<PermissionToken>

  • Details: Returns all of the permission tokens granted to the specified account.

Account

Most queries in Iroha pertain to accounts. At the moment this is the most diverse set of queries.

FindAllAccounts

',17),g=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),m=e("p",null,[e("strong",null,"Details"),t(": Finds all accounts registered globally in the blockchain.")],-1),y=a('

FindAccountById

  • Parameters: AccountId

  • Returns: Account

  • Details: Returns the full account information corresponding to the given AccountId.

FindAccountKeyValueByIdAndKey

  • Parameters: (AccountId, Name)

  • Returns: Value

  • Details: Returns the value keyed by the provided Name for the given account.

    This is done by querying the metadata attached to the given account.

FindAccountsByName

  • Parameters: Name

  • Returns: Vec<Account>

  • Details: Returns all of the accounts that have the given Name.

    This is particularly useful if you remember the name of the account, but do not, for example, recall the domain name in which it was registered.

FindAccountsByDomainId

',7),f=e("li",null,[e("p",null,[e("strong",null,"Parameters"),t(": "),e("code",null,"DomainId")])],-1),b=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),_=e("p",null,[e("strong",null,"Details"),t(": Returns all accounts that belong to a specific domain.")],-1),A=e("p",null,[t("Note that this returns the full accounts and not the "),e("code",null,"AccountId"),t(" collection.")],-1),k=a('

FindAccountsWithAsset

  • Parameters: AccountId

  • Returns: Vec<Account>

  • Details: Returns all accounts that have the given asset.

Asset

Assets include simple numbers, but also a special type of key-to-value map that is used as a secure data storage for privileged information.

FindAllAssets

',5),q=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),F=e("p",null,[e("strong",null,"Details"),t(": Returns all known assets by value.")],-1),T=e("div",{class:"info custom-block"},[e("p",{class:"custom-block-title"},"INFO"),e("p",null,[t("You should note that this is not the same as "),e("code",null,"AssetDefinition"),t(". If you have one asset called e.g. "),e("code",null,"tea#wonderland"),t(" that belongs to every account on the blockchain, you will receive the aggregated value across all accounts, but not the information such as the type of the asset.")])],-1),I=e("h3",{id:"findallassetdefinitions",tabindex:"-1"},[e("code",null,"FindAllAssetDefinitions"),t(),e("a",{class:"header-anchor",href:"#findallassetdefinitions","aria-label":'Permalink to "`FindAllAssetDefinitions`"'},"​")],-1),E=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),P=e("p",null,[e("strong",null,"Details"),t(": Returns all known asset definitions by value.")],-1),v=e("div",{class:"tip custom-block"},[e("p",{class:"custom-block-title"},"TIP"),e("p",null,"To reduce the load on the network, we store the definition of an asset separate from its instances. So if you want to know if an asset is mintable or what type is stored in it, you need to query the asset definition, rather than the asset itself.")],-1),D=a('

FindAssetById

  • Parameters: AssetId

  • Returns: Asset

  • Details: Returns the aggregated data about the asset usage across the network.

FindAssetsByName

  • Parameters: Name

  • Returns: Vec<Asset>

  • Details: Searches the network for all assets that match the given name.

FindAssetsByAccountId

  • Parameters: AccountId

  • Returns: Vec<Asset>

  • Details: Returns all of the assets that belong to a given account.

FindAssetsByAssetDefinitionId

  • Parameters: AssetDefinitionId

  • Returns: Vec<Asset>

  • Details: Searches for all of the assets that have the given definition ID.

',8),R=a('

FindAssetsByDomainId

  • Parameters: DomainId

  • Returns: Vec<Asset>

  • Details: Returns all assets that are registered in the given domain.

',2),B=a('

FindAssetsByDomainIdAndAssetDefinitionId

  • Parameters: (DomainId, AssetDefinitionId)

  • Returns: Vec<Asset>

  • Details: Searches the domain for assets that have the given definition ID.

FindAssetQuantityById

  • Parameters: AssetId

  • Returns: NumericValue

  • Details: Returns the asset quantity.

    Note that this query assumes that the asset given by the identifier is of type AssetValue::Quantity.

    WARNING

    This query can fail.

FindAssetKeyValueByIdAndKey

  • Parameters: (AssetId, Name)

  • Returns: Value

  • Details: Gets the value keyed by the given name in the metadata of the asset corresponding to the given identifier.

FindAssetDefinitionKeyValueByIdAndKey

  • Parameters: (AssetDefinitionId, Name)

  • Returns: Value

  • Details: Gets the value keyed by the given name in the metadata of the asset definition corresponding to the given identifier.

FindTotalAssetQuantityByAssetDefinitionId

  • Parameters: AssetDefinitionId

  • Returns: NumericValue

  • Details: Finds the total asset quantity for the given asset definition. For the Store asset value, finds the sum of asset quantities through all accounts that hold the specified asset.

Block

FindAllBlocks

  • Returns: Vec<VersionedCommittedBlock>
  • Details: Returns all blocks in the blockchain.

FindAllBlockHeaders

  • Returns: Vec<BlockHeader>
  • Details: Returns all block headers for blocks in the blockchain.

FindBlockHeaderByHash

  • Parameters: Hash
  • Returns: BlockHeader
  • Details: Gets the block header that matches the hash that was provided.

Domain

The domain is the basic unit of organisation in an Iroha blockchain. Accounts and assets must be registered inside a domain, triggers are usually scoped by domain, and most queries have the domain as a possible input.

FindAllDomains

  • Returns: Vec<Domain>

  • Details: Returns all of the known registered domains.

    WARNING

    This query returns the full contents of the world state view as of execution. This query should be used sparingly and for debugging purposes only.

FindDomainById

',22),V=e("li",null,[e("p",null,[e("strong",null,"Parameters"),t(": "),e("code",null,"DomainId")])],-1),x=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Domain")])],-1),w=e("p",null,[e("strong",null,"Details"),t(": Gets the domain corresponding to the given identifier.")],-1),C=a(`

FindDomainKeyValueByIdAndKey

  • Parameters: (DomainId, Name)

  • Returns: Value

  • Details: Returns the value keyed by the given name in the domain corresponding to the given identifier.

Peer

A peer is the basic unit of storage and validation. In common parlance we may conflate the node and the peer binary running on the node, but in this case we specifically mean the peer binary as a server with its specific configuration.

FindAllPeers

  • Returns: Vec<Peer>

  • Details: Returns all known peers identified by their key and accompanied by the address of the API endpoint of each.

FindAllParameters

  • Returns: Vec<Parameter>

    rust
    pub enum Parameter {
    +    /// Maximum amount of Faulty Peers in the system.
    +    MaximumFaultyPeersAmount(u32),
    +    /// Maximum time for a leader to create a block.
    +    BlockTime(u128),
    +    /// Maximum time for a proxy tail to send commit message.
    +    CommitTime(u128),
    +    /// Time to wait for a transaction Receipt.
    +    TransactionReceiptTime(u128),
    +}
    pub enum Parameter {
    +    /// Maximum amount of Faulty Peers in the system.
    +    MaximumFaultyPeersAmount(u32),
    +    /// Maximum time for a leader to create a block.
    +    BlockTime(u128),
    +    /// Maximum time for a proxy tail to send commit message.
    +    CommitTime(u128),
    +    /// Time to wait for a transaction Receipt.
    +    TransactionReceiptTime(u128),
    +}
  • Details: Returns the parameters used by all peers in the network.

    This is useful for debugging if any of the peers are incorrectly configured and causing view changes.

Transaction

It is often necessary to query the state of specific transactions, especially for use in blockchain explorers and for user-facing applications.

FindTransactionsByAccountId

`,11),N=a(`
  • Parameters: AccountId

  • Returns: Vec<TransactionValue>

    rust
    pub enum TransactionValue {
    +    /// Committed transaction
    +    Transaction(Box<VersionedSignedTransaction>),
    +    /// Rejected transaction with reason of rejection
    +    RejectedTransaction(Box<VersionedRejectedTransaction>),
    +}
    pub enum TransactionValue {
    +    /// Committed transaction
    +    Transaction(Box<VersionedSignedTransaction>),
    +    /// Rejected transaction with reason of rejection
    +    RejectedTransaction(Box<VersionedRejectedTransaction>),
    +}
  • `,2),S=e("p",null,[e("strong",null,"Details"),t(": Returns the full set of transactions that an account has submitted throughout the existence of the blockchain.")],-1),K=a('

    FindTransactionByHash

    • Parameters: Hash

    • Returns: TransactionValue

    • Details: Returns the transaction by hash.

    Trigger

    Iroha is an event-driven architecture. Every modification of the world state emits a corresponding event that can be captured by appropriate event listeners called filters.

    INFO

    Note that Iroha shut downs all listeners on panic.

    FindAllActiveTriggerIds

    • Returns: Vec<TriggerId>

    • Details: Finds all currently active triggers, that is, triggers that have not expired at the time of the query.

    ',7),H=a('

    FindTriggerById

    • Parameters: TriggerId

    • Returns: Trigger

    • Details: Finds the trigger with the given ID.

    FindTriggerKeyValueByIdAndKey

    • Parameters: (TriggerId, Name)

    • Returns: Trigger

    • Details: Finds the value corresponding to the key in the metadata of the trigger with the given ID.

    FindTriggersByDomainId

    • Parameters: DomainId

    • Returns: Vec<Trigger>

    • Details: Finds all domain triggers for the given domain ID.

    ',6);function M(Q,j,Y,W,G,X){const s=i("WarningFatQuery");return l(),r("div",null,[c,e("ul",null,[p,e("li",null,[u,n(s)])]),h,e("ul",null,[g,e("li",null,[m,n(s)])]),y,e("ul",null,[f,b,e("li",null,[_,A,n(s)])]),k,e("ul",null,[q,e("li",null,[F,n(s),T])]),I,e("ul",null,[E,e("li",null,[P,n(s),v])]),D,n(s),R,n(s),B,e("ul",null,[V,x,e("li",null,[w,n(s)])]),C,e("ul",null,[N,e("li",null,[S,n(s)])]),K,n(s),H])}const $=o(d,[["render",M]]);export{Z as __pageData,$ as default}; diff --git a/assets/reference_queries.md.e4f73f68.lean.js b/assets/reference_queries.md.e4f73f68.lean.js new file mode 100644 index 000000000..1f481c871 --- /dev/null +++ b/assets/reference_queries.md.e4f73f68.lean.js @@ -0,0 +1 @@ +import{_ as o,C as i,o as l,c as r,k as e,H as n,Q as a,a as t}from"./chunks/framework.1293becd.js";const Z=JSON.parse('{"title":"Queries","description":"","frontmatter":{},"headers":[],"relativePath":"reference/queries.md","filePath":"reference/queries.md","lastUpdated":1700563625000}'),d={name:"reference/queries.md"},c=a("",9),p=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),u=e("p",null,[e("strong",null,"Details"),t(": Returns all roles registered as "),e("em",null,"global"),t(" (as opposed to "),e("em",null,"domain-scoped"),t(") in the blockchain.")],-1),h=a("",17),g=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),m=e("p",null,[e("strong",null,"Details"),t(": Finds all accounts registered globally in the blockchain.")],-1),y=a("",7),f=e("li",null,[e("p",null,[e("strong",null,"Parameters"),t(": "),e("code",null,"DomainId")])],-1),b=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),_=e("p",null,[e("strong",null,"Details"),t(": Returns all accounts that belong to a specific domain.")],-1),A=e("p",null,[t("Note that this returns the full accounts and not the "),e("code",null,"AccountId"),t(" collection.")],-1),k=a("",5),q=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),F=e("p",null,[e("strong",null,"Details"),t(": Returns all known assets by value.")],-1),T=e("div",{class:"info custom-block"},[e("p",{class:"custom-block-title"},"INFO"),e("p",null,[t("You should note that this is not the same as "),e("code",null,"AssetDefinition"),t(". If you have one asset called e.g. "),e("code",null,"tea#wonderland"),t(" that belongs to every account on the blockchain, you will receive the aggregated value across all accounts, but not the information such as the type of the asset.")])],-1),I=e("h3",{id:"findallassetdefinitions",tabindex:"-1"},[e("code",null,"FindAllAssetDefinitions"),t(),e("a",{class:"header-anchor",href:"#findallassetdefinitions","aria-label":'Permalink to "`FindAllAssetDefinitions`"'},"​")],-1),E=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Vec")])],-1),P=e("p",null,[e("strong",null,"Details"),t(": Returns all known asset definitions by value.")],-1),v=e("div",{class:"tip custom-block"},[e("p",{class:"custom-block-title"},"TIP"),e("p",null,"To reduce the load on the network, we store the definition of an asset separate from its instances. So if you want to know if an asset is mintable or what type is stored in it, you need to query the asset definition, rather than the asset itself.")],-1),D=a("",8),R=a("",2),B=a("",22),V=e("li",null,[e("p",null,[e("strong",null,"Parameters"),t(": "),e("code",null,"DomainId")])],-1),x=e("li",null,[e("p",null,[e("strong",null,"Returns"),t(": "),e("code",null,"Domain")])],-1),w=e("p",null,[e("strong",null,"Details"),t(": Gets the domain corresponding to the given identifier.")],-1),C=a("",11),N=a("",2),S=e("p",null,[e("strong",null,"Details"),t(": Returns the full set of transactions that an account has submitted throughout the existence of the blockchain.")],-1),K=a("",7),H=a("",6);function M(Q,j,Y,W,G,X){const s=i("WarningFatQuery");return l(),r("div",null,[c,e("ul",null,[p,e("li",null,[u,n(s)])]),h,e("ul",null,[g,e("li",null,[m,n(s)])]),y,e("ul",null,[f,b,e("li",null,[_,A,n(s)])]),k,e("ul",null,[q,e("li",null,[F,n(s),T])]),I,e("ul",null,[E,e("li",null,[P,n(s),v])]),D,n(s),R,n(s),B,e("ul",null,[V,x,e("li",null,[w,n(s)])]),C,e("ul",null,[N,e("li",null,[S,n(s)])]),K,n(s),H])}const $=o(d,[["render",M]]);export{Z as __pageData,$ as default}; diff --git a/assets/reference_specification.md.2a95f9a6.js b/assets/reference_specification.md.2a95f9a6.js new file mode 100644 index 000000000..4fdcb8711 --- /dev/null +++ b/assets/reference_specification.md.2a95f9a6.js @@ -0,0 +1 @@ +import{u as o,o as i,c as s,k as e,a,t as c,l as n}from"./chunks/framework.1293becd.js";const r=e("h1",{id:"api-specification",tabindex:"-1"},[a("API Specification "),e("a",{class:"header-anchor",href:"#api-specification","aria-label":'Permalink to "API Specification"'},"​")],-1),l={class:"info custom-block"},p=e("p",{class:"custom-block-title"},"INFO",-1),d=e("p",null,[a("This page contains a copy of "),e("code",null,"api_spec.md"),a(" from "),e("code",null,"hyperledger/iroha#iroha2-dev"),a(". You can read the most up-to-date version of it on "),e("a",{href:"https://github.com/hyperledger/iroha/blob/iroha2-dev/docs/source/references/api_spec.md",target:"_blank",rel:"noreferrer"},"GitHub"),a(".")],-1),b=JSON.parse('{"title":"API Specification","description":"","frontmatter":{},"headers":[],"relativePath":"reference/specification.md","filePath":"reference/specification.md","lastUpdated":1700563625000}'),f={name:"reference/specification.md"},g=Object.assign(f,{setup(h){const{page:t}=o();return(_,u)=>(i(),s("div",null,[r,e("div",l,[p,d,e("p",null,[a("Please note this page was last updated on "),e("b",null,c(new Date(n(t).lastUpdated).toLocaleString()),1),a(".")])])]))}});export{b as __pageData,g as default}; diff --git a/assets/reference_specification.md.2a95f9a6.lean.js b/assets/reference_specification.md.2a95f9a6.lean.js new file mode 100644 index 000000000..4fdcb8711 --- /dev/null +++ b/assets/reference_specification.md.2a95f9a6.lean.js @@ -0,0 +1 @@ +import{u as o,o as i,c as s,k as e,a,t as c,l as n}from"./chunks/framework.1293becd.js";const r=e("h1",{id:"api-specification",tabindex:"-1"},[a("API Specification "),e("a",{class:"header-anchor",href:"#api-specification","aria-label":'Permalink to "API Specification"'},"​")],-1),l={class:"info custom-block"},p=e("p",{class:"custom-block-title"},"INFO",-1),d=e("p",null,[a("This page contains a copy of "),e("code",null,"api_spec.md"),a(" from "),e("code",null,"hyperledger/iroha#iroha2-dev"),a(". You can read the most up-to-date version of it on "),e("a",{href:"https://github.com/hyperledger/iroha/blob/iroha2-dev/docs/source/references/api_spec.md",target:"_blank",rel:"noreferrer"},"GitHub"),a(".")],-1),b=JSON.parse('{"title":"API Specification","description":"","frontmatter":{},"headers":[],"relativePath":"reference/specification.md","filePath":"reference/specification.md","lastUpdated":1700563625000}'),f={name:"reference/specification.md"},g=Object.assign(f,{setup(h){const{page:t}=o();return(_,u)=>(i(),s("div",null,[r,e("div",l,[p,d,e("p",null,[a("Please note this page was last updated on "),e("b",null,c(new Date(n(t).lastUpdated).toLocaleString()),1),a(".")])])]))}});export{b as __pageData,g as default}; diff --git a/assets/reference_torii-endpoints.md.b5467706.js b/assets/reference_torii-endpoints.md.b5467706.js new file mode 100644 index 000000000..3805df309 --- /dev/null +++ b/assets/reference_torii-endpoints.md.b5467706.js @@ -0,0 +1,147 @@ +import{_ as s,o as n,c as a,Q as e}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Torii Endpoints","description":"","frontmatter":{},"headers":[],"relativePath":"reference/torii-endpoints.md","filePath":"reference/torii-endpoints.md","lastUpdated":1700563625000}'),o={name:"reference/torii-endpoints.md"},t=e(`

    Torii Endpoints

    API Version

    • Protocol: HTTP
    • Method: GET
    • Endpoint: /api_version

    Responses:

    200 OK: The current version of API used by Iroha returned as a JSON string. Grabbed from the genesis block's version, so at least a minimal subnet of 4 peers should be running and the genesis be submitted at the time of request.

    json
    "1"
    "1"

    Blocks Stream

    • Protocol: HTTP
    • Protocol Upgrade:\`WebSocket
    • Endpoint: /block/stream

    The client should send a BlockSubscriptionRequest to initiate communication after the WebSocket handshake. Then the server sends a BlockMessage. Messages are SCALE-encoded[1].

    Via this endpoint, the client first provides the starting block number (i.e. height) in the subscription request. After sending the confirmation message, the server starts streaming all the blocks from the given block number up to the current block and continues to stream blocks as they are added to the blockchain.

    Events

    • Protocol: HTTP
    • Protocol Upgrade: WebSocket
    • Endpoint: /events

    After a handshake, the client should send an EventSubscriptionRequest. Then the server sends an EventMessage. Messages are SCALE-encoded[1:1].

    Transaction Events

    Transaction event statuses can be either Validating, Committed or Rejected.

    Transaction statuses proceed from Validating to either Committed or Rejected. However, due to the distributed nature of the network, some peers might receive events out of order (e.g. Committed before Validating).

    Some peers in the network may be offline for the validation round. If the client connects to them while they are offline, the peers might not respond with the Validating status. But when the offline peers come back online they will synchronize the blocks. They are then guaranteed to respond with the Committed (or Rejected) status depending on the information found in the block.

    Health

    • Protocol: HTTP
    • Method: GET
    • Endpoint: /health

    Responses with 200 OK and a current status of peer as a JSON string:

    json
    "Healthy"
    "Healthy"

    Metrics

    • Protocol: HTTP
    • Method: GET
    • Endpoint: /metrics

    Responses:

    200 OK reports 8 of 10 metrics:

    Sample Prometheus metrics
    bash
    # HELP accounts User accounts registered at this time
    +# TYPE accounts gauge
    +accounts{domain="genesis"} 1
    +accounts{domain="wonderland"} 1
    +# HELP block_height Current block height
    +# TYPE block_height counter
    +block_height 1
    +# HELP connected_peers Total number of currently connected peers
    +# TYPE connected_peers gauge
    +connected_peers 0
    +# HELP domains Total number of domains
    +# TYPE domains gauge
    +domains 2
    +# HELP tx_amount average amount involved in a transaction on this peer
    +# TYPE tx_amount histogram
    +tx_amount_bucket{le="0.005"} 0
    +tx_amount_bucket{le="0.01"} 0
    +tx_amount_bucket{le="0.025"} 0
    +tx_amount_bucket{le="0.05"} 0
    +tx_amount_bucket{le="0.1"} 0
    +tx_amount_bucket{le="0.25"} 0
    +tx_amount_bucket{le="0.5"} 0
    +tx_amount_bucket{le="1"} 0
    +tx_amount_bucket{le="2.5"} 0
    +tx_amount_bucket{le="5"} 0
    +tx_amount_bucket{le="10"} 0
    +tx_amount_bucket{le="+Inf"} 2
    +tx_amount_sum 26
    +tx_amount_count 2
    +# HELP txs Transactions committed
    +# TYPE txs counter
    +txs{type="accepted"} 1
    +txs{type="rejected"} 0
    +txs{type="total"} 1
    +# HELP uptime_since_genesis_ms Network up-time, from creation of the genesis block
    +# TYPE uptime_since_genesis_ms gauge
    +uptime_since_genesis_ms 54572974
    +# HELP view_changes Number of view_changes in the current round
    +# TYPE view_changes gauge
    +view_changes 0
    # HELP accounts User accounts registered at this time
    +# TYPE accounts gauge
    +accounts{domain="genesis"} 1
    +accounts{domain="wonderland"} 1
    +# HELP block_height Current block height
    +# TYPE block_height counter
    +block_height 1
    +# HELP connected_peers Total number of currently connected peers
    +# TYPE connected_peers gauge
    +connected_peers 0
    +# HELP domains Total number of domains
    +# TYPE domains gauge
    +domains 2
    +# HELP tx_amount average amount involved in a transaction on this peer
    +# TYPE tx_amount histogram
    +tx_amount_bucket{le="0.005"} 0
    +tx_amount_bucket{le="0.01"} 0
    +tx_amount_bucket{le="0.025"} 0
    +tx_amount_bucket{le="0.05"} 0
    +tx_amount_bucket{le="0.1"} 0
    +tx_amount_bucket{le="0.25"} 0
    +tx_amount_bucket{le="0.5"} 0
    +tx_amount_bucket{le="1"} 0
    +tx_amount_bucket{le="2.5"} 0
    +tx_amount_bucket{le="5"} 0
    +tx_amount_bucket{le="10"} 0
    +tx_amount_bucket{le="+Inf"} 2
    +tx_amount_sum 26
    +tx_amount_count 2
    +# HELP txs Transactions committed
    +# TYPE txs counter
    +txs{type="accepted"} 1
    +txs{type="rejected"} 0
    +txs{type="total"} 1
    +# HELP uptime_since_genesis_ms Network up-time, from creation of the genesis block
    +# TYPE uptime_since_genesis_ms gauge
    +uptime_since_genesis_ms 54572974
    +# HELP view_changes Number of view_changes in the current round
    +# TYPE view_changes gauge
    +view_changes 0

    Learn how to use metrics.

    Query

    • Protocol: HTTP
    • Method: POST
    • Endpoint: /query
    • Expects:
      • Body: SCALE-encoded[1:2] VersionedSignedQuery
      • Query parameters:
        • start: An optional parameter in queries where results can be indexed. Use to return results from a specified point. Results are ordered by id, which uses Rust's PartialOrd and Ord traits.
        • limit: An optional parameter in queries where results can be indexed. Use to return a specific number of results.
        • sort_by_metadata_key: An optional parameter in queries. Use to sort results containing metadata with a given key.
        • fetch_size: An optional parameter in queries. Use to specify the exact number of results returned by a query.

    Responses:

    ResponseStatusBody
    Success200VersionedBatchedResponse<Value>
    Conversion Error400QueryExecutionFail::Conversion(String)
    Evaluate Error400QueryExecutionFail::Evaluate(String)
    Signature Error401QueryExecutionFail::Signature(String)
    Permission Error403QueryExecutionFail::Permission(String)
    Find Error404QueryExecutionFail::Find(FindError)

    Account Not Found 404

    Whether each prerequisite object was found and FindError:

    DomainAccountFindError
    N-FindError::Domain(DomainId)
    YNFindError::Account(AccountId)

    Asset Not Found 404

    Whether each prerequisite object was found and FindError:

    DomainAccountAsset DefinitionAssetFindError
    N---FindError::Domain(DomainId)
    YN--FindError::Account(AccountId)
    Y-N-FindError::AssetDefinition(AssetDefinitionId)
    YYYNFindError::Asset(AssetId)

    Status

    • Protocol: HTTP
    • Method: GET
    • Endpoint: /status
    • Expects: an optional Accept: application/x-parity-scale request header to encode response with SCALE[1:3]. Otherwise, will fall back to application/json.
    • Responses: with Content-Type set either to application/json or application/x-parity-scale, and an accordingly encoded response body.

    Response body is of the following Status structure:

    rust
    struct Status {
    +    /// Number of connected peers, except for the reporting peer itself
    +    peers: u64,
    +    /// Number of committed blocks
    +    blocks: u64,
    +    /// Number of accepted transactions
    +    txs_accepted: u64,
    +    /// Number of rejected transactions
    +    txs_rejected: u64,
    +    /// Uptime since genesis block creation
    +    uptime: Uptime,
    +    /// Number of view changes in the current round
    +    view_changes: u64,
    +    /// Number of the transactions in the queue
    +    queue_size: u64,
    +}
    +
    +struct Uptime {
    +    secs: u64,
    +    nanos: u32
    +}
    struct Status {
    +    /// Number of connected peers, except for the reporting peer itself
    +    peers: u64,
    +    /// Number of committed blocks
    +    blocks: u64,
    +    /// Number of accepted transactions
    +    txs_accepted: u64,
    +    /// Number of rejected transactions
    +    txs_rejected: u64,
    +    /// Uptime since genesis block creation
    +    uptime: Uptime,
    +    /// Number of view changes in the current round
    +    view_changes: u64,
    +    /// Number of the transactions in the queue
    +    queue_size: u64,
    +}
    +
    +struct Uptime {
    +    secs: u64,
    +    nanos: u32
    +}

    JSON Precision Lost

    Almost all fields in the Status structure are 64-bit integers, and they are encoded in JSON as-is. Since native JSON's number type according to the specification effectively is f64, the precision might be lost on deserialization, for example, in JavaScript's JSON.parse. For more details, see related issue.

    Compact Form in SCALE

    Fields with type u64 serialized in the Compact form.

    Sample responses

    These samples represent the same data:

    json
    {
    +  "peers": 4,
    +  "blocks": 5,
    +  "txs_accepted": 31,
    +  "txs_rejected": 3,
    +  "uptime": {
    +    "secs": 5,
    +    "nanos": 937000000
    +  },
    +  "view_changes": 2,
    +  "queue_size": 18
    +}
    {
    +  "peers": 4,
    +  "blocks": 5,
    +  "txs_accepted": 31,
    +  "txs_rejected": 3,
    +  "uptime": {
    +    "secs": 5,
    +    "nanos": 937000000
    +  },
    +  "view_changes": 2,
    +  "queue_size": 18
    +}
    10 14 7C 0C 14 40 7C D9 37 08 48
    10 14 7C 0C 14 40 7C D9 37 08 48

    Sub-routing

    To obtain the value of a specific field, one can append the name of the field to the path, e.g. /status/peers. This returns the corresponding JSON value.

    json
    4
    4
    json
    {
    +  "secs": 5,
    +  "nanos": 937000000
    +}
    {
    +  "secs": 5,
    +  "nanos": 937000000
    +}
    json
    5
    5

    Transaction

    Responses:

    StatusDescription
    200Transaction Accepted (But not guaranteed to have passed consensus yet)
    400Transaction Rejected (Malformed)
    401Transaction Rejected (Improperly signed)

    1. For more information on Parity SCALE Codec check Substrate Dev Hub and codec's GitHub repository.

      ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
    `,53),l=[t];function p(c,r,i,d,y,u){return n(),a("div",null,l)}const m=s(o,[["render",p]]);export{h as __pageData,m as default}; diff --git a/assets/reference_torii-endpoints.md.b5467706.lean.js b/assets/reference_torii-endpoints.md.b5467706.lean.js new file mode 100644 index 000000000..04d82d739 --- /dev/null +++ b/assets/reference_torii-endpoints.md.b5467706.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as e}from"./chunks/framework.1293becd.js";const h=JSON.parse('{"title":"Torii Endpoints","description":"","frontmatter":{},"headers":[],"relativePath":"reference/torii-endpoints.md","filePath":"reference/torii-endpoints.md","lastUpdated":1700563625000}'),o={name:"reference/torii-endpoints.md"},t=e("",53),l=[t];function p(c,r,i,d,y,u){return n(),a("div",null,l)}const m=s(o,[["render",p]]);export{h as __pageData,m as default}; diff --git a/assets/sample-vue-app.4caffed6.gif b/assets/sample-vue-app.4caffed6.gif new file mode 100644 index 000000000..05578e52a Binary files /dev/null and b/assets/sample-vue-app.4caffed6.gif differ diff --git a/assets/style.60dfd289.css b/assets/style.60dfd289.css new file mode 100644 index 000000000..7fa82f817 --- /dev/null +++ b/assets/style.60dfd289.css @@ -0,0 +1 @@ +@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/iroha-2-docs/assets/inter-roman-cyrillic.5f2c6c8c.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/iroha-2-docs/assets/inter-roman-cyrillic-ext.e75737ce.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/iroha-2-docs/assets/inter-roman-greek.d5a6d92a.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/iroha-2-docs/assets/inter-roman-greek-ext.ab0619bc.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/iroha-2-docs/assets/inter-roman-latin.2ed14f66.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/iroha-2-docs/assets/inter-roman-latin-ext.0030eebd.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/iroha-2-docs/assets/inter-roman-vietnamese.14ce25a6.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/iroha-2-docs/assets/inter-italic-cyrillic.ea42a392.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/iroha-2-docs/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/iroha-2-docs/assets/inter-italic-greek.8f4463c4.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/iroha-2-docs/assets/inter-italic-greek-ext.4fbe9427.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/iroha-2-docs/assets/inter-italic-latin.bd3b6f56.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/iroha-2-docs/assets/inter-italic-latin-ext.bd8920cc.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/iroha-2-docs/assets/inter-italic-vietnamese.6ce511fb.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Chinese Quotes;src:local("PingFang SC Regular"),local("PingFang SC"),local("SimHei"),local("Source Han Sans SC");unicode-range:U+2018,U+2019,U+201C,U+201D}:root{--vp-c-white: #ffffff;--vp-c-black: #000000;--vp-c-neutral: var(--vp-c-black);--vp-c-neutral-inverse: var(--vp-c-white)}.dark{--vp-c-neutral: var(--vp-c-white);--vp-c-neutral-inverse: var(--vp-c-black)}:root{--vp-c-gray-1: #dddde3;--vp-c-gray-2: #e4e4e9;--vp-c-gray-3: #ebebef;--vp-c-gray-soft: rgba(142, 150, 170, .14);--vp-c-indigo-1: #3451b2;--vp-c-indigo-2: #3a5ccc;--vp-c-indigo-3: #5672cd;--vp-c-indigo-soft: rgba(100, 108, 255, .14);--vp-c-green-1: #18794e;--vp-c-green-2: #299764;--vp-c-green-3: #30a46c;--vp-c-green-soft: rgba(16, 185, 129, .14);--vp-c-yellow-1: #915930;--vp-c-yellow-2: #946300;--vp-c-yellow-3: #9f6a00;--vp-c-yellow-soft: rgba(234, 179, 8, .14);--vp-c-red-1: #b8272c;--vp-c-red-2: #d5393e;--vp-c-red-3: #e0575b;--vp-c-red-soft: rgba(244, 63, 94, .14);--vp-c-sponsor: #db2777}.dark{--vp-c-gray-1: #515c67;--vp-c-gray-2: #414853;--vp-c-gray-3: #32363f;--vp-c-gray-soft: rgba(101, 117, 133, .16);--vp-c-indigo-1: #a8b1ff;--vp-c-indigo-2: #5c73e7;--vp-c-indigo-3: #3e63dd;--vp-c-indigo-soft: rgba(100, 108, 255, .16);--vp-c-green-1: #3dd68c;--vp-c-green-2: #30a46c;--vp-c-green-3: #298459;--vp-c-green-soft: rgba(16, 185, 129, .16);--vp-c-yellow-1: #f9b44e;--vp-c-yellow-2: #da8b17;--vp-c-yellow-3: #a46a0a;--vp-c-yellow-soft: rgba(234, 179, 8, .16);--vp-c-red-1: #f66f81;--vp-c-red-2: #f14158;--vp-c-red-3: #b62a3c;--vp-c-red-soft: rgba(244, 63, 94, .16)}:root{--vp-c-bg: #ffffff;--vp-c-bg-alt: #f6f6f7;--vp-c-bg-elv: #ffffff;--vp-c-bg-soft: #f6f6f7}.dark{--vp-c-bg: #1b1b1f;--vp-c-bg-alt: #161618;--vp-c-bg-elv: #202127;--vp-c-bg-soft: #202127}:root{--vp-c-border: #c2c2c4;--vp-c-divider: #e2e2e3;--vp-c-gutter: #e2e2e3}.dark{--vp-c-border: #3c3f44;--vp-c-divider: #2e2e32;--vp-c-gutter: #000000}:root{--vp-c-text-1: rgba(60, 60, 67);--vp-c-text-2: rgba(60, 60, 67, .78);--vp-c-text-3: rgba(60, 60, 67, .56)}.dark{--vp-c-text-1: rgba(255, 255, 245, .86);--vp-c-text-2: rgba(235, 235, 245, .6);--vp-c-text-3: rgba(235, 235, 245, .38)}:root{--vp-c-default-1: var(--vp-c-gray-1);--vp-c-default-2: var(--vp-c-gray-2);--vp-c-default-3: var(--vp-c-gray-3);--vp-c-default-soft: var(--vp-c-gray-soft);--vp-c-brand-1: var(--vp-c-indigo-1);--vp-c-brand-2: var(--vp-c-indigo-2);--vp-c-brand-3: var(--vp-c-indigo-3);--vp-c-brand-soft: var(--vp-c-indigo-soft);--vp-c-brand: var(--vp-c-brand-1);--vp-c-tip-1: var(--vp-c-brand-1);--vp-c-tip-2: var(--vp-c-brand-2);--vp-c-tip-3: var(--vp-c-brand-3);--vp-c-tip-soft: var(--vp-c-brand-soft);--vp-c-warning-1: var(--vp-c-yellow-1);--vp-c-warning-2: var(--vp-c-yellow-2);--vp-c-warning-3: var(--vp-c-yellow-3);--vp-c-warning-soft: var(--vp-c-yellow-soft);--vp-c-danger-1: var(--vp-c-red-1);--vp-c-danger-2: var(--vp-c-red-2);--vp-c-danger-3: var(--vp-c-red-3);--vp-c-danger-soft: var(--vp-c-red-soft)}:root{--vp-font-family-base: "Chinese Quotes", "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--vp-font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}:root{--vp-shadow-1: 0 1px 2px rgba(0, 0, 0, .04), 0 1px 2px rgba(0, 0, 0, .06);--vp-shadow-2: 0 3px 12px rgba(0, 0, 0, .07), 0 1px 4px rgba(0, 0, 0, .07);--vp-shadow-3: 0 12px 32px rgba(0, 0, 0, .1), 0 2px 6px rgba(0, 0, 0, .08);--vp-shadow-4: 0 14px 44px rgba(0, 0, 0, .12), 0 3px 9px rgba(0, 0, 0, .12);--vp-shadow-5: 0 18px 56px rgba(0, 0, 0, .16), 0 4px 12px rgba(0, 0, 0, .16)}:root{--vp-z-index-footer: 10;--vp-z-index-local-nav: 20;--vp-z-index-nav: 30;--vp-z-index-layout-top: 40;--vp-z-index-backdrop: 50;--vp-z-index-sidebar: 60}:root{--vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E")}:root{--vp-layout-max-width: 1440px}:root{--vp-header-anchor-symbol: "#"}:root{--vp-code-line-height: 1.7;--vp-code-font-size: .875em;--vp-code-color: var(--vp-c-brand-1);--vp-code-link-color: var(--vp-c-brand-1);--vp-code-link-hover-color: var(--vp-c-brand-2);--vp-code-bg: var(--vp-c-default-soft);--vp-code-block-color: var(--vp-c-text-2);--vp-code-block-bg: var(--vp-c-bg-alt);--vp-code-block-divider-color: var(--vp-c-gutter);--vp-code-lang-color: var(--vp-c-text-3);--vp-code-line-highlight-color: var(--vp-c-default-soft);--vp-code-line-number-color: var(--vp-c-text-3);--vp-code-line-diff-add-color: var(--vp-c-green-soft);--vp-code-line-diff-add-symbol-color: var(--vp-c-green-1);--vp-code-line-diff-remove-color: var(--vp-c-red-soft);--vp-code-line-diff-remove-symbol-color: var(--vp-c-red-1);--vp-code-line-warning-color: var(--vp-c-yellow-soft);--vp-code-line-error-color: var(--vp-c-red-soft);--vp-code-copy-code-border-color: var(--vp-c-divider);--vp-code-copy-code-bg: var(--vp-c-bg-soft);--vp-code-copy-code-hover-border-color: var(--vp-c-divider);--vp-code-copy-code-hover-bg: var(--vp-c-bg);--vp-code-copy-code-active-text: var(--vp-c-text-2);--vp-code-copy-copied-text-content: "Copied";--vp-code-tab-divider: var(--vp-code-block-divider-color);--vp-code-tab-text-color: var(--vp-c-text-2);--vp-code-tab-bg: var(--vp-code-block-bg);--vp-code-tab-hover-text-color: var(--vp-c-text-1);--vp-code-tab-active-text-color: var(--vp-c-text-1);--vp-code-tab-active-bar-color: var(--vp-c-brand-1)}:root{--vp-button-brand-border: transparent;--vp-button-brand-text: var(--vp-c-white);--vp-button-brand-bg: var(--vp-c-brand-3);--vp-button-brand-hover-border: transparent;--vp-button-brand-hover-text: var(--vp-c-white);--vp-button-brand-hover-bg: var(--vp-c-brand-2);--vp-button-brand-active-border: transparent;--vp-button-brand-active-text: var(--vp-c-white);--vp-button-brand-active-bg: var(--vp-c-brand-1);--vp-button-alt-border: transparent;--vp-button-alt-text: var(--vp-c-text-1);--vp-button-alt-bg: var(--vp-c-default-3);--vp-button-alt-hover-border: transparent;--vp-button-alt-hover-text: var(--vp-c-text-1);--vp-button-alt-hover-bg: var(--vp-c-default-2);--vp-button-alt-active-border: transparent;--vp-button-alt-active-text: var(--vp-c-text-1);--vp-button-alt-active-bg: var(--vp-c-default-1);--vp-button-sponsor-border: var(--vp-c-text-2);--vp-button-sponsor-text: var(--vp-c-text-2);--vp-button-sponsor-bg: transparent;--vp-button-sponsor-hover-border: var(--vp-c-sponsor);--vp-button-sponsor-hover-text: var(--vp-c-sponsor);--vp-button-sponsor-hover-bg: transparent;--vp-button-sponsor-active-border: var(--vp-c-sponsor);--vp-button-sponsor-active-text: var(--vp-c-sponsor);--vp-button-sponsor-active-bg: transparent}:root{--vp-custom-block-font-size: 14px;--vp-custom-block-code-font-size: 13px;--vp-custom-block-info-border: transparent;--vp-custom-block-info-text: var(--vp-c-text-1);--vp-custom-block-info-bg: var(--vp-c-default-soft);--vp-custom-block-info-code-bg: var(--vp-c-default-soft);--vp-custom-block-tip-border: transparent;--vp-custom-block-tip-text: var(--vp-c-text-1);--vp-custom-block-tip-bg: var(--vp-c-brand-soft);--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);--vp-custom-block-warning-border: transparent;--vp-custom-block-warning-text: var(--vp-c-text-1);--vp-custom-block-warning-bg: var(--vp-c-warning-soft);--vp-custom-block-warning-code-bg: var(--vp-c-warning-soft);--vp-custom-block-danger-border: transparent;--vp-custom-block-danger-text: var(--vp-c-text-1);--vp-custom-block-danger-bg: var(--vp-c-danger-soft);--vp-custom-block-danger-code-bg: var(--vp-c-danger-soft);--vp-custom-block-details-border: var(--vp-custom-block-info-border);--vp-custom-block-details-text: var(--vp-custom-block-info-text);--vp-custom-block-details-bg: var(--vp-custom-block-info-bg);--vp-custom-block-details-code-bg: var(--vp-custom-block-info-code-bg)}:root{--vp-input-border-color: var(--vp-c-border);--vp-input-bg-color: var(--vp-c-bg-alt);--vp-input-switch-bg-color: var(--vp-c-gray-soft)}:root{--vp-nav-height: 64px;--vp-nav-bg-color: var(--vp-c-bg);--vp-nav-screen-bg-color: var(--vp-c-bg);--vp-nav-logo-height: 24px}.hide-nav{--vp-nav-height: 0px}.hide-nav .VPSidebar{--vp-nav-height: 22px}:root{--vp-local-nav-bg-color: var(--vp-c-bg)}:root{--vp-sidebar-width: 272px;--vp-sidebar-bg-color: var(--vp-c-bg-alt)}:root{--vp-backdrop-bg-color: rgba(0, 0, 0, .6)}:root{--vp-home-hero-name-color: var(--vp-c-brand-1);--vp-home-hero-name-background: transparent;--vp-home-hero-image-background-image: none;--vp-home-hero-image-filter: none}:root{--vp-badge-info-border: transparent;--vp-badge-info-text: var(--vp-c-text-2);--vp-badge-info-bg: var(--vp-c-default-soft);--vp-badge-tip-border: transparent;--vp-badge-tip-text: var(--vp-c-brand-1);--vp-badge-tip-bg: var(--vp-c-brand-soft);--vp-badge-warning-border: transparent;--vp-badge-warning-text: var(--vp-c-warning-1);--vp-badge-warning-bg: var(--vp-c-warning-soft);--vp-badge-danger-border: transparent;--vp-badge-danger-text: var(--vp-c-danger-1);--vp-badge-danger-bg: var(--vp-c-danger-soft)}:root{--vp-carbon-ads-text-color: var(--vp-c-text-1);--vp-carbon-ads-poweredby-color: var(--vp-c-text-2);--vp-carbon-ads-bg-color: var(--vp-c-bg-soft);--vp-carbon-ads-hover-text-color: var(--vp-c-brand-1);--vp-carbon-ads-hover-poweredby-color: var(--vp-c-text-1)}:root{--vp-local-search-bg: var(--vp-c-bg);--vp-local-search-result-bg: var(--vp-c-bg);--vp-local-search-result-border: var(--vp-c-divider);--vp-local-search-result-selected-bg: var(--vp-c-bg);--vp-local-search-result-selected-border: var(--vp-c-brand-1);--vp-local-search-highlight-bg: var(--vp-c-brand-1);--vp-local-search-highlight-text: var(--vp-c-neutral-inverse)}@media (prefers-reduced-motion: reduce){*,:before,:after{animation-delay:-1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important;background-attachment:initial!important;scroll-behavior:auto!important;transition-duration:0s!important;transition-delay:0s!important}}*,:before,:after{box-sizing:border-box}html{line-height:1.4;font-size:16px;-webkit-text-size-adjust:100%}html.dark{color-scheme:dark}body{margin:0;width:100%;min-width:320px;min-height:100vh;line-height:24px;font-family:var(--vp-font-family-base);font-size:16px;font-weight:400;color:var(--vp-c-text-1);background-color:var(--vp-c-bg);direction:ltr;font-synthesis:style;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}main{display:block}h1,h2,h3,h4,h5,h6{margin:0;line-height:24px;font-size:16px;font-weight:400}p{margin:0}strong,b{font-weight:600}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}a{color:inherit;text-decoration:inherit}ol,ul{list-style:none;margin:0;padding:0}blockquote{margin:0}pre,code,kbd,samp{font-family:var(--vp-font-family-mono)}img,svg,video,canvas,audio,iframe,embed,object{display:block}figure{margin:0}img,video{max-width:100%;height:auto}button,input,optgroup,select,textarea{border:0;padding:0;line-height:inherit;color:inherit}button{padding:0;font-family:inherit;background-color:transparent;background-image:none}button:enabled,[role=button]:enabled{cursor:pointer}button:focus,button:focus-visible{outline:1px dotted;outline:4px auto -webkit-focus-ring-color}button:focus:not(:focus-visible){outline:none!important}input:focus,textarea:focus,select:focus{outline:none}table{border-collapse:collapse}input{background-color:transparent}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--vp-c-text-3)}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--vp-c-text-3)}input::placeholder,textarea::placeholder{color:var(--vp-c-text-3)}input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=number]{-moz-appearance:textfield}textarea{resize:vertical}select{-webkit-appearance:none}fieldset{margin:0;padding:0}h1,h2,h3,h4,h5,h6,li,p{overflow-wrap:break-word}vite-error-overlay{z-index:9999}mjx-container{display:inline-block;margin:auto 2px -2px}mjx-container>svg{margin:auto}.visually-hidden{position:absolute;width:1px;height:1px;white-space:nowrap;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden}.custom-block{border:1px solid transparent;border-radius:8px;padding:16px 16px 8px;line-height:24px;font-size:var(--vp-custom-block-font-size);color:var(--vp-c-text-2)}.custom-block.info{border-color:var(--vp-custom-block-info-border);color:var(--vp-custom-block-info-text);background-color:var(--vp-custom-block-info-bg)}.custom-block.info a,.custom-block.info code{color:var(--vp-c-brand-1)}.custom-block.info a:hover{color:var(--vp-c-brand-2)}.custom-block.info code{background-color:var(--vp-custom-block-info-code-bg)}.custom-block.tip{border-color:var(--vp-custom-block-tip-border);color:var(--vp-custom-block-tip-text);background-color:var(--vp-custom-block-tip-bg)}.custom-block.tip a,.custom-block.tip code{color:var(--vp-c-brand-1)}.custom-block.tip a:hover{color:var(--vp-c-brand-2)}.custom-block.tip code{background-color:var(--vp-custom-block-tip-code-bg)}.custom-block.warning{border-color:var(--vp-custom-block-warning-border);color:var(--vp-custom-block-warning-text);background-color:var(--vp-custom-block-warning-bg)}.custom-block.warning a,.custom-block.warning code{color:var(--vp-c-warning-1)}.custom-block.warning a:hover{color:var(--vp-c-warning-2)}.custom-block.warning code{background-color:var(--vp-custom-block-warning-code-bg)}.custom-block.danger{border-color:var(--vp-custom-block-danger-border);color:var(--vp-custom-block-danger-text);background-color:var(--vp-custom-block-danger-bg)}.custom-block.danger a,.custom-block.danger code{color:var(--vp-c-danger-1)}.custom-block.danger a:hover{color:var(--vp-c-danger-2)}.custom-block.danger code{background-color:var(--vp-custom-block-danger-code-bg)}.custom-block.details{border-color:var(--vp-custom-block-details-border);color:var(--vp-custom-block-details-text);background-color:var(--vp-custom-block-details-bg)}.custom-block.details a{color:var(--vp-c-brand-1)}.custom-block.details a:hover{color:var(--vp-c-brand-2)}.custom-block.details code{background-color:var(--vp-custom-block-details-code-bg)}.custom-block-title{font-weight:600}.custom-block p+p{margin:8px 0}.custom-block.details summary{margin:0 0 8px;font-weight:700;cursor:pointer}.custom-block.details summary+p{margin:8px 0}.custom-block a{color:inherit;font-weight:600;text-decoration:underline;text-underline-offset:2px;transition:opacity .25s}.custom-block a:hover{opacity:.75}.custom-block code{font-size:var(--vp-custom-block-code-font-size)}.custom-block.custom-block th,.custom-block.custom-block blockquote>p{font-size:var(--vp-custom-block-font-size);color:inherit}.dark .vp-code-light{display:none}html:not(.dark) .vp-code-dark{display:none}.vp-code-group{margin-top:16px}.vp-code-group .tabs{position:relative;display:flex;margin-right:-24px;margin-left:-24px;padding:0 12px;background-color:var(--vp-code-tab-bg);overflow-x:auto;overflow-y:hidden;box-shadow:inset 0 -1px var(--vp-code-tab-divider)}@media (min-width: 640px){.vp-code-group .tabs{margin-right:0;margin-left:0;border-radius:8px 8px 0 0}}.vp-code-group .tabs input{position:fixed;opacity:0;pointer-events:none}.vp-code-group .tabs label{position:relative;display:inline-block;border-bottom:1px solid transparent;padding:0 12px;line-height:48px;font-size:14px;font-weight:500;color:var(--vp-code-tab-text-color);white-space:nowrap;cursor:pointer;transition:color .25s}.vp-code-group .tabs label:after{position:absolute;right:8px;bottom:-1px;left:8px;z-index:1;height:2px;border-radius:2px;content:"";background-color:transparent;transition:background-color .25s}.vp-code-group label:hover{color:var(--vp-code-tab-hover-text-color)}.vp-code-group input:checked+label{color:var(--vp-code-tab-active-text-color)}.vp-code-group input:checked+label:after{background-color:var(--vp-code-tab-active-bar-color)}.vp-code-group div[class*=language-],.vp-block{display:none;margin-top:0!important;border-top-left-radius:0!important;border-top-right-radius:0!important}.vp-code-group div[class*=language-].active,.vp-block.active{display:block}.vp-block{padding:20px 24px}.vp-doc h1,.vp-doc h2,.vp-doc h3,.vp-doc h4,.vp-doc h5,.vp-doc h6{position:relative;font-weight:600;outline:none}.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:28px}.vp-doc h2{margin:48px 0 16px;border-top:1px solid var(--vp-c-divider);padding-top:24px;letter-spacing:-.02em;line-height:32px;font-size:24px}.vp-doc h3{margin:32px 0 0;letter-spacing:-.01em;line-height:28px;font-size:20px}.vp-doc .header-anchor{position:absolute;top:0;left:0;margin-left:-.87em;font-weight:500;-webkit-user-select:none;user-select:none;opacity:0;text-decoration:none;transition:color .25s,opacity .25s}.vp-doc .header-anchor:before{content:var(--vp-header-anchor-symbol)}.vp-doc h1:hover .header-anchor,.vp-doc h1 .header-anchor:focus,.vp-doc h2:hover .header-anchor,.vp-doc h2 .header-anchor:focus,.vp-doc h3:hover .header-anchor,.vp-doc h3 .header-anchor:focus,.vp-doc h4:hover .header-anchor,.vp-doc h4 .header-anchor:focus,.vp-doc h5:hover .header-anchor,.vp-doc h5 .header-anchor:focus,.vp-doc h6:hover .header-anchor,.vp-doc h6 .header-anchor:focus{opacity:1}@media (min-width: 768px){.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:32px}}.vp-doc h2 .header-anchor{top:24px}.vp-doc p,.vp-doc summary{margin:16px 0}.vp-doc p{line-height:28px}.vp-doc blockquote{margin:16px 0;border-left:2px solid var(--vp-c-divider);padding-left:16px;transition:border-color .5s}.vp-doc blockquote>p{margin:0;font-size:16px;color:var(--vp-c-text-2);transition:color .5s}.vp-doc a{font-weight:500;color:var(--vp-c-brand-1);text-decoration:underline;text-underline-offset:2px;transition:color .25s,opacity .25s}.vp-doc a:hover{color:var(--vp-c-brand-2)}.vp-doc strong{font-weight:600}.vp-doc ul,.vp-doc ol{padding-left:1.25rem;margin:16px 0}.vp-doc ul{list-style:disc}.vp-doc ol{list-style:decimal}.vp-doc li+li{margin-top:8px}.vp-doc li>ol,.vp-doc li>ul{margin:8px 0 0}.vp-doc table{display:block;border-collapse:collapse;margin:20px 0;overflow-x:auto}.vp-doc tr{border-top:1px solid var(--vp-c-divider);transition:background-color .5s}.vp-doc tr:nth-child(2n){background-color:var(--vp-c-bg-soft)}.vp-doc th,.vp-doc td{border:1px solid var(--vp-c-divider);padding:8px 16px}.vp-doc th{text-align:left;font-size:14px;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-doc td{font-size:14px}.vp-doc hr{margin:16px 0;border:none;border-top:1px solid var(--vp-c-divider)}.vp-doc .custom-block{margin:16px 0}.vp-doc .custom-block p{margin:8px 0;line-height:24px}.vp-doc .custom-block p:first-child{margin:0}.vp-doc .custom-block div[class*=language-]{margin:8px 0;border-radius:8px}.vp-doc .custom-block div[class*=language-] code{font-weight:400;background-color:transparent}.vp-doc .custom-block .vp-code-group .tabs{margin:0;border-radius:8px 8px 0 0}.vp-doc :not(pre,h1,h2,h3,h4,h5,h6)>code{font-size:var(--vp-code-font-size);color:var(--vp-code-color)}.vp-doc :not(pre)>code{border-radius:4px;padding:3px 6px;background-color:var(--vp-code-bg);transition:color .25s,background-color .5s}.vp-doc a>code{color:var(--vp-code-link-color)}.vp-doc a:hover>code{color:var(--vp-code-link-hover-color)}.vp-doc h1>code,.vp-doc h2>code,.vp-doc h3>code{font-size:.9em}.vp-doc div[class*=language-],.vp-block{position:relative;margin:16px -24px;background-color:var(--vp-code-block-bg);overflow-x:auto;transition:background-color .5s}@media (min-width: 640px){.vp-doc div[class*=language-],.vp-block{border-radius:8px;margin:16px 0}}@media (max-width: 639px){.vp-doc li div[class*=language-]{border-radius:8px 0 0 8px}}.vp-doc div[class*=language-]+div[class*=language-],.vp-doc div[class$=-api]+div[class*=language-],.vp-doc div[class*=language-]+div[class$=-api]>div[class*=language-]{margin-top:-8px}.vp-doc [class*=language-] pre,.vp-doc [class*=language-] code{direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}.vp-doc [class*=language-] pre{position:relative;z-index:1;margin:0;padding:20px 0;background:transparent;overflow-x:auto}.vp-doc [class*=language-] code{display:block;padding:0 24px;width:fit-content;min-width:100%;line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-block-color);transition:color .5s}.vp-doc [class*=language-] code .highlighted{background-color:var(--vp-code-line-highlight-color);transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .highlighted.error{background-color:var(--vp-code-line-error-color)}.vp-doc [class*=language-] code .highlighted.warning{background-color:var(--vp-code-line-warning-color)}.vp-doc [class*=language-] code .diff{transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .diff:before{position:absolute;left:10px}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){filter:blur(.095rem);opacity:.4;transition:filter .35s,opacity .35s}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){opacity:.7;transition:filter .35s,opacity .35s}.vp-doc [class*=language-]:hover .has-focused-lines .line:not(.has-focus){filter:blur(0);opacity:1}.vp-doc [class*=language-] code .diff.remove{background-color:var(--vp-code-line-diff-remove-color);opacity:.7}.vp-doc [class*=language-] code .diff.remove:before{content:"-";color:var(--vp-code-line-diff-remove-symbol-color)}.vp-doc [class*=language-] code .diff.add{background-color:var(--vp-code-line-diff-add-color)}.vp-doc [class*=language-] code .diff.add:before{content:"+";color:var(--vp-code-line-diff-add-symbol-color)}.vp-doc div[class*=language-].line-numbers-mode{padding-left:32px}.vp-doc .line-numbers-wrapper{position:absolute;top:0;bottom:0;left:0;z-index:3;border-right:1px solid var(--vp-code-block-divider-color);padding-top:20px;width:32px;text-align:center;font-family:var(--vp-font-family-mono);line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-line-number-color);transition:border-color .5s,color .5s}.vp-doc [class*=language-]>button.copy{direction:ltr;position:absolute;top:12px;right:12px;z-index:3;border:1px solid var(--vp-code-copy-code-border-color);border-radius:4px;width:40px;height:40px;background-color:var(--vp-code-copy-code-bg);opacity:0;cursor:pointer;background-image:var(--vp-icon-copy);background-position:50%;background-size:20px;background-repeat:no-repeat;transition:border-color .25s,background-color .25s,opacity .25s}.vp-doc [class*=language-]:hover>button.copy,.vp-doc [class*=language-]>button.copy:focus{opacity:1}.vp-doc [class*=language-]>button.copy:hover,.vp-doc [class*=language-]>button.copy.copied{border-color:var(--vp-code-copy-code-hover-border-color);background-color:var(--vp-code-copy-code-hover-bg)}.vp-doc [class*=language-]>button.copy.copied,.vp-doc [class*=language-]>button.copy:hover.copied{border-radius:0 4px 4px 0;background-color:var(--vp-code-copy-code-hover-bg);background-image:var(--vp-icon-copied)}.vp-doc [class*=language-]>button.copy.copied:before,.vp-doc [class*=language-]>button.copy:hover.copied:before{position:relative;top:-1px;transform:translate(calc(-100% - 1px));display:flex;justify-content:center;align-items:center;border:1px solid var(--vp-code-copy-code-hover-border-color);border-right:0;border-radius:4px 0 0 4px;padding:0 10px;width:fit-content;height:40px;text-align:center;font-size:12px;font-weight:500;color:var(--vp-code-copy-code-active-text);background-color:var(--vp-code-copy-code-hover-bg);white-space:nowrap;content:var(--vp-code-copy-copied-text-content)}.vp-doc [class*=language-]>span.lang{position:absolute;top:2px;right:8px;z-index:2;font-size:12px;font-weight:500;color:var(--vp-code-lang-color);transition:color .4s,opacity .4s}.vp-doc [class*=language-]:hover>button.copy+span.lang,.vp-doc [class*=language-]>button.copy:focus+span.lang{opacity:0}.vp-doc .VPTeamMembers{margin-top:24px}.vp-doc .VPTeamMembers.small.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}.vp-doc .VPTeamMembers.small.count-2 .container,.vp-doc .VPTeamMembers.small.count-3 .container{max-width:100%!important}.vp-doc .VPTeamMembers.medium.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}:is(.vp-external-link-icon,.vp-doc a[href*="://"],.vp-doc a[target=_blank]):not(.no-icon):after{display:inline-block;margin-top:-1px;margin-left:4px;width:11px;height:11px;background:currentColor;color:var(--vp-c-text-3);flex-shrink:0;--icon: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none' /%3E%3Cpath d='M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z' /%3E%3C/svg%3E");-webkit-mask-image:var(--icon);mask-image:var(--icon)}.vp-external-link-icon:after{content:""}.vp-sponsor{border-radius:16px;overflow:hidden}.vp-sponsor.aside{border-radius:12px}.vp-sponsor-section+.vp-sponsor-section{margin-top:4px}.vp-sponsor-tier{margin-bottom:4px;text-align:center;letter-spacing:1px;line-height:24px;width:100%;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-sponsor.normal .vp-sponsor-tier{padding:13px 0 11px;font-size:14px}.vp-sponsor.aside .vp-sponsor-tier{padding:9px 0 7px;font-size:12px}.vp-sponsor-grid+.vp-sponsor-tier{margin-top:4px}.vp-sponsor-grid{display:flex;flex-wrap:wrap;gap:4px}.vp-sponsor-grid.xmini .vp-sponsor-grid-link{height:64px}.vp-sponsor-grid.xmini .vp-sponsor-grid-image{max-width:64px;max-height:22px}.vp-sponsor-grid.mini .vp-sponsor-grid-link{height:72px}.vp-sponsor-grid.mini .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.small .vp-sponsor-grid-link{height:96px}.vp-sponsor-grid.small .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.medium .vp-sponsor-grid-link{height:112px}.vp-sponsor-grid.medium .vp-sponsor-grid-image{max-width:120px;max-height:36px}.vp-sponsor-grid.big .vp-sponsor-grid-link{height:184px}.vp-sponsor-grid.big .vp-sponsor-grid-image{max-width:192px;max-height:56px}.vp-sponsor-grid[data-vp-grid="2"] .vp-sponsor-grid-item{width:calc((100% - 4px)/2)}.vp-sponsor-grid[data-vp-grid="3"] .vp-sponsor-grid-item{width:calc((100% - 4px * 2) / 3)}.vp-sponsor-grid[data-vp-grid="4"] .vp-sponsor-grid-item{width:calc((100% - 12px)/4)}.vp-sponsor-grid[data-vp-grid="5"] .vp-sponsor-grid-item{width:calc((100% - 16px)/5)}.vp-sponsor-grid[data-vp-grid="6"] .vp-sponsor-grid-item{width:calc((100% - 4px * 5) / 6)}.vp-sponsor-grid-item{flex-shrink:0;width:100%;background-color:var(--vp-c-bg-soft);transition:background-color .25s}.vp-sponsor-grid-item:hover{background-color:var(--vp-c-default-soft)}.vp-sponsor-grid-item:hover .vp-sponsor-grid-image{filter:grayscale(0) invert(0)}.vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.dark .vp-sponsor-grid-item:hover{background-color:var(--vp-c-white)}.dark .vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.vp-sponsor-grid-link{display:flex}.vp-sponsor-grid-box{display:flex;justify-content:center;align-items:center;width:100%}.vp-sponsor-grid-image{max-width:100%;filter:grayscale(1);transition:filter .25s}.dark .vp-sponsor-grid-image{filter:grayscale(1) invert(1)}.VPBadge[data-v-55bb7998]{display:inline-block;margin-left:2px;border:1px solid transparent;border-radius:12px;padding:0 10px;line-height:22px;font-size:12px;font-weight:500;transform:translateY(-2px)}.vp-doc h1>.VPBadge[data-v-55bb7998]{margin-top:4px;vertical-align:top}.vp-doc h2>.VPBadge[data-v-55bb7998]{margin-top:3px;padding:0 8px;vertical-align:top}.vp-doc h3>.VPBadge[data-v-55bb7998]{vertical-align:middle}.vp-doc h4>.VPBadge[data-v-55bb7998],.vp-doc h5>.VPBadge[data-v-55bb7998],.vp-doc h6>.VPBadge[data-v-55bb7998]{vertical-align:middle;line-height:18px}.VPBadge.info[data-v-55bb7998]{border-color:var(--vp-badge-info-border);color:var(--vp-badge-info-text);background-color:var(--vp-badge-info-bg)}.VPBadge.tip[data-v-55bb7998]{border-color:var(--vp-badge-tip-border);color:var(--vp-badge-tip-text);background-color:var(--vp-badge-tip-bg)}.VPBadge.warning[data-v-55bb7998]{border-color:var(--vp-badge-warning-border);color:var(--vp-badge-warning-text);background-color:var(--vp-badge-warning-bg)}.VPBadge.danger[data-v-55bb7998]{border-color:var(--vp-badge-danger-border);color:var(--vp-badge-danger-text);background-color:var(--vp-badge-danger-bg)}.VPBackdrop[data-v-b9dde47a]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--vp-z-index-backdrop);background:var(--vp-backdrop-bg-color);transition:opacity .5s}.VPBackdrop.fade-enter-from[data-v-b9dde47a],.VPBackdrop.fade-leave-to[data-v-b9dde47a]{opacity:0}.VPBackdrop.fade-leave-active[data-v-b9dde47a]{transition-duration:.25s}@media (min-width: 1280px){.VPBackdrop[data-v-b9dde47a]{display:none}}.NotFound[data-v-ef5d1a0f]{padding:64px 24px 96px;text-align:center}@media (min-width: 768px){.NotFound[data-v-ef5d1a0f]{padding:96px 32px 168px}}.code[data-v-ef5d1a0f]{line-height:64px;font-size:64px;font-weight:600}.title[data-v-ef5d1a0f]{padding-top:12px;letter-spacing:2px;line-height:20px;font-size:20px;font-weight:700}.divider[data-v-ef5d1a0f]{margin:24px auto 18px;width:64px;height:1px;background-color:var(--vp-c-divider)}.quote[data-v-ef5d1a0f]{margin:0 auto;max-width:256px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.action[data-v-ef5d1a0f]{padding-top:20px}.link[data-v-ef5d1a0f]{display:inline-block;border:1px solid var(--vp-c-brand-1);border-radius:16px;padding:3px 16px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:border-color .25s,color .25s}.link[data-v-ef5d1a0f]:hover{border-color:var(--vp-c-brand-2);color:var(--vp-c-brand-2)}.root[data-v-095ce067]{position:relative;z-index:1}.nested[data-v-095ce067]{padding-left:16px}.outline-link[data-v-095ce067]{display:block;line-height:28px;color:var(--vp-c-text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .5s;font-weight:400}.outline-link[data-v-095ce067]:hover,.outline-link.active[data-v-095ce067]{color:var(--vp-c-text-1);transition:color .25s}.outline-link.nested[data-v-095ce067]{padding-left:13px}.VPDocAsideOutline[data-v-b6505075]{display:none}.VPDocAsideOutline.has-outline[data-v-b6505075]{display:block}.content[data-v-b6505075]{position:relative;border-left:1px solid var(--vp-c-divider);padding-left:16px;font-size:13px;font-weight:500}.outline-marker[data-v-b6505075]{position:absolute;top:32px;left:-1px;z-index:0;opacity:0;width:2px;border-radius:2px;height:18px;background-color:var(--vp-c-brand-1);transition:top .25s cubic-bezier(0,1,.5,1),background-color .5s,opacity .25s}.outline-title[data-v-b6505075]{letter-spacing:.4px;line-height:28px;font-size:13px;font-weight:600}.VPDocAside[data-v-faa59fde]{display:flex;flex-direction:column;flex-grow:1}.spacer[data-v-faa59fde]{flex-grow:1}.VPDocAside[data-v-faa59fde] .spacer+.VPDocAsideSponsors,.VPDocAside[data-v-faa59fde] .spacer+.VPDocAsideCarbonAds{margin-top:24px}.VPDocAside[data-v-faa59fde] .VPDocAsideSponsors+.VPDocAsideCarbonAds{margin-top:16px}.VPLastUpdated[data-v-50a67d3d]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 640px){.VPLastUpdated[data-v-50a67d3d]{line-height:32px;font-size:14px;font-weight:500}}.VPDocFooter[data-v-c6cf7d08]{margin-top:64px}.edit-info[data-v-c6cf7d08]{padding-bottom:18px}@media (min-width: 640px){.edit-info[data-v-c6cf7d08]{display:flex;justify-content:space-between;align-items:center;padding-bottom:14px}}.edit-link-button[data-v-c6cf7d08]{display:flex;align-items:center;border:0;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:color .25s}.edit-link-button[data-v-c6cf7d08]:hover{color:var(--vp-c-brand-2)}.edit-link-icon[data-v-c6cf7d08]{margin-right:8px;width:14px;height:14px;fill:currentColor}.prev-next[data-v-c6cf7d08]{border-top:1px solid var(--vp-c-divider);padding-top:24px;display:grid;grid-row-gap:8px}@media (min-width: 640px){.prev-next[data-v-c6cf7d08]{grid-template-columns:repeat(2,1fr);grid-column-gap:16px}}.pager-link[data-v-c6cf7d08]{display:block;border:1px solid var(--vp-c-divider);border-radius:8px;padding:11px 16px 13px;width:100%;height:100%;transition:border-color .25s}.pager-link[data-v-c6cf7d08]:hover{border-color:var(--vp-c-brand-1)}.pager-link.next[data-v-c6cf7d08]{margin-left:auto;text-align:right}.desc[data-v-c6cf7d08]{display:block;line-height:20px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.title[data-v-c6cf7d08]{display:block;line-height:20px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:color .25s}.VPDocOutlineDropdown[data-v-1b8a3c67]{margin-bottom:48px}.VPDocOutlineDropdown button[data-v-1b8a3c67]{display:block;font-size:14px;font-weight:500;line-height:24px;border:1px solid var(--vp-c-border);padding:4px 12px;color:var(--vp-c-text-2);background-color:var(--vp-c-default-soft);border-radius:8px;transition:color .5s}.VPDocOutlineDropdown button[data-v-1b8a3c67]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPDocOutlineDropdown button.open[data-v-1b8a3c67]{color:var(--vp-c-text-1)}.icon[data-v-1b8a3c67]{display:inline-block;vertical-align:middle;width:16px;height:16px;fill:currentColor}[data-v-1b8a3c67] .outline-link{font-size:14px;font-weight:400}.open>.icon[data-v-1b8a3c67]{transform:rotate(90deg)}.items[data-v-1b8a3c67]{margin-top:12px;border-left:1px solid var(--vp-c-divider)}.VPDoc[data-v-4c05baee]{padding:32px 24px 96px;width:100%}.VPDoc .VPDocOutlineDropdown[data-v-4c05baee]{display:none}@media (min-width: 960px) and (max-width: 1279px){.VPDoc .VPDocOutlineDropdown[data-v-4c05baee]{display:block}}@media (min-width: 768px){.VPDoc[data-v-4c05baee]{padding:48px 32px 128px}}@media (min-width: 960px){.VPDoc[data-v-4c05baee]{padding:32px 32px 0}.VPDoc:not(.has-sidebar) .container[data-v-4c05baee]{display:flex;justify-content:center;max-width:992px}.VPDoc:not(.has-sidebar) .content[data-v-4c05baee]{max-width:752px}}@media (min-width: 1280px){.VPDoc .container[data-v-4c05baee]{display:flex;justify-content:center}.VPDoc .aside[data-v-4c05baee]{display:block}}@media (min-width: 1440px){.VPDoc:not(.has-sidebar) .content[data-v-4c05baee]{max-width:784px}.VPDoc:not(.has-sidebar) .container[data-v-4c05baee]{max-width:1104px}}.container[data-v-4c05baee]{margin:0 auto;width:100%}.aside[data-v-4c05baee]{position:relative;display:none;order:2;flex-grow:1;padding-left:32px;width:100%;max-width:256px}.left-aside[data-v-4c05baee]{order:1;padding-left:unset;padding-right:32px}.aside-container[data-v-4c05baee]{position:fixed;top:0;padding-top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 32px);width:224px;height:100vh;overflow-x:hidden;overflow-y:auto;scrollbar-width:none}.aside-container[data-v-4c05baee]::-webkit-scrollbar{display:none}.aside-curtain[data-v-4c05baee]{position:fixed;bottom:0;z-index:10;width:224px;height:32px;background:linear-gradient(transparent,var(--vp-c-bg) 70%)}.aside-content[data-v-4c05baee]{display:flex;flex-direction:column;min-height:calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px));padding-bottom:32px}.content[data-v-4c05baee]{position:relative;margin:0 auto;width:100%}@media (min-width: 960px){.content[data-v-4c05baee]{padding:0 32px 128px}}@media (min-width: 1280px){.content[data-v-4c05baee]{order:1;margin:0;min-width:640px}}.content-container[data-v-4c05baee]{margin:0 auto}.VPDoc.has-aside .content-container[data-v-4c05baee]{max-width:688px}.external-link-icon-enabled[data-v-4c05baee] :is(.vp-doc a[href*="://"],.vp-doc a[target=_blank]):after{content:"";color:currentColor}.VPButton[data-v-7f452ae2]{display:inline-block;border:1px solid transparent;text-align:center;font-weight:600;white-space:nowrap;transition:color .25s,border-color .25s,background-color .25s}.VPButton[data-v-7f452ae2]:active{transition:color .1s,border-color .1s,background-color .1s}.VPButton.medium[data-v-7f452ae2]{border-radius:20px;padding:0 20px;line-height:38px;font-size:14px}.VPButton.big[data-v-7f452ae2]{border-radius:24px;padding:0 24px;line-height:46px;font-size:16px}.VPButton.brand[data-v-7f452ae2]{border-color:var(--vp-button-brand-border);color:var(--vp-button-brand-text);background-color:var(--vp-button-brand-bg)}.VPButton.brand[data-v-7f452ae2]:hover{border-color:var(--vp-button-brand-hover-border);color:var(--vp-button-brand-hover-text);background-color:var(--vp-button-brand-hover-bg)}.VPButton.brand[data-v-7f452ae2]:active{border-color:var(--vp-button-brand-active-border);color:var(--vp-button-brand-active-text);background-color:var(--vp-button-brand-active-bg)}.VPButton.alt[data-v-7f452ae2]{border-color:var(--vp-button-alt-border);color:var(--vp-button-alt-text);background-color:var(--vp-button-alt-bg)}.VPButton.alt[data-v-7f452ae2]:hover{border-color:var(--vp-button-alt-hover-border);color:var(--vp-button-alt-hover-text);background-color:var(--vp-button-alt-hover-bg)}.VPButton.alt[data-v-7f452ae2]:active{border-color:var(--vp-button-alt-active-border);color:var(--vp-button-alt-active-text);background-color:var(--vp-button-alt-active-bg)}.VPButton.sponsor[data-v-7f452ae2]{border-color:var(--vp-button-sponsor-border);color:var(--vp-button-sponsor-text);background-color:var(--vp-button-sponsor-bg)}.VPButton.sponsor[data-v-7f452ae2]:hover{border-color:var(--vp-button-sponsor-hover-border);color:var(--vp-button-sponsor-hover-text);background-color:var(--vp-button-sponsor-hover-bg)}.VPButton.sponsor[data-v-7f452ae2]:active{border-color:var(--vp-button-sponsor-active-border);color:var(--vp-button-sponsor-active-text);background-color:var(--vp-button-sponsor-active-bg)}html:not(.dark) .VPImage.dark[data-v-726efd0b]{display:none}.dark .VPImage.light[data-v-726efd0b]{display:none}.VPHero[data-v-620e6b40]{margin-top:calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1);padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px) 24px 48px}@media (min-width: 640px){.VPHero[data-v-620e6b40]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 48px 64px}}@media (min-width: 960px){.VPHero[data-v-620e6b40]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 64px 64px}}.container[data-v-620e6b40]{display:flex;flex-direction:column;margin:0 auto;max-width:1152px}@media (min-width: 960px){.container[data-v-620e6b40]{flex-direction:row}}.main[data-v-620e6b40]{position:relative;z-index:10;order:2;flex-grow:1;flex-shrink:0}.VPHero.has-image .container[data-v-620e6b40]{text-align:center}@media (min-width: 960px){.VPHero.has-image .container[data-v-620e6b40]{text-align:left}}@media (min-width: 960px){.main[data-v-620e6b40]{order:1;width:calc((100% / 3) * 2)}.VPHero.has-image .main[data-v-620e6b40]{max-width:592px}}.name[data-v-620e6b40],.text[data-v-620e6b40]{max-width:392px;letter-spacing:-.4px;line-height:40px;font-size:32px;font-weight:700;white-space:pre-wrap}.VPHero.has-image .name[data-v-620e6b40],.VPHero.has-image .text[data-v-620e6b40]{margin:0 auto}.name[data-v-620e6b40]{color:var(--vp-home-hero-name-color)}.clip[data-v-620e6b40]{background:var(--vp-home-hero-name-background);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:var(--vp-home-hero-name-color)}@media (min-width: 640px){.name[data-v-620e6b40],.text[data-v-620e6b40]{max-width:576px;line-height:56px;font-size:48px}}@media (min-width: 960px){.name[data-v-620e6b40],.text[data-v-620e6b40]{line-height:64px;font-size:56px}.VPHero.has-image .name[data-v-620e6b40],.VPHero.has-image .text[data-v-620e6b40]{margin:0}}.tagline[data-v-620e6b40]{padding-top:8px;max-width:392px;line-height:28px;font-size:18px;font-weight:500;white-space:pre-wrap;color:var(--vp-c-text-2)}.VPHero.has-image .tagline[data-v-620e6b40]{margin:0 auto}@media (min-width: 640px){.tagline[data-v-620e6b40]{padding-top:12px;max-width:576px;line-height:32px;font-size:20px}}@media (min-width: 960px){.tagline[data-v-620e6b40]{line-height:36px;font-size:24px}.VPHero.has-image .tagline[data-v-620e6b40]{margin:0}}.actions[data-v-620e6b40]{display:flex;flex-wrap:wrap;margin:-6px;padding-top:24px}.VPHero.has-image .actions[data-v-620e6b40]{justify-content:center}@media (min-width: 640px){.actions[data-v-620e6b40]{padding-top:32px}}@media (min-width: 960px){.VPHero.has-image .actions[data-v-620e6b40]{justify-content:flex-start}}.action[data-v-620e6b40]{flex-shrink:0;padding:6px}.image[data-v-620e6b40]{order:1;margin:-76px -24px -48px}@media (min-width: 640px){.image[data-v-620e6b40]{margin:-108px -24px -48px}}@media (min-width: 960px){.image[data-v-620e6b40]{flex-grow:1;order:2;margin:0;min-height:100%}}.image-container[data-v-620e6b40]{position:relative;margin:0 auto;width:320px;height:320px}@media (min-width: 640px){.image-container[data-v-620e6b40]{width:392px;height:392px}}@media (min-width: 960px){.image-container[data-v-620e6b40]{display:flex;justify-content:center;align-items:center;width:100%;height:100%;transform:translate(-32px,-32px)}}.image-bg[data-v-620e6b40]{position:absolute;top:50%;left:50%;border-radius:50%;width:192px;height:192px;background-image:var(--vp-home-hero-image-background-image);filter:var(--vp-home-hero-image-filter);transform:translate(-50%,-50%)}@media (min-width: 640px){.image-bg[data-v-620e6b40]{width:256px;height:256px}}@media (min-width: 960px){.image-bg[data-v-620e6b40]{width:320px;height:320px}}[data-v-620e6b40] .image-src{position:absolute;top:50%;left:50%;max-width:192px;max-height:192px;transform:translate(-50%,-50%)}@media (min-width: 640px){[data-v-620e6b40] .image-src{max-width:256px;max-height:256px}}@media (min-width: 960px){[data-v-620e6b40] .image-src{max-width:320px;max-height:320px}}.VPFeature[data-v-ba9ef0bd]{display:block;border:1px solid var(--vp-c-bg-soft);border-radius:12px;height:100%;background-color:var(--vp-c-bg-soft);transition:border-color .25s,background-color .25s}.VPFeature.link[data-v-ba9ef0bd]:hover{border-color:var(--vp-c-brand-1)}.box[data-v-ba9ef0bd]{display:flex;flex-direction:column;padding:24px;height:100%}.box[data-v-ba9ef0bd]>.VPImage{margin-bottom:20px}.icon[data-v-ba9ef0bd]{display:flex;justify-content:center;align-items:center;margin-bottom:20px;border-radius:6px;background-color:var(--vp-c-default-soft);width:48px;height:48px;font-size:24px;transition:background-color .25s}.title[data-v-ba9ef0bd]{line-height:24px;font-size:16px;font-weight:600}.details[data-v-ba9ef0bd]{flex-grow:1;padding-top:8px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.link-text[data-v-ba9ef0bd]{padding-top:8px}.link-text-value[data-v-ba9ef0bd]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-brand-1)}.link-text-icon[data-v-ba9ef0bd]{display:inline-block;margin-left:6px;width:14px;height:14px;fill:currentColor}.VPFeatures[data-v-2f5ee4d1]{position:relative;padding:0 24px}@media (min-width: 640px){.VPFeatures[data-v-2f5ee4d1]{padding:0 48px}}@media (min-width: 960px){.VPFeatures[data-v-2f5ee4d1]{padding:0 64px}}.container[data-v-2f5ee4d1]{margin:0 auto;max-width:1152px}.items[data-v-2f5ee4d1]{display:flex;flex-wrap:wrap;margin:-8px}.item[data-v-2f5ee4d1]{padding:8px;width:100%}@media (min-width: 640px){.item.grid-2[data-v-2f5ee4d1],.item.grid-4[data-v-2f5ee4d1],.item.grid-6[data-v-2f5ee4d1]{width:50%}}@media (min-width: 768px){.item.grid-2[data-v-2f5ee4d1],.item.grid-4[data-v-2f5ee4d1]{width:50%}.item.grid-3[data-v-2f5ee4d1],.item.grid-6[data-v-2f5ee4d1]{width:calc(100% / 3)}}@media (min-width: 960px){.item.grid-4[data-v-2f5ee4d1]{width:25%}}.VPHome[data-v-3e3c67d4]{padding-bottom:96px}.VPHome[data-v-3e3c67d4] .VPHomeSponsors{margin-top:112px;margin-bottom:-128px}@media (min-width: 768px){.VPHome[data-v-3e3c67d4]{padding-bottom:128px}}.VPContent[data-v-a0e631d0]{flex-grow:1;flex-shrink:0;margin:var(--vp-layout-top-height, 0px) auto 0;width:100%}.VPContent.is-home[data-v-a0e631d0]{width:100%;max-width:100%}.VPContent.has-sidebar[data-v-a0e631d0]{margin:0}@media (min-width: 960px){.VPContent[data-v-a0e631d0]{padding-top:var(--vp-nav-height)}.VPContent.has-sidebar[data-v-a0e631d0]{margin:var(--vp-layout-top-height, 0px) 0 0;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPContent.has-sidebar[data-v-a0e631d0]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.VPFooter[data-v-8ea47eff]{position:relative;z-index:var(--vp-z-index-footer);border-top:1px solid var(--vp-c-gutter);padding:32px 24px;background-color:var(--vp-c-bg)}.VPFooter.has-sidebar[data-v-8ea47eff]{display:none}@media (min-width: 768px){.VPFooter[data-v-8ea47eff]{padding:32px}}.container[data-v-8ea47eff]{margin:0 auto;max-width:var(--vp-layout-max-width);text-align:center}.message[data-v-8ea47eff],.copyright[data-v-8ea47eff]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.VPLocalNavOutlineDropdown[data-v-86af0a73]{padding:12px 20px 11px}.VPLocalNavOutlineDropdown button[data-v-86af0a73]{display:block;font-size:12px;font-weight:500;line-height:24px;color:var(--vp-c-text-2);transition:color .5s;position:relative}.VPLocalNavOutlineDropdown button[data-v-86af0a73]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPLocalNavOutlineDropdown button.open[data-v-86af0a73]{color:var(--vp-c-text-1)}.icon[data-v-86af0a73]{display:inline-block;vertical-align:middle;margin-left:2px;width:14px;height:14px;fill:currentColor}[data-v-86af0a73] .outline-link{font-size:14px;padding:2px 0}.open>.icon[data-v-86af0a73]{transform:rotate(90deg)}.items[data-v-86af0a73]{position:absolute;top:64px;right:16px;left:16px;display:grid;gap:1px;border:1px solid var(--vp-c-border);border-radius:8px;background-color:var(--vp-c-gutter);max-height:calc(var(--vp-vh, 100vh) - 86px);overflow:hidden auto;box-shadow:var(--vp-shadow-3)}.header[data-v-86af0a73]{background-color:var(--vp-c-bg-soft)}.top-link[data-v-86af0a73]{display:block;padding:0 16px;line-height:48px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1)}.outline[data-v-86af0a73]{padding:8px 0;background-color:var(--vp-c-bg-soft)}.flyout-enter-active[data-v-86af0a73]{transition:all .2s ease-out}.flyout-leave-active[data-v-86af0a73]{transition:all .15s ease-in}.flyout-enter-from[data-v-86af0a73],.flyout-leave-to[data-v-86af0a73]{opacity:0;transform:translateY(-16px)}.VPLocalNav[data-v-e86af244]{position:sticky;top:0;left:0;z-index:var(--vp-z-index-local-nav);display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--vp-c-gutter);border-bottom:1px solid var(--vp-c-gutter);padding-top:var(--vp-layout-top-height, 0px);width:100%;background-color:var(--vp-local-nav-bg-color)}.VPLocalNav.fixed[data-v-e86af244]{position:fixed}.VPLocalNav.reached-top[data-v-e86af244]{border-top-color:transparent}@media (min-width: 960px){.VPLocalNav[data-v-e86af244]{display:none}}.menu[data-v-e86af244]{display:flex;align-items:center;padding:12px 24px 11px;line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.menu[data-v-e86af244]:hover{color:var(--vp-c-text-1);transition:color .25s}@media (min-width: 768px){.menu[data-v-e86af244]{padding:0 32px}}.menu-icon[data-v-e86af244]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPOutlineDropdown[data-v-e86af244]{padding:12px 24px 11px}@media (min-width: 768px){.VPOutlineDropdown[data-v-e86af244]{padding:12px 32px 11px}}.VPSwitch[data-v-70543c77]{position:relative;border-radius:11px;display:block;width:40px;height:22px;flex-shrink:0;border:1px solid var(--vp-input-border-color);background-color:var(--vp-input-switch-bg-color);transition:border-color .25s!important}.VPSwitch[data-v-70543c77]:hover{border-color:var(--vp-c-brand-1)}.check[data-v-70543c77]{position:absolute;top:1px;left:1px;width:18px;height:18px;border-radius:50%;background-color:var(--vp-c-neutral-inverse);box-shadow:var(--vp-shadow-1);transition:transform .25s!important}.icon[data-v-70543c77]{position:relative;display:block;width:18px;height:18px;border-radius:50%;overflow:hidden}.icon[data-v-70543c77] svg{position:absolute;top:3px;left:3px;width:12px;height:12px;fill:var(--vp-c-text-2)}.dark .icon[data-v-70543c77] svg{fill:var(--vp-c-text-1);transition:opacity .25s!important}.sun[data-v-9e8fb728]{opacity:1}.moon[data-v-9e8fb728],.dark .sun[data-v-9e8fb728]{opacity:0}.dark .moon[data-v-9e8fb728]{opacity:1}.dark .VPSwitchAppearance[data-v-9e8fb728] .check{transform:translate(18px)}.VPNavBarAppearance[data-v-8024a3fd]{display:none}@media (min-width: 1280px){.VPNavBarAppearance[data-v-8024a3fd]{display:flex;align-items:center}}.VPMenuGroup+.VPMenuLink[data-v-567fbcba]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.link[data-v-567fbcba]{display:block;border-radius:6px;padding:0 12px;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);white-space:nowrap;transition:background-color .25s,color .25s}.link[data-v-567fbcba]:hover{color:var(--vp-c-brand-1);background-color:var(--vp-c-default-soft)}.link.active[data-v-567fbcba]{color:var(--vp-c-brand-1)}.VPMenuGroup[data-v-b0ffa15d]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.VPMenuGroup[data-v-b0ffa15d]:first-child{margin-top:0;border-top:0;padding-top:0}.VPMenuGroup+.VPMenuGroup[data-v-b0ffa15d]{margin-top:12px;border-top:1px solid var(--vp-c-divider)}.title[data-v-b0ffa15d]{padding:0 12px;line-height:32px;font-size:14px;font-weight:600;color:var(--vp-c-text-2);white-space:nowrap;transition:color .25s}.VPMenu[data-v-923db0f3]{border-radius:12px;padding:12px;min-width:128px;border:1px solid var(--vp-c-divider);background-color:var(--vp-c-bg-elv);box-shadow:var(--vp-shadow-3);transition:background-color .5s;max-height:calc(100vh - var(--vp-nav-height));overflow-y:auto}.VPMenu[data-v-923db0f3] .group{margin:0 -12px;padding:0 12px 12px}.VPMenu[data-v-923db0f3] .group+.group{border-top:1px solid var(--vp-c-divider);padding:11px 12px 12px}.VPMenu[data-v-923db0f3] .group:last-child{padding-bottom:0}.VPMenu[data-v-923db0f3] .group+.item{border-top:1px solid var(--vp-c-divider);padding:11px 16px 0}.VPMenu[data-v-923db0f3] .item{padding:0 16px;white-space:nowrap}.VPMenu[data-v-923db0f3] .label{flex-grow:1;line-height:28px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.VPMenu[data-v-923db0f3] .action{padding-left:24px}.VPFlyout[data-v-8377b1cc]{position:relative}.VPFlyout[data-v-8377b1cc]:hover{color:var(--vp-c-brand-1);transition:color .25s}.VPFlyout:hover .text[data-v-8377b1cc]{color:var(--vp-c-text-2)}.VPFlyout:hover .icon[data-v-8377b1cc]{fill:var(--vp-c-text-2)}.VPFlyout.active .text[data-v-8377b1cc]{color:var(--vp-c-brand-1)}.VPFlyout.active:hover .text[data-v-8377b1cc]{color:var(--vp-c-brand-2)}.VPFlyout:hover .menu[data-v-8377b1cc],.button[aria-expanded=true]+.menu[data-v-8377b1cc]{opacity:1;visibility:visible;transform:translateY(0)}.button[aria-expanded=false]+.menu[data-v-8377b1cc]{opacity:0;visibility:hidden;transform:translateY(0)}.button[data-v-8377b1cc]{display:flex;align-items:center;padding:0 12px;height:var(--vp-nav-height);color:var(--vp-c-text-1);transition:color .5s}.text[data-v-8377b1cc]{display:flex;align-items:center;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.option-icon[data-v-8377b1cc]{margin-right:0;width:16px;height:16px;fill:currentColor}.text-icon[data-v-8377b1cc]{margin-left:4px;width:14px;height:14px;fill:currentColor}.icon[data-v-8377b1cc]{width:20px;height:20px;fill:currentColor;transition:fill .25s}.menu[data-v-8377b1cc]{position:absolute;top:calc(var(--vp-nav-height) / 2 + 20px);right:0;opacity:0;visibility:hidden;transition:opacity .25s,visibility .25s,transform .25s}.VPSocialLink[data-v-5c356b5c]{display:flex;justify-content:center;align-items:center;width:36px;height:36px;color:var(--vp-c-text-2);transition:color .5s}.VPSocialLink[data-v-5c356b5c]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPSocialLink[data-v-5c356b5c]>svg{width:20px;height:20px;fill:currentColor}.VPSocialLinks[data-v-ba919e0f]{display:flex;justify-content:center}.VPNavBarExtra[data-v-1425926a]{display:none;margin-right:-12px}@media (min-width: 768px){.VPNavBarExtra[data-v-1425926a]{display:block}}@media (min-width: 1280px){.VPNavBarExtra[data-v-1425926a]{display:none}}.trans-title[data-v-1425926a]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.item.appearance[data-v-1425926a],.item.social-links[data-v-1425926a]{display:flex;align-items:center;padding:0 12px}.item.appearance[data-v-1425926a]{min-width:176px}.appearance-action[data-v-1425926a]{margin-right:-2px}.social-links-list[data-v-1425926a]{margin:-4px -8px}.VPNavBarHamburger[data-v-b37633d2]{display:flex;justify-content:center;align-items:center;width:48px;height:var(--vp-nav-height)}@media (min-width: 768px){.VPNavBarHamburger[data-v-b37633d2]{display:none}}.container[data-v-b37633d2]{position:relative;width:16px;height:14px;overflow:hidden}.VPNavBarHamburger:hover .top[data-v-b37633d2]{top:0;left:0;transform:translate(4px)}.VPNavBarHamburger:hover .middle[data-v-b37633d2]{top:6px;left:0;transform:translate(0)}.VPNavBarHamburger:hover .bottom[data-v-b37633d2]{top:12px;left:0;transform:translate(8px)}.VPNavBarHamburger.active .top[data-v-b37633d2]{top:6px;transform:translate(0) rotate(225deg)}.VPNavBarHamburger.active .middle[data-v-b37633d2]{top:6px;transform:translate(16px)}.VPNavBarHamburger.active .bottom[data-v-b37633d2]{top:6px;transform:translate(0) rotate(135deg)}.VPNavBarHamburger.active:hover .top[data-v-b37633d2],.VPNavBarHamburger.active:hover .middle[data-v-b37633d2],.VPNavBarHamburger.active:hover .bottom[data-v-b37633d2]{background-color:var(--vp-c-text-2);transition:top .25s,background-color .25s,transform .25s}.top[data-v-b37633d2],.middle[data-v-b37633d2],.bottom[data-v-b37633d2]{position:absolute;width:16px;height:2px;background-color:var(--vp-c-text-1);transition:top .25s,background-color .5s,transform .25s}.top[data-v-b37633d2]{top:0;left:0;transform:translate(0)}.middle[data-v-b37633d2]{top:6px;left:0;transform:translate(8px)}.bottom[data-v-b37633d2]{top:12px;left:0;transform:translate(4px)}.VPNavBarMenuLink[data-v-b0bf7172]{display:flex;align-items:center;padding:0 12px;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.VPNavBarMenuLink.active[data-v-b0bf7172],.VPNavBarMenuLink[data-v-b0bf7172]:hover{color:var(--vp-c-brand-1)}.VPNavBarMenu[data-v-4907edd6]{display:none}@media (min-width: 768px){.VPNavBarMenu[data-v-4907edd6]{display:flex}}/*! @docsearch/css 3.5.2 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */:root{--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12)}html[data-theme=dark]{--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}.DocSearch-Button{align-items:center;background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;display:flex;font-weight:500;height:36px;justify-content:space-between;margin:0 0 0 16px;padding:0 8px;-webkit-user-select:none;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:none}.DocSearch-Button-Container{align-items:center;display:flex}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;position:relative;padding:0 0 2px;border:0;top:-1px;width:20px}@media (max-width:768px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder{display:none}}.DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;-webkit-user-select:none;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;-webkit-user-select:none;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;color:var(--docsearch-muted-color);border:0;width:20px}@media (max-width:768px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;-webkit-user-select:none;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}[class*=DocSearch]{--docsearch-primary-color: var(--vp-c-brand-1);--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-text-color: var(--vp-c-text-1);--docsearch-muted-color: var(--vp-c-text-2);--docsearch-searchbox-shadow: none;--docsearch-searchbox-background: transparent;--docsearch-searchbox-focus-background: transparent;--docsearch-key-gradient: transparent;--docsearch-key-shadow: none;--docsearch-modal-background: var(--vp-c-bg-soft);--docsearch-footer-background: var(--vp-c-bg)}.dark [class*=DocSearch]{--docsearch-modal-shadow: none;--docsearch-footer-shadow: none;--docsearch-logo-color: var(--vp-c-text-2);--docsearch-hit-background: var(--vp-c-default-soft);--docsearch-hit-color: var(--vp-c-text-2);--docsearch-hit-shadow: none}.DocSearch-Button{display:flex;justify-content:center;align-items:center;margin:0;padding:0;width:48px;height:55px;background:transparent;transition:border-color .25s}.DocSearch-Button:hover{background:transparent}.DocSearch-Button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.DocSearch-Button:focus:not(:focus-visible){outline:none!important}@media (min-width: 768px){.DocSearch-Button{justify-content:flex-start;border:1px solid transparent;border-radius:8px;padding:0 10px 0 12px;width:100%;height:40px;background-color:var(--vp-c-bg-alt)}.DocSearch-Button:hover{border-color:var(--vp-c-brand-1);background:var(--vp-c-bg-alt)}}.DocSearch-Button .DocSearch-Button-Container{display:flex;align-items:center}.DocSearch-Button .DocSearch-Search-Icon{position:relative;width:16px;height:16px;color:var(--vp-c-text-1);fill:currentColor;transition:color .5s}.DocSearch-Button:hover .DocSearch-Search-Icon{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Search-Icon{top:1px;margin-right:8px;width:14px;height:14px;color:var(--vp-c-text-2)}}.DocSearch-Button .DocSearch-Button-Placeholder{display:none;margin-top:2px;padding:0 16px 0 0;font-size:13px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.DocSearch-Button:hover .DocSearch-Button-Placeholder{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Placeholder{display:inline-block}}.DocSearch-Button .DocSearch-Button-Keys{direction:ltr;display:none;min-width:auto}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Keys{display:flex;align-items:center}}.DocSearch-Button .DocSearch-Button-Key{display:block;margin:2px 0 0;border:1px solid var(--vp-c-divider);border-right:none;border-radius:4px 0 0 4px;padding-left:6px;min-width:0;width:auto;height:22px;line-height:22px;font-family:var(--vp-font-family-base);font-size:12px;font-weight:500;transition:color .5s,border-color .5s}.DocSearch-Button .DocSearch-Button-Key+.DocSearch-Button-Key{border-right:1px solid var(--vp-c-divider);border-left:none;border-radius:0 4px 4px 0;padding-left:2px;padding-right:6px}.DocSearch-Button .DocSearch-Button-Key:first-child{font-size:0!important}.DocSearch-Button .DocSearch-Button-Key:first-child:after{content:"Ctrl";font-size:12px;letter-spacing:normal;color:var(--docsearch-muted-color)}.mac .DocSearch-Button .DocSearch-Button-Key:first-child:after{content:"⌘"}.DocSearch-Button .DocSearch-Button-Key:first-child>*{display:none}.VPNavBarSearch{display:flex;align-items:center}@media (min-width: 768px){.VPNavBarSearch{flex-grow:1;padding-left:24px}}@media (min-width: 960px){.VPNavBarSearch{padding-left:32px}}.dark .DocSearch-Footer{border-top:1px solid var(--vp-c-divider)}.DocSearch-Form{border:1px solid var(--vp-c-brand-1);background-color:var(--vp-c-white)}.dark .DocSearch-Form{background-color:var(--vp-c-default-soft)}.DocSearch-Screen-Icon>svg{margin:auto}.VPNavBarSocialLinks[data-v-24391483]{display:none}@media (min-width: 1280px){.VPNavBarSocialLinks[data-v-24391483]{display:flex;align-items:center}}.title[data-v-a332c19e]{display:flex;align-items:center;border-bottom:1px solid transparent;width:100%;height:var(--vp-nav-height);font-size:16px;font-weight:600;color:var(--vp-c-text-1);transition:opacity .25s}@media (min-width: 960px){.title[data-v-a332c19e]{flex-shrink:0}.VPNavBarTitle.has-sidebar .title[data-v-a332c19e]{border-bottom-color:var(--vp-c-divider)}}[data-v-a332c19e] .logo{margin-right:8px;height:var(--vp-nav-logo-height)}.VPNavBarTranslations[data-v-c9642a5b]{display:none}@media (min-width: 1280px){.VPNavBarTranslations[data-v-c9642a5b]{display:flex;align-items:center}}.title[data-v-c9642a5b]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.VPNavBar[data-v-37ae32c0]{position:relative;border-bottom:1px solid transparent;padding:0 8px 0 24px;height:var(--vp-nav-height);pointer-events:none;white-space:nowrap}@media (min-width: 768px){.VPNavBar[data-v-37ae32c0]{padding:0 32px}}@media (min-width: 960px){.VPNavBar.has-sidebar[data-v-37ae32c0]{padding:0}.VPNavBar[data-v-37ae32c0]:not(.has-sidebar):not(.top){border-bottom-color:var(--vp-c-gutter);background-color:var(--vp-nav-bg-color)}}.container[data-v-37ae32c0]{display:flex;justify-content:space-between;margin:0 auto;max-width:calc(var(--vp-layout-max-width) - 64px);height:var(--vp-nav-height);pointer-events:none}.container>.title[data-v-37ae32c0],.container>.content[data-v-37ae32c0]{pointer-events:none}.container[data-v-37ae32c0] *{pointer-events:auto}@media (min-width: 960px){.VPNavBar.has-sidebar .container[data-v-37ae32c0]{max-width:100%}}.title[data-v-37ae32c0]{flex-shrink:0;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar.has-sidebar .title[data-v-37ae32c0]{position:absolute;top:0;left:0;z-index:2;padding:0 32px;width:var(--vp-sidebar-width);height:var(--vp-nav-height);background-color:transparent}}@media (min-width: 1440px){.VPNavBar.has-sidebar .title[data-v-37ae32c0]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}.content[data-v-37ae32c0]{flex-grow:1}@media (min-width: 960px){.VPNavBar.has-sidebar .content[data-v-37ae32c0]{position:relative;z-index:1;padding-right:32px;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPNavBar.has-sidebar .content[data-v-37ae32c0]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2 + 32px);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.content-body[data-v-37ae32c0]{display:flex;justify-content:flex-end;align-items:center;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar:not(.top) .content-body[data-v-37ae32c0]{position:relative;background-color:var(--vp-nav-bg-color)}}@media (max-width: 767px){.content-body[data-v-37ae32c0]{column-gap:.5rem}}.menu+.translations[data-v-37ae32c0]:before,.menu+.appearance[data-v-37ae32c0]:before,.menu+.social-links[data-v-37ae32c0]:before,.translations+.appearance[data-v-37ae32c0]:before,.appearance+.social-links[data-v-37ae32c0]:before{margin-right:8px;margin-left:8px;width:1px;height:24px;background-color:var(--vp-c-divider);content:""}.menu+.appearance[data-v-37ae32c0]:before,.translations+.appearance[data-v-37ae32c0]:before{margin-right:16px}.appearance+.social-links[data-v-37ae32c0]:before{margin-left:16px}.social-links[data-v-37ae32c0]{margin-right:-8px}@media (min-width: 960px){.VPNavBar.has-sidebar .curtain[data-v-37ae32c0]{position:absolute;right:0;bottom:-31px;width:calc(100% - var(--vp-sidebar-width));height:32px}.VPNavBar.has-sidebar .curtain[data-v-37ae32c0]:before{display:block;width:100%;height:32px;background:linear-gradient(var(--vp-c-bg),transparent 70%);content:""}}@media (min-width: 1440px){.VPNavBar.has-sidebar .curtain[data-v-37ae32c0]{width:calc(100% - ((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)))}}.VPNavScreenAppearance[data-v-5e6cad11]{display:flex;justify-content:space-between;align-items:center;border-radius:8px;padding:12px 14px 12px 16px;background-color:var(--vp-c-bg-soft)}.text[data-v-5e6cad11]{line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.VPNavScreenMenuLink[data-v-ba033da0]{display:block;border-bottom:1px solid var(--vp-c-divider);padding:12px 0 11px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:border-color .25s,color .25s}.VPNavScreenMenuLink[data-v-ba033da0]:hover{color:var(--vp-c-brand-1)}.VPNavScreenMenuGroupLink[data-v-eaf005d4]{display:block;margin-left:12px;line-height:32px;font-size:14px;font-weight:400;color:var(--vp-c-text-1);transition:color .25s}.VPNavScreenMenuGroupLink[data-v-eaf005d4]:hover{color:var(--vp-c-brand-1)}.VPNavScreenMenuGroupSection[data-v-a8341ee1]{display:block}.title[data-v-a8341ee1]{line-height:32px;font-size:13px;font-weight:700;color:var(--vp-c-text-2);transition:color .25s}.VPNavScreenMenuGroup[data-v-7cc3174b]{border-bottom:1px solid var(--vp-c-divider);height:48px;overflow:hidden;transition:border-color .5s}.VPNavScreenMenuGroup .items[data-v-7cc3174b]{visibility:hidden}.VPNavScreenMenuGroup.open .items[data-v-7cc3174b]{visibility:visible}.VPNavScreenMenuGroup.open[data-v-7cc3174b]{padding-bottom:10px;height:auto}.VPNavScreenMenuGroup.open .button[data-v-7cc3174b]{padding-bottom:6px;color:var(--vp-c-brand-1)}.VPNavScreenMenuGroup.open .button-icon[data-v-7cc3174b]{transform:rotate(45deg)}.button[data-v-7cc3174b]{display:flex;justify-content:space-between;align-items:center;padding:12px 4px 11px 0;width:100%;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.button[data-v-7cc3174b]:hover{color:var(--vp-c-brand-1)}.button-icon[data-v-7cc3174b]{width:14px;height:14px;fill:var(--vp-c-text-2);transition:fill .5s,transform .25s}.group[data-v-7cc3174b]:first-child{padding-top:0}.group+.group[data-v-7cc3174b],.group+.item[data-v-7cc3174b]{padding-top:4px}.VPNavScreenTranslations[data-v-60f40e20]{height:24px;overflow:hidden}.VPNavScreenTranslations.open[data-v-60f40e20]{height:auto}.title[data-v-60f40e20]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-text-1)}.icon[data-v-60f40e20]{width:16px;height:16px;fill:currentColor}.icon.lang[data-v-60f40e20]{margin-right:8px}.icon.chevron[data-v-60f40e20]{margin-left:4px}.list[data-v-60f40e20]{padding:4px 0 0 24px}.link[data-v-60f40e20]{line-height:32px;font-size:13px;color:var(--vp-c-text-1)}.VPNavScreen[data-v-c0c39f05]{position:fixed;top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 1px);right:0;bottom:0;left:0;padding:0 32px;width:100%;background-color:var(--vp-nav-screen-bg-color);overflow-y:auto;transition:background-color .5s;pointer-events:auto}.VPNavScreen.fade-enter-active[data-v-c0c39f05],.VPNavScreen.fade-leave-active[data-v-c0c39f05]{transition:opacity .25s}.VPNavScreen.fade-enter-active .container[data-v-c0c39f05],.VPNavScreen.fade-leave-active .container[data-v-c0c39f05]{transition:transform .25s ease}.VPNavScreen.fade-enter-from[data-v-c0c39f05],.VPNavScreen.fade-leave-to[data-v-c0c39f05]{opacity:0}.VPNavScreen.fade-enter-from .container[data-v-c0c39f05],.VPNavScreen.fade-leave-to .container[data-v-c0c39f05]{transform:translateY(-8px)}@media (min-width: 768px){.VPNavScreen[data-v-c0c39f05]{display:none}}.container[data-v-c0c39f05]{margin:0 auto;padding:24px 0 96px;max-width:288px}.menu+.translations[data-v-c0c39f05],.menu+.appearance[data-v-c0c39f05],.translations+.appearance[data-v-c0c39f05]{margin-top:24px}.menu+.social-links[data-v-c0c39f05]{margin-top:16px}.appearance+.social-links[data-v-c0c39f05]{margin-top:16px}.VPNav[data-v-ddf930b5]{position:relative;top:var(--vp-layout-top-height, 0px);left:0;z-index:var(--vp-z-index-nav);width:100%;pointer-events:none;transition:background-color .5s}@media (min-width: 960px){.VPNav[data-v-ddf930b5]{position:fixed}}.VPSidebarItem.level-0[data-v-9e3d5071]{padding-bottom:24px}.VPSidebarItem.collapsed.level-0[data-v-9e3d5071]{padding-bottom:10px}.item[data-v-9e3d5071]{position:relative;display:flex;width:100%}.VPSidebarItem.collapsible>.item[data-v-9e3d5071]{cursor:pointer}.indicator[data-v-9e3d5071]{position:absolute;top:6px;bottom:6px;left:-17px;width:2px;border-radius:2px;transition:background-color .25s}.VPSidebarItem.level-2.is-active>.item>.indicator[data-v-9e3d5071],.VPSidebarItem.level-3.is-active>.item>.indicator[data-v-9e3d5071],.VPSidebarItem.level-4.is-active>.item>.indicator[data-v-9e3d5071],.VPSidebarItem.level-5.is-active>.item>.indicator[data-v-9e3d5071]{background-color:var(--vp-c-brand-1)}.link[data-v-9e3d5071]{display:flex;align-items:center;flex-grow:1}.text[data-v-9e3d5071]{flex-grow:1;padding:4px 0;line-height:24px;font-size:14px;transition:color .25s}.VPSidebarItem.level-0 .text[data-v-9e3d5071]{font-weight:700;color:var(--vp-c-text-1)}.VPSidebarItem.level-1 .text[data-v-9e3d5071],.VPSidebarItem.level-2 .text[data-v-9e3d5071],.VPSidebarItem.level-3 .text[data-v-9e3d5071],.VPSidebarItem.level-4 .text[data-v-9e3d5071],.VPSidebarItem.level-5 .text[data-v-9e3d5071]{font-weight:500;color:var(--vp-c-text-2)}.VPSidebarItem.level-0.is-link>.item>.link:hover .text[data-v-9e3d5071],.VPSidebarItem.level-1.is-link>.item>.link:hover .text[data-v-9e3d5071],.VPSidebarItem.level-2.is-link>.item>.link:hover .text[data-v-9e3d5071],.VPSidebarItem.level-3.is-link>.item>.link:hover .text[data-v-9e3d5071],.VPSidebarItem.level-4.is-link>.item>.link:hover .text[data-v-9e3d5071],.VPSidebarItem.level-5.is-link>.item>.link:hover .text[data-v-9e3d5071]{color:var(--vp-c-brand-1)}.VPSidebarItem.level-0.has-active>.item>.text[data-v-9e3d5071],.VPSidebarItem.level-1.has-active>.item>.text[data-v-9e3d5071],.VPSidebarItem.level-2.has-active>.item>.text[data-v-9e3d5071],.VPSidebarItem.level-3.has-active>.item>.text[data-v-9e3d5071],.VPSidebarItem.level-4.has-active>.item>.text[data-v-9e3d5071],.VPSidebarItem.level-5.has-active>.item>.text[data-v-9e3d5071],.VPSidebarItem.level-0.has-active>.item>.link>.text[data-v-9e3d5071],.VPSidebarItem.level-1.has-active>.item>.link>.text[data-v-9e3d5071],.VPSidebarItem.level-2.has-active>.item>.link>.text[data-v-9e3d5071],.VPSidebarItem.level-3.has-active>.item>.link>.text[data-v-9e3d5071],.VPSidebarItem.level-4.has-active>.item>.link>.text[data-v-9e3d5071],.VPSidebarItem.level-5.has-active>.item>.link>.text[data-v-9e3d5071]{color:var(--vp-c-text-1)}.VPSidebarItem.level-0.is-active>.item .link>.text[data-v-9e3d5071],.VPSidebarItem.level-1.is-active>.item .link>.text[data-v-9e3d5071],.VPSidebarItem.level-2.is-active>.item .link>.text[data-v-9e3d5071],.VPSidebarItem.level-3.is-active>.item .link>.text[data-v-9e3d5071],.VPSidebarItem.level-4.is-active>.item .link>.text[data-v-9e3d5071],.VPSidebarItem.level-5.is-active>.item .link>.text[data-v-9e3d5071]{color:var(--vp-c-brand-1)}.caret[data-v-9e3d5071]{display:flex;justify-content:center;align-items:center;margin-right:-7px;width:32px;height:32px;color:var(--vp-c-text-3);cursor:pointer;transition:color .25s;flex-shrink:0}.item:hover .caret[data-v-9e3d5071]{color:var(--vp-c-text-2)}.item:hover .caret[data-v-9e3d5071]:hover{color:var(--vp-c-text-1)}.caret-icon[data-v-9e3d5071]{width:18px;height:18px;fill:currentColor;transform:rotate(90deg);transition:transform .25s}.VPSidebarItem.collapsed .caret-icon[data-v-9e3d5071]{transform:rotate(0)}.VPSidebarItem.level-1 .items[data-v-9e3d5071],.VPSidebarItem.level-2 .items[data-v-9e3d5071],.VPSidebarItem.level-3 .items[data-v-9e3d5071],.VPSidebarItem.level-4 .items[data-v-9e3d5071],.VPSidebarItem.level-5 .items[data-v-9e3d5071]{border-left:1px solid var(--vp-c-divider);padding-left:16px}.VPSidebarItem.collapsed .items[data-v-9e3d5071]{display:none}.VPSidebar[data-v-727d01b3]{position:fixed;top:var(--vp-layout-top-height, 0px);bottom:0;left:0;z-index:var(--vp-z-index-sidebar);padding:32px 32px 96px;width:calc(100vw - 64px);max-width:320px;background-color:var(--vp-sidebar-bg-color);opacity:0;box-shadow:var(--vp-c-shadow-3);overflow-x:hidden;overflow-y:auto;transform:translate(-100%);transition:opacity .5s,transform .25s ease;overscroll-behavior:contain}.VPSidebar.open[data-v-727d01b3]{opacity:1;visibility:visible;transform:translate(0);transition:opacity .25s,transform .5s cubic-bezier(.19,1,.22,1)}.dark .VPSidebar[data-v-727d01b3]{box-shadow:var(--vp-shadow-1)}@media (min-width: 960px){.VPSidebar[data-v-727d01b3]{z-index:1;padding-top:var(--vp-nav-height);padding-bottom:128px;width:var(--vp-sidebar-width);max-width:100%;background-color:var(--vp-sidebar-bg-color);opacity:1;visibility:visible;box-shadow:none;transform:translate(0)}}@media (min-width: 1440px){.VPSidebar[data-v-727d01b3]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}@media (min-width: 960px){.curtain[data-v-727d01b3]{position:sticky;top:-64px;left:0;z-index:1;margin-top:calc(var(--vp-nav-height) * -1);margin-right:-32px;margin-left:-32px;height:var(--vp-nav-height);background-color:var(--vp-sidebar-bg-color)}}.nav[data-v-727d01b3]{outline:0}.group+.group[data-v-727d01b3]{border-top:1px solid var(--vp-c-divider);padding-top:10px}@media (min-width: 960px){.group[data-v-727d01b3]{padding-top:10px;width:calc(var(--vp-sidebar-width) - 64px)}}.VPSkipLink[data-v-7190744d]{top:8px;left:8px;padding:8px 16px;z-index:999;border-radius:8px;font-size:12px;font-weight:700;text-decoration:none;color:var(--vp-c-brand-1);box-shadow:var(--vp-shadow-3);background-color:var(--vp-c-bg)}.VPSkipLink[data-v-7190744d]:focus{height:auto;width:auto;clip:auto;clip-path:none}@media (min-width: 1280px){.VPSkipLink[data-v-7190744d]{top:14px;left:16px}}.Layout[data-v-18c0e4a1]{display:flex;flex-direction:column;min-height:100vh}.VPHomeSponsors[data-v-7a7af83e]{border-top:1px solid var(--vp-c-gutter);padding:88px 24px 96px;background-color:var(--vp-c-bg)}.container[data-v-7a7af83e]{margin:0 auto;max-width:1152px}.love[data-v-7a7af83e]{margin:0 auto;width:28px;height:28px;color:var(--vp-c-text-3)}.icon[data-v-7a7af83e]{width:28px;height:28px;fill:currentColor}.message[data-v-7a7af83e]{margin:0 auto;padding-top:10px;max-width:320px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.sponsors[data-v-7a7af83e]{padding-top:32px}.action[data-v-7a7af83e]{padding-top:40px;text-align:center}.VPTeamPage[data-v-ed0f8242]{padding-bottom:96px}@media (min-width: 768px){.VPTeamPage[data-v-ed0f8242]{padding-bottom:128px}}.VPTeamPageSection+.VPTeamPageSection[data-v-ed0f8242-s],.VPTeamMembers+.VPTeamPageSection[data-v-ed0f8242-s]{margin-top:64px}.VPTeamMembers+.VPTeamMembers[data-v-ed0f8242-s]{margin-top:24px}@media (min-width: 768px){.VPTeamPageTitle+.VPTeamPageSection[data-v-ed0f8242-s]{margin-top:16px}.VPTeamPageSection+.VPTeamPageSection[data-v-ed0f8242-s],.VPTeamMembers+.VPTeamPageSection[data-v-ed0f8242-s]{margin-top:96px}}.VPTeamMembers[data-v-ed0f8242-s]{padding:0 24px}@media (min-width: 768px){.VPTeamMembers[data-v-ed0f8242-s]{padding:0 48px}}@media (min-width: 960px){.VPTeamMembers[data-v-ed0f8242-s]{padding:0 64px}}.VPTeamPageTitle[data-v-b04b813b]{padding:48px 32px;text-align:center}@media (min-width: 768px){.VPTeamPageTitle[data-v-b04b813b]{padding:64px 48px 48px}}@media (min-width: 960px){.VPTeamPageTitle[data-v-b04b813b]{padding:80px 64px 48px}}.title[data-v-b04b813b]{letter-spacing:0;line-height:44px;font-size:36px;font-weight:500}@media (min-width: 768px){.title[data-v-b04b813b]{letter-spacing:-.5px;line-height:56px;font-size:48px}}.lead[data-v-b04b813b]{margin:0 auto;max-width:512px;padding-top:12px;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 768px){.lead[data-v-b04b813b]{max-width:592px;letter-spacing:.15px;line-height:28px;font-size:20px}}.VPTeamPageSection[data-v-98fd41b4]{padding:0 32px}@media (min-width: 768px){.VPTeamPageSection[data-v-98fd41b4]{padding:0 48px}}@media (min-width: 960px){.VPTeamPageSection[data-v-98fd41b4]{padding:0 64px}}.title[data-v-98fd41b4]{position:relative;margin:0 auto;max-width:1152px;text-align:center;color:var(--vp-c-text-2)}.title-line[data-v-98fd41b4]{position:absolute;top:16px;left:0;width:100%;height:1px;background-color:var(--vp-c-divider)}.title-text[data-v-98fd41b4]{position:relative;display:inline-block;padding:0 24px;letter-spacing:0;line-height:32px;font-size:20px;font-weight:500;background-color:var(--vp-c-bg)}.lead[data-v-98fd41b4]{margin:0 auto;max-width:480px;padding-top:12px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.members[data-v-98fd41b4]{padding-top:40px}.VPTeamMembersItem[data-v-61a83fed]{display:flex;flex-direction:column;gap:2px;border-radius:12px;width:100%;height:100%;overflow:hidden}.VPTeamMembersItem.small .profile[data-v-61a83fed]{padding:32px}.VPTeamMembersItem.small .data[data-v-61a83fed]{padding-top:20px}.VPTeamMembersItem.small .avatar[data-v-61a83fed]{width:64px;height:64px}.VPTeamMembersItem.small .name[data-v-61a83fed]{line-height:24px;font-size:16px}.VPTeamMembersItem.small .affiliation[data-v-61a83fed]{padding-top:4px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .desc[data-v-61a83fed]{padding-top:12px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .links[data-v-61a83fed]{margin:0 -16px -20px;padding:10px 0 0}.VPTeamMembersItem.medium .profile[data-v-61a83fed]{padding:48px 32px}.VPTeamMembersItem.medium .data[data-v-61a83fed]{padding-top:24px;text-align:center}.VPTeamMembersItem.medium .avatar[data-v-61a83fed]{width:96px;height:96px}.VPTeamMembersItem.medium .name[data-v-61a83fed]{letter-spacing:.15px;line-height:28px;font-size:20px}.VPTeamMembersItem.medium .affiliation[data-v-61a83fed]{padding-top:4px;font-size:16px}.VPTeamMembersItem.medium .desc[data-v-61a83fed]{padding-top:16px;max-width:288px;font-size:16px}.VPTeamMembersItem.medium .links[data-v-61a83fed]{margin:0 -16px -12px;padding:16px 12px 0}.profile[data-v-61a83fed]{flex-grow:1;background-color:var(--vp-c-bg-soft)}.data[data-v-61a83fed]{text-align:center}.avatar[data-v-61a83fed]{position:relative;flex-shrink:0;margin:0 auto;border-radius:50%;box-shadow:var(--vp-shadow-3)}.avatar-img[data-v-61a83fed]{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:50%;object-fit:cover}.name[data-v-61a83fed]{margin:0;font-weight:600}.affiliation[data-v-61a83fed]{margin:0;font-weight:500;color:var(--vp-c-text-2)}.org.link[data-v-61a83fed]{color:var(--vp-c-text-2);transition:color .25s}.org.link[data-v-61a83fed]:hover{color:var(--vp-c-brand-1)}.desc[data-v-61a83fed]{margin:0 auto}.desc[data-v-61a83fed] a{font-weight:500;color:var(--vp-c-brand-1);text-decoration-style:dotted;transition:color .25s}.links[data-v-61a83fed]{display:flex;justify-content:center;height:56px}.sp-link[data-v-61a83fed]{display:flex;justify-content:center;align-items:center;text-align:center;padding:16px;font-size:14px;font-weight:500;color:var(--vp-c-sponsor);background-color:var(--vp-c-bg-soft);transition:color .25s,background-color .25s}.sp .sp-link.link[data-v-61a83fed]:hover,.sp .sp-link.link[data-v-61a83fed]:focus{outline:none;color:var(--vp-c-white);background-color:var(--vp-c-sponsor)}.sp-icon[data-v-61a83fed]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPTeamMembers.small .container[data-v-9c8d38e0]{grid-template-columns:repeat(auto-fit,minmax(224px,1fr))}.VPTeamMembers.small.count-1 .container[data-v-9c8d38e0]{max-width:276px}.VPTeamMembers.small.count-2 .container[data-v-9c8d38e0]{max-width:576px}.VPTeamMembers.small.count-3 .container[data-v-9c8d38e0]{max-width:876px}.VPTeamMembers.medium .container[data-v-9c8d38e0]{grid-template-columns:repeat(auto-fit,minmax(256px,1fr))}@media (min-width: 375px){.VPTeamMembers.medium .container[data-v-9c8d38e0]{grid-template-columns:repeat(auto-fit,minmax(288px,1fr))}}.VPTeamMembers.medium.count-1 .container[data-v-9c8d38e0]{max-width:368px}.VPTeamMembers.medium.count-2 .container[data-v-9c8d38e0]{max-width:760px}.container[data-v-9c8d38e0]{display:grid;gap:24px;margin:0 auto;max-width:1152px}.domains-example-scope[data-v-5d111d5d] .aliceblue rect{stroke:#3b82f6cc!important;stroke-width:4!important}.domains-example-scope[data-v-5d111d5d] .pink rect{stroke:#f63264cc!important;stroke-width:4!important}.border[data-v-ba4f590a]{border-color:var(--vp-c-border)}td.status-cell[data-v-ba4f590a]{font-size:1.3em;padding:0}td.status-cell svg[data-v-ba4f590a]{margin-left:auto;margin-right:auto}svg.compat-matrix-table-icon[data-status=ok]{color:var(--vp-c-green)}svg.compat-matrix-table-icon[data-status=failed]{color:var(--vp-c-red)}svg.compat-matrix-table-icon[data-status=no-data]{color:var(--vp-c-yellow)}.v-btn-primary{border-radius:.5rem;background-color:var(--vp-c-brand-2);padding:.375rem .75rem;font-size:.875rem;line-height:1.25rem;font-weight:500;--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));--un-shadow:0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);box-shadow:var(--un-ring-offset-shadow),var(--un-ring-shadow),var(--un-shadow)}.dark .v-btn-primary{background-color:var(--vp-c-brand-3)}.v-btn-primary:not(:disabled):hover{background-color:var(--vp-c-brand-1)}.dark .v-btn-primary:not(:disabled):hover{background-color:var(--vp-c-brand-2)}.v-btn-primary:disabled{cursor:not-allowed;opacity:.5;--un-shadow:0 0 var(--un-shadow-color, rgba(0,0,0,0));box-shadow:var(--un-ring-offset-shadow),var(--un-ring-shadow),var(--un-shadow)}.feedback-card[data-v-701191ba]{background:var(--vp-c-bg);border-radius:8px;width:calc(100vw - 32px);max-width:420px;max-height:calc(100vh - 32px);border:1px solid var(--vp-c-border)}.feedback-card_header[data-v-701191ba]{border-bottom:1px solid var(--vp-c-border);padding:16px}.feedback-card_title[data-v-701191ba]{font-size:16px;font-weight:bolder}.feedback-card_check[data-v-701191ba]{color:var(--vp-custom-block-tip-text)}textarea[data-v-701191ba],input[data-v-701191ba]{font-family:var(--vp-font-family-base);background:var(--vp-c-bg-soft);font-size:14px;border-radius:4px;padding:16px;width:100%}textarea[data-v-701191ba]::placeholder,input[data-v-701191ba]::placeholder{color:var(--vp-c-text-2)!important;opacity:1}#share-feedback-description[data-v-701191ba]{color:var(--vp-c-text-2)}.field-label[data-v-701191ba]{font-size:12px;font-weight:bolder;display:block;margin-bottom:8px}.VPLocalSearchBox[data-v-58ff7037]{position:fixed;z-index:100;top:0;right:0;bottom:0;left:0;display:flex}.backdrop[data-v-58ff7037]{position:absolute;top:0;right:0;bottom:0;left:0;background:var(--vp-backdrop-bg-color);transition:opacity .5s}.shell[data-v-58ff7037]{position:relative;padding:12px;margin:64px auto;display:flex;flex-direction:column;gap:16px;background:var(--vp-local-search-bg);width:min(100vw - 60px,900px);height:min-content;max-height:min(100vh - 128px,900px);border-radius:6px}@media (max-width: 767px){.shell[data-v-58ff7037]{margin:0;width:100vw;height:100vh;max-height:none;border-radius:0}}.search-bar[data-v-58ff7037]{border:1px solid var(--vp-c-divider);border-radius:4px;display:flex;align-items:center;padding:0 12px;cursor:text}@media (max-width: 767px){.search-bar[data-v-58ff7037]{padding:0 8px}}.search-bar[data-v-58ff7037]:focus-within{border-color:var(--vp-c-brand-1)}.search-icon[data-v-58ff7037]{margin:8px}@media (max-width: 767px){.search-icon[data-v-58ff7037]{display:none}}.search-input[data-v-58ff7037]{padding:6px 12px;font-size:inherit;width:100%}@media (max-width: 767px){.search-input[data-v-58ff7037]{padding:6px 4px}}.search-actions[data-v-58ff7037]{display:flex;gap:4px}@media (any-pointer: coarse){.search-actions[data-v-58ff7037]{gap:8px}}@media (min-width: 769px){.search-actions.before[data-v-58ff7037]{display:none}}.search-actions button[data-v-58ff7037]{padding:8px}.search-actions button[data-v-58ff7037]:not([disabled]):hover,.toggle-layout-button.detailed-list[data-v-58ff7037]{color:var(--vp-c-brand-1)}.search-actions button.clear-button[data-v-58ff7037]:disabled{opacity:.37}.search-keyboard-shortcuts[data-v-58ff7037]{font-size:.8rem;opacity:75%;display:flex;flex-wrap:wrap;gap:16px;line-height:14px}.search-keyboard-shortcuts span[data-v-58ff7037]{display:flex;align-items:center;gap:4px}@media (max-width: 767px){.search-keyboard-shortcuts[data-v-58ff7037]{display:none}}.search-keyboard-shortcuts kbd[data-v-58ff7037]{background:rgba(128,128,128,.1);border-radius:4px;padding:3px 6px;min-width:24px;display:inline-block;text-align:center;vertical-align:middle;border:1px solid rgba(128,128,128,.15);box-shadow:0 2px 2px #0000001a}.results[data-v-58ff7037]{display:flex;flex-direction:column;gap:6px;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}.result[data-v-58ff7037]{display:flex;align-items:center;gap:8px;border-radius:4px;transition:none;line-height:1rem;border:solid 2px var(--vp-local-search-result-border);outline:none}.result>div[data-v-58ff7037]{margin:12px;width:100%;overflow:hidden}@media (max-width: 767px){.result>div[data-v-58ff7037]{margin:8px}}.titles[data-v-58ff7037]{display:flex;flex-wrap:wrap;gap:4px;position:relative;z-index:1001;padding:2px 0}.title[data-v-58ff7037]{display:flex;align-items:center;gap:4px}.title.main[data-v-58ff7037]{font-weight:500}.title-icon[data-v-58ff7037]{opacity:.5;font-weight:500;color:var(--vp-c-brand-1)}.title svg[data-v-58ff7037]{opacity:.5}.result.selected[data-v-58ff7037]{--vp-local-search-result-bg: var(--vp-local-search-result-selected-bg);border-color:var(--vp-local-search-result-selected-border)}.excerpt-wrapper[data-v-58ff7037]{position:relative}.excerpt[data-v-58ff7037]{opacity:75%;pointer-events:none;max-height:140px;overflow:hidden;position:relative;opacity:.5;margin-top:4px}.result.selected .excerpt[data-v-58ff7037]{opacity:1}.excerpt[data-v-58ff7037] *{font-size:.8rem!important;line-height:130%!important}.titles[data-v-58ff7037] mark,.excerpt[data-v-58ff7037] mark{background-color:var(--vp-local-search-highlight-bg);color:var(--vp-local-search-highlight-text);border-radius:2px;padding:0 2px}.excerpt[data-v-58ff7037] .vp-code-group .tabs{display:none}.excerpt[data-v-58ff7037] .vp-code-group div[class*=language-]{border-radius:8px!important}.excerpt-gradient-bottom[data-v-58ff7037]{position:absolute;bottom:-1px;left:0;width:100%;height:8px;background:linear-gradient(transparent,var(--vp-local-search-result-bg));z-index:1000}.excerpt-gradient-top[data-v-58ff7037]{position:absolute;top:-1px;left:0;width:100%;height:8px;background:linear-gradient(var(--vp-local-search-result-bg),transparent);z-index:1000}.result.selected .titles[data-v-58ff7037],.result.selected .title-icon[data-v-58ff7037]{color:var(--vp-c-brand-1)!important}.no-results[data-v-58ff7037]{font-size:.9rem;text-align:center;padding:12px}svg[data-v-58ff7037]{flex:none}.sticky-container[data-v-12fc2a51]{position:sticky;top:0;background:var(--vp-sidebar-bg-color);z-index:9}[data-v-12fc2a51] .VPSidebar.open .sticky-container{top:-32px}*,:before,:after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgba(0,0,0,0);--un-ring-shadow:0 0 rgba(0,0,0,0);--un-shadow-inset: ;--un-shadow:0 0 rgba(0,0,0,0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgba(0,0,0,0);--un-ring-shadow:0 0 rgba(0,0,0,0);--un-shadow-inset: ;--un-shadow:0 0 rgba(0,0,0,0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.static{position:static}.inset-0{top:0;right:0;bottom:0;left:0}.-top-0\.5{top:-.125rem}.z-90{z-index:90}.grid{display:grid}.-m-2{margin:-.5rem}.m10\.6{margin:2.65rem}.m12{margin:3rem}.m21{margin:5.25rem}.m9{margin:2.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.me{margin-inline-end:1rem}.block{display:block}.inline-block{display:inline-block}.contents{display:contents}.hidden{display:none}.h1{height:.25rem}.h2{height:.5rem}.h3{height:.75rem}.h4{height:1rem}.h5{height:1.25rem}.h6{height:1.5rem}.max-w-min{max-width:min-content}.flex{display:flex}.inline-flex{display:inline-flex}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-grow,.grow{flex-grow:1}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.table{display:table}.transform{transform:translate(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotate(var(--un-rotate-z)) skew(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z))}.cursor-not-allowed{cursor:not-allowed}.items-center{align-items:center}.justify-center{justify-content:center}.space-x-2>:not([hidden])~:not([hidden]){--un-space-x-reverse:0;margin-left:calc(.5rem * calc(1 - var(--un-space-x-reverse)));margin-right:calc(.5rem * var(--un-space-x-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--un-space-x-reverse:0;margin-left:calc(1rem * calc(1 - var(--un-space-x-reverse)));margin-right:calc(1rem * var(--un-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--un-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--un-space-y-reverse)));margin-bottom:calc(.25rem * var(--un-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--un-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--un-space-y-reverse)));margin-bottom:calc(1rem * var(--un-space-y-reverse))}.overflow-y-scroll{overflow-y:scroll}.whitespace-pre{white-space:pre}.b,.border{border-width:1px}.border-gray-300{--un-border-opacity:1;border-color:rgba(209,213,219,var(--un-border-opacity))}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border-solid{border-style:solid}.bg-black\/30{background-color:#0000004d}.bg-vp-bg{background-color:var(--vp-c-bg)}.bg-vp-brand-2{background-color:var(--vp-c-brand-2)}.dark .dark\:bg-vp-brand-3{background-color:var(--vp-c-brand-3)}.dark .dark\:hover\:bg-vp-brand-2:hover{background-color:var(--vp-c-brand-2)}.hover\:bg-vp-brand-1:hover{background-color:var(--vp-c-brand-1)}.hover\:bg-vp-brand-soft:hover{background-color:var(--vp-c-brand-soft)}.p-2{padding:.5rem}.p-4{padding:1rem}.p-8{padding:2rem}.px,.px-4{padding-left:1rem;padding-right:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-4{padding-top:1rem;padding-bottom:1rem}.ps{padding-inline-start:1rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.text-white{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity))}.hover\:text-vp-brand-2:hover{color:var(--vp-c-brand-2)}.tab{-moz-tab-size:4;-o-tab-size:4;tab-size:4}.opacity-50{opacity:.5}.shadow-elevation-btn{--un-shadow:0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);box-shadow:var(--un-ring-offset-shadow),var(--un-ring-shadow),var(--un-shadow)}.shadow-lg{--un-shadow:var(--un-shadow-inset) 0 10px 15px -3px var(--un-shadow-color, rgba(0,0,0,.1)),var(--un-shadow-inset) 0 4px 6px -4px var(--un-shadow-color, rgba(0,0,0,.1));box-shadow:var(--un-ring-offset-shadow),var(--un-ring-shadow),var(--un-shadow)}.shadow-md{--un-shadow:var(--un-shadow-inset) 0 4px 6px -1px var(--un-shadow-color, rgba(0,0,0,.1)),var(--un-shadow-inset) 0 2px 4px -2px var(--un-shadow-color, rgba(0,0,0,.1));box-shadow:var(--un-ring-offset-shadow),var(--un-ring-shadow),var(--un-shadow)}.shadow-none{--un-shadow:0 0 var(--un-shadow-color, rgba(0,0,0,0));box-shadow:var(--un-ring-offset-shadow),var(--un-ring-shadow),var(--un-shadow)}.outline{outline-style:solid}.blur{--un-blur:blur(8px);filter:var(--un-blur) var(--un-brightness) var(--un-contrast) var(--un-drop-shadow) var(--un-grayscale) var(--un-hue-rotate) var(--un-invert) var(--un-saturate) var(--un-sepia)}.filter{filter:var(--un-blur) var(--un-brightness) var(--un-contrast) var(--un-drop-shadow) var(--un-grayscale) var(--un-hue-rotate) var(--un-invert) var(--un-saturate) var(--un-sepia)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.ease{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}:root{--vp-font-family-mono: "JetBrains Mono", "Fira Code", Menlo, Monaco, Consolas, "Courier New", monospace}.vp-doc div[class*=language-] pre{font-size:14px;font-variant-ligatures:none}.vp-doc code{font-variant-ligatures:none} diff --git a/documenting/snippets.html b/documenting/snippets.html new file mode 100644 index 000000000..b2b05a571 --- /dev/null +++ b/documenting/snippets.html @@ -0,0 +1,106 @@ + + + + + + Code Snippets | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Code Snippets

    To make code snippets in the documentation more "real" and robust, it is better to fetch them directly from the source files. The sources are located in other repositories, where they are built, run, and tested.

    How it works

    Snippet Sources

    Snippet sources are defined in snippet_sources.ts. The snippet_sources.ts file is located at the documentation repository and has the following format:

    ts
    export default [
    +  {
    +    src: 'https://raw.githubusercontent.com/hyperledger/iroha/iroha2-stable/MAINTAINERS.md',
    +    filename: 'iroha-maintainers-at-stable.md',
    +  },
    +  {
    +    src: './src/example_code/lorem.rs',
    +  },
    +]
    export default [
    +  {
    +    src: 'https://raw.githubusercontent.com/hyperledger/iroha/iroha2-stable/MAINTAINERS.md',
    +    filename: 'iroha-maintainers-at-stable.md',
    +  },
    +  {
    +    src: './src/example_code/lorem.rs',
    +  },
    +]
    • src defines the source file location and could be either an HTTP(s) URI or a relative file path.
    • filename (optional) explicitly defines the local filename.

    Fetching Snippets

    Code snippets are fetched from the locations specified in snippet_sources.ts and written into the /src/snippets directory. There are two ways to fetch the snippets:

    • Automatically after dependencies were installed (i.e. pnpm install)
    • Manually by calling pnpm get-snippets

    TIP

    By default, snippets are deleted and reloaded each time pnpm get-snippets is called. For local development it might be more convenient to enable "lazy" behavior by calling pnpm get-snippets --force false.

    Using Snippets in Markdown

    Use Code Snippets feature in VitePress to include snippets into documentation:

    Input

    md
    <<<@/snippets/lorem.rs
    +
    +<<<@/snippets/lorem.rs#ipsum
    <<<@/snippets/lorem.rs
    +
    +<<<@/snippets/lorem.rs#ipsum

    Output

    rs
    fn main() {
    +    // #region ipsum
    +    println!("Lorem ipsum");
    +    // #endregion ipsum
    +}
    fn main() {
    +    // #region ipsum
    +    println!("Lorem ipsum");
    +    // #endregion ipsum
    +}
    rs
    println!("Lorem ipsum");
    println!("Lorem ipsum");

    Note that we included only the #ipsum code region, not the entire file. This feature is essential when it comes to including code from real source files into the documentation.

    Example

    Let's add a code snippet from Iroha JavaScript SDK. For example, this one: /packages/docs-recipes/src/1.client-install.ts.

    1. First, get a permalink to the file. Open the file on GitHub and click Raw button to get the link. For example: https://raw.githubusercontent.com/hyperledger/iroha-javascript/e300886e76c777776efad1e2f5cb245bfb8ed02e/packages/docs-recipes/src/1.client-install.ts

    2. Define the new snippet in the Snippet Sources:

      ts
      export default [
      +  /// ...
      +
      +  {
      +    src: 'https://raw.githubusercontent.com/hyperledger/iroha-javascript/e300886e76c777776efad1e2f5cb245bfb8ed02e/packages/docs-recipes/src/1.client-install.ts',
      +    filename: 'js-sdk-1-client-install.ts',
      +  },
      +]
      export default [
      +  /// ...
      +
      +  {
      +    src: 'https://raw.githubusercontent.com/hyperledger/iroha-javascript/e300886e76c777776efad1e2f5cb245bfb8ed02e/packages/docs-recipes/src/1.client-install.ts',
      +    filename: 'js-sdk-1-client-install.ts',
      +  },
      +]

      TIP

      Since snippet_sources.ts is a TypeScript file, we can use all scripting features in it. Meanwhile, we are trying to keep it as simple as possible, so even the one who doesn't know TypeScript at all could edit it.

      However, we use a bit of scripting. We defined several constants with git revisions from multiple repositories:

      ts
      const IROHA_REV_STABLE = 'c4af68c4f7959b154eb5380aa93c894e2e63fe4e'
      +
      +const IROHA_REV_DEV = '...'
      +
      +const IROHA_JS_REV = '...'
      const IROHA_REV_STABLE = 'c4af68c4f7959b154eb5380aa93c894e2e63fe4e'
      +
      +const IROHA_REV_DEV = '...'
      +
      +const IROHA_JS_REV = '...'

      Then we use them in links to snippet sources in place of git revisions, like this:

      ts
      export default [
      +  // ...
      +
      +  {
      +    src: `https://raw.githubusercontent.com/hyperledger/iroha/${IROHA_REV_STABLE}/MAINTAINERS.md`,
      +    //                                                        ^^^^^^^^^^^^^^^^^^^
      +    filename: 'iroha-maintainers-at-stable.md',
      +  },
      +]
      export default [
      +  // ...
      +
      +  {
      +    src: `https://raw.githubusercontent.com/hyperledger/iroha/${IROHA_REV_STABLE}/MAINTAINERS.md`,
      +    //                                                        ^^^^^^^^^^^^^^^^^^^
      +    filename: 'iroha-maintainers-at-stable.md',
      +  },
      +]

      It helps us to reduce repetitions and keep sources clean.

    3. Include the snippet in any Markdown file in the documentation as follows:

      Input

      md
      <<<@/snippets/js-sdk-1-client-install.ts
      <<<@/snippets/js-sdk-1-client-install.ts

      Output

      ts
      import { crypto } from '@iroha2/crypto-target-node'
      +import { setCrypto } from '@iroha2/client'
      +
      +setCrypto(crypto)
      import { crypto } from '@iroha2/crypto-target-node'
      +import { setCrypto } from '@iroha2/client'
      +
      +setCrypto(crypto)
    + + + + \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 000000000..b14a82960 Binary files /dev/null and b/favicon.ico differ diff --git a/guide/advanced/hot-reload.html b/guide/advanced/hot-reload.html new file mode 100644 index 000000000..12ebbbd21 --- /dev/null +++ b/guide/advanced/hot-reload.html @@ -0,0 +1,34 @@ + + + + + + How to hot reload Iroha in a Docker container | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    How to hot reload Iroha in a Docker container

    Here is the overall procedure for hot reloading Iroha in a Docker container:

    1. Build Iroha on your host OS.

      To avoid issues with dynamic linking, run:

      bash
      $ cargo build --release --target x86_64-unknown-linux-musl --features "vendored"
      $ cargo build --release --target x86_64-unknown-linux-musl --features "vendored"
      An explanation for using `cargo build` with these parameters.

      You may experience an issue with dynamic linking if your host OS has a newer version of glibc compared to the one in the Docker container. The options used in the command above resolve the issue:

      • --target x86_64-unknown-linux-musl forces static linking against musl libc implementation
      • --features "vendored" facilitates static linkage of the openssl library
    2. Enter Docker container. For example:

      bash
      $ docker exec -it iroha-iroha0-1 bash
      $ docker exec -it iroha-iroha0-1 bash
    3. Copy Iroha to the current directory:

      bash
      $ docker cp root/soramitsu/iroha/target/x86_64-unknown-linux-musl/release/iroha .
      $ docker cp root/soramitsu/iroha/target/x86_64-unknown-linux-musl/release/iroha .
    4. (Optional) Make any modifications you need:

    5. Exit docker container and restart it using docker restart.

      Note: If you use the combination of container down and container up, any modifications you made on the previous step will be lost. Use docker restart to preserve changes.

    If you skip the optional step (step 4), the state of the blockchain after hot reload will be the same as it was before the Docker container was restarted.

    Note that if you get the Kura initialisation failed error message, it might mean one of two things: corruption or binary incompatibility of the stored block. To fix this, remove the blocks/ directory.

    Wiping previous blockchain state (recommit genesis)

    To recommit a custom genesis block, remove the previously stored blocks before restarting the container:

    bash
    $ rm blocks/*
    $ rm blocks/*

    The new genesis block will be automatically recommited upon container restart.

    Use custom configuration files

    To use custom configuration files, such as config.json or genesis.json, copy (or bind mount) them to the config/ subvolume before restarting the Docker container.

    The changes will take effect upon container restart.

    Use custom environment variables

    To use custom environment variables (e.g. IROHA_PUBLIC_KEY), simply modify them before restarting the Docker container. For example:

    bash
    $ IROHA_PUBLIC_KEY=<new_key> docker restart
    $ IROHA_PUBLIC_KEY=<new_key> docker restart

    The changes will take effect upon container restart.

    + + + + \ No newline at end of file diff --git a/guide/advanced/metrics.html b/guide/advanced/metrics.html new file mode 100644 index 000000000..8ba6ef1f5 --- /dev/null +++ b/guide/advanced/metrics.html @@ -0,0 +1,62 @@ + + + + + + Metrics | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Metrics

    To conveniently and thoroughly monitor the performance of the network, we recommend using prometheus. Prometheus is a program that can monitor your Iroha peer over a separate socket and provide different kinds of performance metrics.

    This data can help you find performance bottlenecks and optimise your Iroha configuration.

    How to use metrics

    To use metrics, you need to configure the /metrics endpoint in the Iroha configuration. By default, the endpoint is exposed at 127.0.0.1:8180/metrics. If the port is not available, Iroha will still start and work normally, but metrics won't be accessible.

    After that, use the IP address to access the data from the running Iroha instance. For example:

    bash
    $ curl http://127.0.0.1:8080/metrics
    $ curl http://127.0.0.1:8080/metrics

    This will give you a result like this:

    bash
    # HELP blocks_height Total number of blocks in chain
    +# TYPE blocks_height gauge
    +blocks_height 135543
    +# HELP peers_number Total number peers to send transactions and request proposals
    +# TYPE peers_number gauge
    +peers_number 7
    +# HELP number_of_domains Total number of domains in WSV
    +# TYPE number_of_domains gauge
    +number_of_domains 14
    +# HELP total_number_of_transactions Total number of transactions in blockchain
    +# TYPE total_number_of_transactions gauge
    +total_number_of_transactions 216499
    +# HELP number_of_signatures_in_last_block Number of signatures in last block
    +# TYPE number_of_signatures_in_last_block gauge
    +number_of_signatures_in_last_block 5
    # HELP blocks_height Total number of blocks in chain
    +# TYPE blocks_height gauge
    +blocks_height 135543
    +# HELP peers_number Total number peers to send transactions and request proposals
    +# TYPE peers_number gauge
    +peers_number 7
    +# HELP number_of_domains Total number of domains in WSV
    +# TYPE number_of_domains gauge
    +number_of_domains 14
    +# HELP total_number_of_transactions Total number of transactions in blockchain
    +# TYPE total_number_of_transactions gauge
    +total_number_of_transactions 216499
    +# HELP number_of_signatures_in_last_block Number of signatures in last block
    +# TYPE number_of_signatures_in_last_block gauge
    +number_of_signatures_in_last_block 5

    /metrics endpoint

    Refer to the API specification.

    + + + + \ No newline at end of file diff --git a/guide/advanced/running-iroha-on-bare-metal.html b/guide/advanced/running-iroha-on-bare-metal.html new file mode 100644 index 000000000..c37d4d388 --- /dev/null +++ b/guide/advanced/running-iroha-on-bare-metal.html @@ -0,0 +1,284 @@ + + + + + + Iroha on bare metal | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Iroha on bare metal

    What we are going to do is replicate the setup that we have in docker compose and run Iroha directly, without going through the intermediary of containers. Running Iroha on bare metal involves manipulating files and/or environment variables.

    The file-based approach is the easiest to get right. Using environment variables can offer a better user experience if done right, but is more error-prone, particularly for exotic systems (Windows).

    INFO

    For this chapter, we assume you have learned about configuration and management in Iroha 2. Here we offer you instructions to run Iroha on bare metal without going into details about various configuration options available.

    You can always check sample configuration files for configs/peer/genesis.json and configs/peer/config.json, or refer to peer configuration options for more details.

    The complete list of options is available in the Iroha Configuration Reference.

    Prerequisites

    First of all, we should note that we have only built the Iroha client so far in this tutorial. We also need to build the peer software to run Iroha on bare metal.

    INFO

    Building in debug mode retains much more information and optimises the binary to a far lesser extent. As such, we advise you to build Iroha in debug mode for testing: it’s faster and it makes it easier for you to find issues and fix them. However, if you intend to actually deploy Iroha, you should build it in --release mode.

    • To build the peer software in debug mode, run:

      kotlin
      cargo build -p iroha
      cargo build -p iroha
    • To build the peer software in release mode, run:

      kotlin
      cargo build --bin iroha --release
      cargo build --bin iroha --release

      The release mode binary takes significantly longer to compile than debug mode, but the result is a smaller and faster binary, suitable for deployment in the actual blockchains.

    Setup

    Setup: Environment variables

    We want to make sure that we have the right configuration.

    There are different ways to do this. You can copy the contents of the ~/Git/iroha/configs/peer/ into a new directory, or, alternatively, just run all commands from that directory:

    bash
    $ cd ~/Git/iroha/configs/peer
    $ cd ~/Git/iroha/configs/peer

    The third option is to specify the full path to the configuration file in an environment variable. For simplicity, we shall do the latter:

    bash
    $ cd ~
    +$ export IROHA2_GENESIS_PATH="$(pwd)/Git/iroha/configs/peer/genesis.json"
    +$ export IROHA2_CONFIG_PATH="$(pwd)/Git/iroha/configs/peer/config.json"
    $ cd ~
    +$ export IROHA2_GENESIS_PATH="$(pwd)/Git/iroha/configs/peer/genesis.json"
    +$ export IROHA2_CONFIG_PATH="$(pwd)/Git/iroha/configs/peer/config.json"

    For extra convenience, you could add the Iroha 2 target directory to your PATH. This means that instead of having to specify the full path to the executable iroha, you can instead type iroha directly into your command line.

    bash
    $ export PATH="$PATH:$(pwd)/Git/iroha/target/debug"
    $ export PATH="$PATH:$(pwd)/Git/iroha/target/debug"

    TIP

    Don’t forget to replace debug with release when you’re ready to deploy in the real world.

    This way you can run iroha from any directory without having to worry about configuration paths and/or specifying the full path to the Iroha executable.

    It is a good idea to make these instructions permanent, which you can do by adding the environment variables to your startup shell.

    Save the instructions to the startup shell

    On older Linux systems, you copy and paste the instructions (without the cd ~) to ~/.bashrc. On Mac OS X 10.6 and later, as well as some Linux systems, you want to add the same lines to ~/.zshrc.

    Copy these instructions to the specified files (replace debug with release when you are ready to deploy):

    bash
    $ export IROHA2_GENESIS_PATH="$(pwd)/Git/iroha/configs/peer/genesis.json"
    +$ export IROHA2_CONFIG_PATH="$(pwd)/Git/iroha/configs/peer/config.json"
    +$ export PATH="$PATH:$(pwd)/Git/iroha/target/debug"
    $ export IROHA2_GENESIS_PATH="$(pwd)/Git/iroha/configs/peer/genesis.json"
    +$ export IROHA2_CONFIG_PATH="$(pwd)/Git/iroha/configs/peer/config.json"
    +$ export PATH="$PATH:$(pwd)/Git/iroha/target/debug"

    TIP

    This process is almost universally unreliable and messy, and it is likely that your system is special in that it breaks some of our assumptions.

    If the above optional steps didn’t work for you, you can keep working in the ~/Git/iroha/configs/peer/ folder, and run Iroha via ~/Git/iroha/target/debug/iroha.

    This makes the command-line a little harder to read, which is why we recommend setting up your environment first.

    Note

    The tutorial assumes that you’re running either Linux, Mac OS X, or Windows using WSL. It should be possible to run directly on Windows, but that is neither recommended nor easy. If you don’t want to use a Unix-like system, we suggest that you wait until we publish a detailed guide for Windows users.

    Setup: Files

    This is the recommended method of bringing up an Iroha peer. What we do is:

    1. Create a new directory for the configuration files:

      bash
      $ mkdir -p ~/Git/iroha/deploy
      $ mkdir -p ~/Git/iroha/deploy
    2. Copy the peer configuration into it:

      bash
      $ cp -vfr ~/Git/iroha/configs/peer/*.json ~/Git/iroha/deploy
      $ cp -vfr ~/Git/iroha/configs/peer/*.json ~/Git/iroha/deploy
    3. Copy the respective Iroha binary into your binary folder:

      bash
      $ sudo cp -vfr ~/Git/iroha/target/debug/iroha /usr/bin/
      $ sudo cp -vfr ~/Git/iroha/target/debug/iroha /usr/bin/

      which will install Iroha 2 system wide.

    TIP

    You could also use the iroha peer binary locally by copying it into the same folder. The only difference would be that you’d be calling Iroha like so: ./iroha instead of iroha.

    First run of Iroha on bare metal

    If you’ve done everything correctly, you can now do

    bash
    $ iroha
    $ iroha

    to start your first peer and be greeted with

    Untitled

    This means that everything is working, but also that we need to do some more work.

    You have just started a single peer, which can tolerate exactly 0 faults. Running two peers is also possible, but again, can tolerate 0 faults. You must run at least 4 peers in order to have the capacity to tolerate at least one fault.

    In general, if you want to be resistant to f faults, you want to have 3f+1 peers: (4, 7, 10, etc.).

    You cannot really start the peers in any way you want, though. When we started our original peer, in its configuration, we specified that it has to trust very specific peers, which have the given private key and listen on a specific address. In order to know how to run them appropriately, take a look at docker-compose.yml:

    docker-compose.yaml
    yaml
    version: '3.8'
    +services:
    +  iroha0:
    +    image: hyperledger/iroha2:dev
    +    environment:
    +      TORII_P2P_ADDR: iroha0:1337
    +      TORII_API_URL: iroha0:8080
    +      TORII_TELEMETRY_URL: iroha0:8180
    +      IROHA_PUBLIC_KEY: 'ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b'
    +      IROHA_PRIVATE_KEY:
    +        '{"digest_function": "ed25519", "payload":
    +        "282ed9f3cf92811c3818dbc4ae594ed59dc1a2f78e4241e31924e101d6b1fb831c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"}'
    +      SUMERAGI_TRUSTED_PEERS:
    +        '[{"address":"iroha0:1337", "public_key":
    +        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
    +        {"address":"iroha1:1338", "public_key":
    +        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
    +        {"address": "iroha2:1339", "public_key":
    +        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
    +        {"address": "iroha3:1340", "public_key":
    +        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    +      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
    +      IROHA_GENESIS_ACCOUNT_PRIVATE_KEY:
    +        '{ "digest_function": "ed25519", "payload":
    +        "038ae16b219da35aa036335ed0a43c28a2cc737150112c78a7b8034b9d99c9023f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255"
    +        }'
    +    ports:
    +      - '1337:1337'
    +      - '8080:8080'
    +      - '8180:8180'
    +    volumes:
    +      - './configs/peer:/config'
    +    init: true
    +    command: iroha --submit-genesis
    +
    +  iroha1:
    +    image: hyperledger/iroha2:dev
    +    environment:
    +      TORII_P2P_ADDR: iroha1:1338
    +      TORII_API_URL: iroha1:8081
    +      TORII_TELEMETRY_URL: iroha1:8181
    +      IROHA_PUBLIC_KEY: 'ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1'
    +      IROHA_PRIVATE_KEY:
    +        '{"digest_function": "ed25519", "payload":
    +        "3bac34cda9e3763fa069c1198312d1ec73b53023b8180c822ac355435edc4a24cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"}'
    +      SUMERAGI_TRUSTED_PEERS:
    +        '[{"address":"iroha0:1337", "public_key":
    +        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
    +        {"address":"iroha1:1338", "public_key":
    +        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
    +        {"address": "iroha2:1339", "public_key":
    +        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
    +        {"address": "iroha3:1340", "public_key":
    +        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    +      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
    +    ports:
    +      - '1338:1338'
    +      - '8081:8081'
    +      - '8181:8181'
    +    volumes:
    +      - './configs/peer:/config'
    +    init: true
    +
    +  iroha2:
    +    image: hyperledger/iroha2:dev
    +    environment:
    +      TORII_P2P_ADDR: iroha2:1339
    +      TORII_API_URL: iroha2:8082
    +      TORII_TELEMETRY_URL: iroha2:8182
    +      IROHA_PUBLIC_KEY: 'ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020'
    +      IROHA_PRIVATE_KEY:
    +        '{"digest_function": "ed25519", "payload":
    +        "1261a436d36779223d7d6cf20e8b644510e488e6a50bafd77a7485264d27197dfaca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"}'
    +      SUMERAGI_TRUSTED_PEERS:
    +        '[{"address":"iroha0:1337", "public_key":
    +        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
    +        {"address":"iroha1:1338", "public_key":
    +        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
    +        {"address": "iroha2:1339", "public_key":
    +        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
    +        {"address": "iroha3:1340", "public_key":
    +        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    +      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
    +    ports:
    +      - '1339:1339'
    +      - '8082:8082'
    +      - '8182:8182'
    +    volumes:
    +      - './configs/peer:/config'
    +    init: true
    +
    +  iroha3:
    +    image: hyperledger/iroha2:dev
    +    environment:
    +      TORII_P2P_ADDR: iroha3:1340
    +      TORII_API_URL: iroha3:8083
    +      TORII_TELEMETRY_URL: iroha3:8183
    +      IROHA_PUBLIC_KEY: 'ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f'
    +      IROHA_PRIVATE_KEY:
    +        '{"digest_function": "ed25519", "payload":
    +        "a70dab95c7482eb9f159111b65947e482108cfe67df877bd8d3b9441a781c7c98e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}'
    +      SUMERAGI_TRUSTED_PEERS:
    +        '[{"address":"iroha0:1337", "public_key":
    +        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
    +        {"address":"iroha1:1338", "public_key":
    +        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
    +        {"address": "iroha2:1339", "public_key":
    +        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
    +        {"address": "iroha3:1340", "public_key":
    +        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    +      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
    +    ports:
    +      - '1340:1340'
    +      - '8083:8083'
    +      - '8183:8183'
    +    volumes:
    +      - './configs/peer:/config'
    +    init: true
    version: '3.8'
    +services:
    +  iroha0:
    +    image: hyperledger/iroha2:dev
    +    environment:
    +      TORII_P2P_ADDR: iroha0:1337
    +      TORII_API_URL: iroha0:8080
    +      TORII_TELEMETRY_URL: iroha0:8180
    +      IROHA_PUBLIC_KEY: 'ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b'
    +      IROHA_PRIVATE_KEY:
    +        '{"digest_function": "ed25519", "payload":
    +        "282ed9f3cf92811c3818dbc4ae594ed59dc1a2f78e4241e31924e101d6b1fb831c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"}'
    +      SUMERAGI_TRUSTED_PEERS:
    +        '[{"address":"iroha0:1337", "public_key":
    +        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
    +        {"address":"iroha1:1338", "public_key":
    +        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
    +        {"address": "iroha2:1339", "public_key":
    +        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
    +        {"address": "iroha3:1340", "public_key":
    +        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    +      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
    +      IROHA_GENESIS_ACCOUNT_PRIVATE_KEY:
    +        '{ "digest_function": "ed25519", "payload":
    +        "038ae16b219da35aa036335ed0a43c28a2cc737150112c78a7b8034b9d99c9023f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255"
    +        }'
    +    ports:
    +      - '1337:1337'
    +      - '8080:8080'
    +      - '8180:8180'
    +    volumes:
    +      - './configs/peer:/config'
    +    init: true
    +    command: iroha --submit-genesis
    +
    +  iroha1:
    +    image: hyperledger/iroha2:dev
    +    environment:
    +      TORII_P2P_ADDR: iroha1:1338
    +      TORII_API_URL: iroha1:8081
    +      TORII_TELEMETRY_URL: iroha1:8181
    +      IROHA_PUBLIC_KEY: 'ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1'
    +      IROHA_PRIVATE_KEY:
    +        '{"digest_function": "ed25519", "payload":
    +        "3bac34cda9e3763fa069c1198312d1ec73b53023b8180c822ac355435edc4a24cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"}'
    +      SUMERAGI_TRUSTED_PEERS:
    +        '[{"address":"iroha0:1337", "public_key":
    +        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
    +        {"address":"iroha1:1338", "public_key":
    +        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
    +        {"address": "iroha2:1339", "public_key":
    +        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
    +        {"address": "iroha3:1340", "public_key":
    +        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    +      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
    +    ports:
    +      - '1338:1338'
    +      - '8081:8081'
    +      - '8181:8181'
    +    volumes:
    +      - './configs/peer:/config'
    +    init: true
    +
    +  iroha2:
    +    image: hyperledger/iroha2:dev
    +    environment:
    +      TORII_P2P_ADDR: iroha2:1339
    +      TORII_API_URL: iroha2:8082
    +      TORII_TELEMETRY_URL: iroha2:8182
    +      IROHA_PUBLIC_KEY: 'ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020'
    +      IROHA_PRIVATE_KEY:
    +        '{"digest_function": "ed25519", "payload":
    +        "1261a436d36779223d7d6cf20e8b644510e488e6a50bafd77a7485264d27197dfaca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"}'
    +      SUMERAGI_TRUSTED_PEERS:
    +        '[{"address":"iroha0:1337", "public_key":
    +        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
    +        {"address":"iroha1:1338", "public_key":
    +        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
    +        {"address": "iroha2:1339", "public_key":
    +        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
    +        {"address": "iroha3:1340", "public_key":
    +        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    +      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
    +    ports:
    +      - '1339:1339'
    +      - '8082:8082'
    +      - '8182:8182'
    +    volumes:
    +      - './configs/peer:/config'
    +    init: true
    +
    +  iroha3:
    +    image: hyperledger/iroha2:dev
    +    environment:
    +      TORII_P2P_ADDR: iroha3:1340
    +      TORII_API_URL: iroha3:8083
    +      TORII_TELEMETRY_URL: iroha3:8183
    +      IROHA_PUBLIC_KEY: 'ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f'
    +      IROHA_PRIVATE_KEY:
    +        '{"digest_function": "ed25519", "payload":
    +        "a70dab95c7482eb9f159111b65947e482108cfe67df877bd8d3b9441a781c7c98e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}'
    +      SUMERAGI_TRUSTED_PEERS:
    +        '[{"address":"iroha0:1337", "public_key":
    +        "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"},
    +        {"address":"iroha1:1338", "public_key":
    +        "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"},
    +        {"address": "iroha2:1339", "public_key":
    +        "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"},
    +        {"address": "iroha3:1340", "public_key":
    +        "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    +      IROHA_GENESIS_ACCOUNT_PUBLIC_KEY: 'ed01203f4e3e98571b55514edc5ccf7e53ca7509d89b2868e62921180a6f57c2f4e255'
    +    ports:
    +      - '1340:1340'
    +      - '8083:8083'
    +      - '8183:8183'
    +    volumes:
    +      - './configs/peer:/config'
    +    init: true

    For every peer, the environment section is a set of things that you should put in front of the iroha command, replacing colons with equals signs. All the socket addresses are also given internal to the docker network, so we should replace them with [localhost](http://localhost), which is 127.0.0.1 on most machines.

    TIP

    Each Iroha instance is going to listen on three ports: the Peer-to-peer communications channel (133X), the API url, where most client requests are posted (808X), and finally, a telemetry endpoint 818X. All three ports need to be adjusted so there are no collisions. See the docker-compose.yml for an example, and adjust as needed.

    Deploy a minimal BFT network

    Both of there approaches are messy and error-prone, which is why the tutorial uses docker-compose. However, this brings you closer to the experience of actually maintaining a functional Iroha peer.

    Using Environment Variables

    To run the First peer, we need to write

    bash
    $ TORII_P2P_ADDR="127.0.0.1:1337"
    +$ TORII_API_URL="127.0.0.1:8080"
    +$ TORII_STATUS_URL="127.0.0.1:8180"
    +$ IROHA_PUBLIC_KEY="ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" IROHA_PRIVATE_KEY='{"digest_function": "ed25519", "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"}'
    +$ iroha --submit
    $ TORII_P2P_ADDR="127.0.0.1:1337"
    +$ TORII_API_URL="127.0.0.1:8080"
    +$ TORII_STATUS_URL="127.0.0.1:8180"
    +$ IROHA_PUBLIC_KEY="ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0" IROHA_PRIVATE_KEY='{"digest_function": "ed25519", "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"}'
    +$ iroha --submit

    and three other similar lines of bash code for the remaining deployments.

    TIP

    To copy and paste into the terminal on Linux systems, you should remember that Control + Shift + V is the appropriate paste shortcut.

    Also note that we asked this peer to --submit or --submit-genesis. This means that in the initial network topology, this peer is the leader. At least one peer (usually the first) needs to be the leader in the initial topology.

    Now you should do the same for the other four peers. Be mindful not to mix up which address goes where, replace irohaX with 127.0.0.1 in the addresses, and make sure that they correspond to the right public key.

    Using Files

    Our first peer can run off of the original configuration file. What we should do is create three more similar files and move them to three different folders e.g. peer1, peer2.

    What you need to do is change the TORII:P2P_ADDR, TORII:API_URLTORII:STATUS_URL and the PUBLIC_KEY configuration options to align with their docker-compose.yml counterparts.

    Be mindful not to mix up which address goes where, replace irohaX with 127.0.0.1 in the addresses, and make sure that they correspond to the right public key.

    Then, in each of the new folders (with the exception of peer0) run:

    bash
    $ iroha
    $ iroha

    In the first folder peer0 you should run:

    bash
    $ iroha --submit-genesis
    $ iroha --submit-genesis

    We effectively asked this peer to --submit or --submit-genesis in the initial, or bootstrap, network. This means that in the initial network topology, this peer is the leader.

    Note

    Only the leader of the genesis network needs to have access to configs/peer/genesis.json. Having the same genesis in the initial folders of the other peers could be useful, since future versions of iroha will also sanity-check the genesis blocks.

    If all went well, you should be greeted with nice logs on each of the nodes, and the nodes should commit the blocks to the blocks/ directory.

    Real-world deployment

    Suppose now, that you have done all of the tinkering and want to deploy Iroha in the real world.

    1. Build Iroha in release mode:

      bash
      $ cargo build --release
      $ cargo build --release
    2. Generate a key pair for your peer and take note of that key:

      bash
      $ cargo run --bin iroha_crypto_cli
      $ cargo run --bin iroha_crypto_cli
    3. Register your peer to a network, and make sure to add at least four of the peers on that network to the TRUSTED_PEERS array in your configuration file.

    4. Determine the web socket that the other peers will use to connect to you. Make sure that the port is open and use that address (P2P_ADDR) in your configs/peer/config.json file.

    5. After you have finished editing the configuration file, deploy Iroha by running

      bash
      $ ~/Git/iroha/target/release/iroha
      $ ~/Git/iroha/target/release/iroha

    Note

    There is no need to pass the --submit flag unless you are starting the initial peer on the network.

    + + + + \ No newline at end of file diff --git a/guide/blockchain/accounts.html b/guide/blockchain/accounts.html new file mode 100644 index 000000000..340c59e47 --- /dev/null +++ b/guide/blockchain/accounts.html @@ -0,0 +1,34 @@ + + + + + + Accounts | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/guide/blockchain/assets.html b/guide/blockchain/assets.html new file mode 100644 index 000000000..6b16eef10 --- /dev/null +++ b/guide/blockchain/assets.html @@ -0,0 +1,34 @@ + + + + + + Assets | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Assets

    Iroha has been built with few underlying assumptions about what the assets need to be.

    The assets can be fungible (every £1 is exactly the same as every other £1) or non-fungible (a £1 bill signed by the Queen of Hearts is not the same as a £1 bill signed by the King of Spades).

    The assets can also be mintable (you can make more of them) and non-mintable (you can only specify their initial quantity in the genesis block).

    Value Types

    Additionally, the assets have different underlying value types. Specifically, we have AssetValueType.Quantity, which is effectively an unsigned 32-bit integer, a BigQuantity, which is an unsigned 128-bit integer, and Fixed, which is a positive (though signed) 64-bit fixed-precision number with nine significant digits after the decimal point. All three types can be registered as either mintable or non-mintable.

    There is also the Store asset type, which is used for storing key-values in object's metadata. We talk in detail about Store asset in the chapter related to metadata.

    Asset Structure

    Instructions

    Assets can be registered, minted or burned, and transferred.

    Refer to one of the language-specific guides to walk you through the process of registering and minting assets in a blockchain:

    + + + + \ No newline at end of file diff --git a/guide/blockchain/consensus.html b/guide/blockchain/consensus.html new file mode 100644 index 000000000..7e241c062 --- /dev/null +++ b/guide/blockchain/consensus.html @@ -0,0 +1,34 @@ + + + + + + Consensus | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Consensus

    Each time you send a transaction to Iroha, it gets put into a queue. When it's time to produce a new block, the queue is emptied, and the consensus process begins. This process is equal parts common sense and black magic[1].

    The mundane aspect is that a special set of peers needs to take the transaction queue and reproduce the same world state. If the world state cannot be reproduced for some reason or another, none of the transactions get committed to a block.

    The consensus starts over from scratch by choosing a different special set of peers. This is where the black magic comes in. There is a number of things that are fine-tuned: the number of peers in the voting process, the way in which subsequent voting peers are chosen, and the way in which the peers communicate that consensus has failed. Because this changes the view of the world, the process is called a view change. The exact reason for why the view was changed is encoded in the view change proof, but decoding that information is an advanced topic that we won't cover here.

    The reasoning behind this algorithm is simple: if someone had some evil peers and connected them to the existing network, if they tried to fake data, some good™ peers would not get the same (evil™) world state. If that's the case, the evil™ peers would not be allowed to participate in consensus, and you would eventually produce a block using only good™ peers.

    As a natural consequence, if any changes to the world state are made without the use of ISI, the good™ peers cannot know of them. They won't be able to reproduce the hash of the world state, and thus consensus will fail. The same thing happens if the peers have different instructions.


    1. For prospective wizards, the Iroha 2 Whitepaper is a good start. ↩︎

    + + + + \ No newline at end of file diff --git a/guide/blockchain/data-model.html b/guide/blockchain/data-model.html new file mode 100644 index 000000000..e20288939 --- /dev/null +++ b/guide/blockchain/data-model.html @@ -0,0 +1,68 @@ + + + + + + Data Model | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Data Model

    In language-specific guides we already walked you through registering domains, accounts, and assets. Here we merely wish to illustrate the relationship between various objects in the blockchain.

    +-----------------------------------------------+
    +   |                                               |
    +   |     +-----------------+                       |
    +   |     |Domain           |                       |
    +   |     +--------------+  |                       |
    +   |     ||Asset        |  |                       |
    ++--+--+  ||Definition(s)|  |                       |
    +|World|  +--------------+  |                       |
    ++--+--+  |                 |                       |
    +   |     +------------+    |                       |
    +   |     ||Account(s)||    | has   +-----------+   |
    +   |     |------------------------->Signatories|   |
    +   |     +-----------------+       +-----------+   |
    +   |                       |                       |
    +   |                       |  has  +--------+      |
    +   |                       +------->Asset(s)|      |
    +   |                               +--------+      |
    +   +-----------------------------------------------+
    +-----------------------------------------------+
    +   |                                               |
    +   |     +-----------------+                       |
    +   |     |Domain           |                       |
    +   |     +--------------+  |                       |
    +   |     ||Asset        |  |                       |
    ++--+--+  ||Definition(s)|  |                       |
    +|World|  +--------------+  |                       |
    ++--+--+  |                 |                       |
    +   |     +------------+    |                       |
    +   |     ||Account(s)||    | has   +-----------+   |
    +   |     |------------------------->Signatories|   |
    +   |     +-----------------+       +-----------+   |
    +   |                       |                       |
    +   |                       |  has  +--------+      |
    +   |                       +------->Asset(s)|      |
    +   |                               +--------+      |
    +   +-----------------------------------------------+

    The following example shows the relationship between domains, accounts, and assets.

    Language-specific guides to register these objects
    LanguageGuide
    BashRegister a domain, an account, an asset
    RustRegister a domain, an account, an asset
    Kotlin/JavaRegister a domain, an account, an asset
    PythonRegister a domain, an account, an asset
    JavaScript/TypeScriptRegister a domain, an account, an asset

    The diagram below provides a more detailed illustration of the relationship between domains, accounts, and assets in the blockchain. You can learn more about permissions and roles and metadata in the corresponding sections. The asset structure is illustrated in a dedicated chapter.

    + + + + \ No newline at end of file diff --git a/guide/blockchain/domains.html b/guide/blockchain/domains.html new file mode 100644 index 000000000..0622cd4b6 --- /dev/null +++ b/guide/blockchain/domains.html @@ -0,0 +1,34 @@ + + + + + + Domains | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/guide/blockchain/events.html b/guide/blockchain/events.html new file mode 100644 index 000000000..1bcbfb442 --- /dev/null +++ b/guide/blockchain/events.html @@ -0,0 +1,34 @@ + + + + + + Events | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Events

    Events are emitted when certain things happen within the blockchain, e.g. a new account is created or a block is committed. There are different types of events:

    • pipeline events
    • data events
    • time events
    • trigger execution events

    Pipeline Events

    Pipeline events are emitted when transactions are submitted, executed, or committed to a block. A pipeline event contains the following information: the kind of entity that caused an event (transaction or block), its hash and status. The status can be either Validating (validation in progress), Rejected, or Committed. If an entity was rejected, the reason for the rejection is provided.

    Data Events

    Data events are emitted when there is a change related to one of the following entities: peers, domains, accounts, asset definitions, assets, triggers, roles, permission tokens, permission validators, or Iroha configuration. These types of events are used in entity filters.

    Time Events

    Time events are emitted when the world state view is ready to handle time triggers.

    Trigger Execution Events

    Trigger execution events are emitted when the ExecuteTrigger instruction is executed

    + + + + \ No newline at end of file diff --git a/guide/blockchain/expressions.html b/guide/blockchain/expressions.html new file mode 100644 index 000000000..c20a0e466 --- /dev/null +++ b/guide/blockchain/expressions.html @@ -0,0 +1,34 @@ + + + + + + Expressions, Conditionals, Logic | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Expressions, Conditionals, Logic

    All Iroha Special Instructions operate on expressions. Each expression has an EvaluatesTo, which is used in instruction execution. While you could specify the account name directly, you could also specify the account ID via some mathematical or string operation. You can check if an account is registered on the blockchain too.

    Using expressions that implement EvaluatesTo<bool>, you can set up conditional logic and execute more sophisticated operations on-chain. For example, you can submit a Mint instruction only if a specific account is registered.

    Recall that you can combine this with queries, and as such can program the blockchain to do some amazing stuff. This is what we refer to as smart contracts, the defining feature of the advanced usage of blockchain technology.

    + + + + \ No newline at end of file diff --git a/guide/blockchain/filters.html b/guide/blockchain/filters.html new file mode 100644 index 000000000..71c0234f6 --- /dev/null +++ b/guide/blockchain/filters.html @@ -0,0 +1,34 @@ + + + + + + Filters | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Filters

    Iroha uses filter-map paradigm to monitor events. Let's look at different types of filters that can be used in Iroha.

    Data Filters

    A data filter is a tuple with a single variant, which is a FilterOpt of an EntityFilter:

    FilterOpt stands for Optional Filter. It can either AcceptAll or accept BySome of another Filter. An EntityFilter is a filter that matches events produced by a certain type entity, e.g. by account or domain.

    Here is the list of EntityFilters in Iroha:

    + + + + \ No newline at end of file diff --git a/guide/blockchain/how-iroha-works.html b/guide/blockchain/how-iroha-works.html new file mode 100644 index 000000000..4d8b79b4e --- /dev/null +++ b/guide/blockchain/how-iroha-works.html @@ -0,0 +1,34 @@ + + + + + + How Iroha works | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    How Iroha works

    To understand how Iroha operates, let's draw parallels between a blockchain and a computer. If the blockchain is the computer, then in this metaphor of ours the client binary (for example: iroha_client_cli) is the keyboard, the blockchain is the hard drive, and the Iroha peer software is the processor. Like a processor, Iroha accepts portable instructions that modify what's written to the blockchain, allow certain users to use the network, and lock others out.

    Any operation that is run on-chain is written in terms of Iroha Special Instructions (ISI), and there is no other way of modifying the world state.

    Each interaction with the blockchain is done via a transaction. A transaction is a collection of instructions, which are either glued together in sequence or compiled into what we affectionately call a WASM blob. You need these instructions to register an account, remove an account, add X amount of Y currency, and so on.

    To read the information encoded in the blocks of a blockchain (the current world state), you use queries. Queries are submitted like instructions, but they're not tracked and recorded in blocks, so they're much more lightweight. If you use queries as part of complicated logic (e.g. inside WASM), they have a non-negligible impact on the size of the blocks. Queries that are only used to get information leave no trace in the blockchain.

    + + + + \ No newline at end of file diff --git a/guide/blockchain/instructions.html b/guide/blockchain/instructions.html new file mode 100644 index 000000000..37efc8976 --- /dev/null +++ b/guide/blockchain/instructions.html @@ -0,0 +1,34 @@ + + + + + + Iroha Special Instructions | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Iroha Special Instructions

    When we spoke about how Iroha operates, we said that Iroha Special Instructions are the only way to modify the world state. So, what kind of special instructions do we have? If you've read the language-specific guides in this tutorial, you've already seen a couple of instructions: Register<Account> and Mint<Quantity>.

    Here is the full list of Iroha Special Instructions:

    InstructionDescriptions
    Register/UnregisterGive an ID to a new entity on the blockchain.
    Mint/BurnMint/burn assets, triggers, or permission tokens.
    SetKeyValue/RemoveKeyValueUpdate blockchain object metadata.
    NewParameter/SetParameterCreate/set a chain-wide config parameter.
    Grant/RevokeGive or remove certain permissions from accounts.
    TransferTransfer assets between accounts.
    ExecuteTriggerExecute triggers.
    If, Pair, SequenceUse to create composite instructions.

    Let's start with a summary of Iroha Special Instructions; what objects each instruction can be called for and what instructions are available for each object.

    Summary

    For each instruction, there is a list of objects on which this instruction can be run on. For example, only assets can be transferred, while minting can refer to assets, triggers, and permission tokens.

    Some instructions require a destination to be specified. For example, if you transfer assets, you always need to specify to which account you are transferring them. On the other hand, when you are registering something, all you need is the object that you want to register.

    InstructionObjectsDestination
    Register/Unregisteraccounts, domains, asset definitions, triggers, roles, peers
    Mint/Burnassets, triggers (trigger repetitions), permission tokensaccounts
    SetKeyValue/RemoveKeyValueany objects that have metadata: accounts, domains, assets, asset definitions, triggers, transactions
    NewParameter/SetParameterIroha configuration parameters
    Grant/Revokeroles, permission tokensaccounts
    Transferassetsaccounts
    ExecuteTriggertriggers
    If, Pair, Sequenceany instructions

    There is also another way of looking at ISI, i.e. in terms of the target of each instruction. For example, when you register an account, you do so within a certain domain. This means that the target of the Register<Account> instruction would be the domain within which it is being registered.

    TargetInstructions
    Account(un)register assets, mint/burn account public key, mint/burn account signature condition check, update account metadata, grant/revoke a permission token, grant/revoke role
    Domain(un)register accounts, (un)register asset definitions, update asset metadata, update domain metadata
    Assetupdate metadata, mint/burn, transfer
    Trigger(un)register, mint/burn trigger repetitions, execute trigger
    World(un)register domains, peers, roles

    (Un)Register

    Registering and unregistering are the instructions used to give an ID to a new entity on the blockchain.

    Everything that can be registered is both Registrable and Identifiable, but not everything that's Identifiable is Registrable. Most things are registered directly, like Peers, but in some cases the representation in the blockchain has considerably more data. For security and performance reasons, we use builders for such data structures (e.g. NewAccount). As a rule, everything that can be registered, can also be un-registered, but that is not a hard and fast rule.

    You can register domains, accounts, asset definitions, peers, roles, and triggers. Check our naming conventions to learn about the restrictions put on entity names.

    INFO

    Note that depending on how you decide to set up your genesis block in genesis.json (specifically, whether or not you include registration of permission tokens), the process for registering an account can be very different. In general, we can summarise it like this:

    • In a public blockchain, anyone should be able to register an account.
    • In a private blockchain, there can be a unique process for registering accounts. In a typical private blockchain, i.e. a blockchain without any unique processes for registering accounts, you need an account to register another account.

    We discuss these differences in great detail when we compare private and public blockchains.

    INFO

    Registering a peer is currently the only way of adding peers that were not part of the original TRUSTED_PEERS array to the network.

    Refer to one of the language-specific guides to walk you through the process of registering objects in a blockchain:

    LanguageGuide
    BashRegister a domain, an account, an asset
    RustRegister a domain, an account, an asset
    Kotlin/JavaRegister a domain, an account, an asset
    PythonRegister a domain, an account, an asset
    JavaScript/TypeScriptRegister a domain, an account, an asset

    Mint/Burn

    Minting and burning can refer to assets, triggers (if the trigger has a limited number of repetitions), and temporary permission tokens. Some assets can be declared as non-mintable, meaning that they can be minted only once after registration.

    Assets and permission tokens need to be minted to a specific account, usually the one that registered the asset in the first place. All assets are assumed to be non-negative as well, so you can never have of time or Burn a negative amount and get a Mint.

    Refer to one of the language-specific guides to walk you through the process of minting assets in a blockchain:

    Here are examples of burning assets:

    Transfer

    Similar to mint and burn instructions, transferring refers to assets. You can transfer assets between different accounts.

    To do this, an account have to be granted the permission to transfer assets. Refer to an example on how to transfer assets in Bash.

    Grant/Revoke

    Grant and revoke instructions are used for account permissions and roles.

    Grant is used to permanently grant a user either a single permission, or a group of permissions (a "role"). Granted roles and permissions can only be removed via the Revoke instruction. As such, these instructions should be used carefully.

    SetKeyValue/RemoveKeyValue

    These instructions are used with the key/value Store asset type. This use case has not received much attention so far, because storing data in the blockchain is a rather advanced topic that we shall cover separately.

    NewParameter/SetParameter

    With these instructions, you can create (NewParameter) and change (SetParameter) chain-wide configuration parameters for Iroha.

    ExecuteTrigger

    This instruction is used to execute triggers.

    Composite instructions

    Iroha also offers composite instructions (If, Pair, Sequence) to execute instructions in a certain way:

    • If: execute one of the two given instructions based on a given condition
    • Sequence: execute a provided vector of instructions in a given order
    • Pair: execute both provided instructions in a specified order
    + + + + \ No newline at end of file diff --git a/guide/blockchain/metadata.html b/guide/blockchain/metadata.html new file mode 100644 index 000000000..893ae2e76 --- /dev/null +++ b/guide/blockchain/metadata.html @@ -0,0 +1,152 @@ + + + + + + Metadata | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content
    + + + + \ No newline at end of file diff --git a/guide/blockchain/permissions.html b/guide/blockchain/permissions.html new file mode 100644 index 000000000..65a387500 --- /dev/null +++ b/guide/blockchain/permissions.html @@ -0,0 +1,62 @@ + + + + + + Permissions | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Permissions

    Accounts need permission tokens for various actions on a blockchain, e.g. to mint or burn assets.

    There is a difference between a public and a private blockchain in terms of permissions granted to users. In a public blockchain, most accounts have the same set of permissions. In a private blockchain, most accounts are assumed not to be able to do anything outside of their own account or domain unless explicitly granted said permission.

    Having a permission to do something means having a PermissionToken to do so. There are two ways for users to receive permission tokens: they can be granted directly or as a part of a Role (a set of permission tokens). Permissions are granted via Grant special instruction. Permission tokens and roles do not expire, they can only be removed using Revoke instruction.

    Permission Tokens

    Permission token definitions have parameters. When a new permission token is registered, the names of the parameters and their types are checked against their names and types in the token definition. The token registration fails if there are too few parameters, if the parameter types don't match the definition, or parameters with unrecognised names.

    Here are some examples of parameters used for various permission tokens:

    • A token that grants permission to change the values associated to keys in a Store asset needs the asset_definition_id parameter of type Id:

      json
      "params": {
      +     "asset_definition_id": "Id"
      +}
      "params": {
      +     "asset_definition_id": "Id"
      +}
    • By contrast, the permission token that grants the permission to set keys to values in user metadata needs the account_id parameter of type Id:

      json
      "params": {
      +  "account_id": "Id"
      +}
      "params": {
      +  "account_id": "Id"
      +}
    • The permission token that grants the permission to transfer assets only a fixed number of times per some time period, needs these two parameters:

      json
      "params": {
      +  "count": "U32",
      +  "period": "U128"
      +}
      "params": {
      +  "count": "U32",
      +  "period": "U128"
      +}

      Where the period is specified as a standard Duration since the UNIX epoch in milliseconds (more details about time in Rust).

    Pre-configured Permission Tokens

    You can find the list of pre-configured permission tokens in the Reference chapter.

    Permission Groups (Roles)

    A set of permissions is called a role. Similarly to permission tokens, roles can be granted using the Grant instruction and revoked using the Revoke instruction.

    Before granting a role to an account, the role should be registered first.

    Register a new role

    Let's register a new role that, when granted, will allow another account access to the metadata in Mouse's account:

    rust
    let role_id = RoleId::from_str("ACCESS_TO_MOUSE_METADATA")?;
    +let role = iroha_data_model::role::Role::new(role_id)
    +    .add_permission(CanSetKeyValueInUserMetadata::new(mouse_id))
    +    .add_permission(CanRemoveKeyValueInUserMetadata::new(mouse_id));
    +let register_role = RegisterBox::new(role);
    let role_id = RoleId::from_str("ACCESS_TO_MOUSE_METADATA")?;
    +let role = iroha_data_model::role::Role::new(role_id)
    +    .add_permission(CanSetKeyValueInUserMetadata::new(mouse_id))
    +    .add_permission(CanRemoveKeyValueInUserMetadata::new(mouse_id));
    +let register_role = RegisterBox::new(role);

    Grant a role

    After the role is registered, Mouse can grant it to Alice:

    rust
    let grant_role = GrantBox::new(role_id, alice_id);
    +let grant_role_tx =
    +    Transaction::new(mouse_id, vec![grant_role.into()].into(), 100_000)
    +    .sign(mouse_key_pair)?;
    let grant_role = GrantBox::new(role_id, alice_id);
    +let grant_role_tx =
    +    Transaction::new(mouse_id, vec![grant_role.into()].into(), 100_000)
    +    .sign(mouse_key_pair)?;

    Permission Validators

    Permissions exist so that only those accounts that have a required permission token to perform a certain action could do so.

    The Judge trait is used to check permissions. The Judge decides whether a certain operation (instruction, query, or expression) could be performed based on the verdicts of multiple validators.

    Each validator returns one of the following verdicts: Deny (with the exact reason to deny an operation), Skip (if an operation is not supported or has no meaning in a given context), or Allow.

    There are several implementations of the Judge trait in Iroha 2, such as:

    JudgeDescription
    AtLeastOneAllowThe judge that succeeds only if there is at least one Allow verdict. The execution is stopped once there is a first Allow verdict.
    NoDeniesThe judge that succeeds only if there is no Deny verdict. All validators are checked.
    NoDeniesAndAtLeastOneAllowThe judge that succeeds only if there is no Deny verdict and at least one Allow verdict. The execution is stopped once there is a Deny verdict or all validators were checked.
    AllowAllFor tests and simple cases. All operations are allowed to be executed for all possible values.
    DenyAllFor tests and simple cases. All operations are disallowed to be executed for all possible for all possible values.

    You can also build a custom permission validator by combining multiple validators, all of which should be of the same type (for checking instructions, queries, or expressions).

    Runtime Validators

    Currently Iroha 2 has only built-in validators. In the future, built-in validators will be completely replaced with runtime validators that use WASM.

    The chain of runtime validators is used to validate operations that require permissions. It works similarly to the Chain of responsibility.

    All runtime validators return validation verdict. By default, all operations are considered valid unless proven otherwise. Validators check whether or not an operation is not allowed: each validator either allows an operation and passes it to the following validator, or denies the operation. The validation stops at the first Deny verdict.

    Supported Queries

    Permission tokens and roles can be queried.

    Queries for roles:

    Queries for permission tokens:

    + + + + \ No newline at end of file diff --git a/guide/blockchain/queries.html b/guide/blockchain/queries.html new file mode 100644 index 000000000..560d7ef63 --- /dev/null +++ b/guide/blockchain/queries.html @@ -0,0 +1,46 @@ + + + + + + Queries | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Queries

    Although much of the information about the state of the blockchain can be obtained, as we've shown before, using an event subscriber and a filter to narrow the scope of the events to those of interest, sometimes you need to take a more direct approach. Enter queries.

    Queries are small instruction-like objects that, when sent to an Iroha peer, prompt a response with details from the current world state view.

    This is not necessarily the only kind of information that is available on the network, but it's the only kind of information that is guaranteed to be accessible on all networks.

    For each deployment of Iroha, there might be other available information. For example, the availability of telemetry data is up to the network administrators. It's entirely their decision whether or not they want to allocate processing power to track the work instead of using it to do the actual work. By contrast, some functions are always required, e.g. having access to your account balance.

    The results of queries can be sorted, paginated and filtered peer-side all at once. Sorting is done lexicographically on metadata keys. Filtering can be done on a variety of principles, from domain-specific (individual IP address filter masks) to sub-string methods like begins_with combined using logical operations.

    Create a query

    Use QueryBox to construct a query. For example, a query to find all accounts would be created like this:

    rust
    let query = QueryBox::FindAllAccounts(FindAllAccounts {});
    let query = QueryBox::FindAllAccounts(FindAllAccounts {});

    Here is an example of a query that finds Alice's assets:

    rust
    let alice_id =
    +    AccountId::from_str("alice@wonderland")?;
    +let query = QueryBox::FindAssetsByAccountId(
    +    FindAssetsByAccountId::new(alice_id)
    +  );
    let alice_id =
    +    AccountId::from_str("alice@wonderland")?;
    +let query = QueryBox::FindAssetsByAccountId(
    +    FindAssetsByAccountId::new(alice_id)
    +  );

    Pagination

    For both a Vec<Z> and just Z as the return type, you can use client.request(query) to submit a query and get the full result in one go.

    However, some queries, particularly the ones with "All" in their names, can return exorbitant amounts of data. As such, we highly recommend you consider pagination to reduce the load on the system.

    To construct a Pagination, you need to call client.request_with_pagination(query, pagination), where the pagination is constructed as follows:

    rust
    let starting_result: u32 = _;
    +let limit: u32 = _;
    +let pagination = Pagination::new(Some(starting_result), Some(limit));
    let starting_result: u32 = _;
    +let limit: u32 = _;
    +let pagination = Pagination::new(Some(starting_result), Some(limit));

    Filters

    When you create a query, you can use a filter to only return the results that match the specified filter.

    Sorting

    Iroha 2 can sort items with metadata lexicographically if you provide a key to sort by during the construction of the query. A typical use case is for accounts to have a registered-on metadata entry, which, when sorted, allows you to view the account registration history.

    Sorting only applies to entities that have metadata, as the metadata key is used to sort query results.

    You can combine sorting with pagination and filters. Note that sorting is an optional feature, most queries with pagination won't need it.

    Reference

    Check the list of existing queries for detailed information about them.

    + + + + \ No newline at end of file diff --git a/guide/blockchain/transactions.html b/guide/blockchain/transactions.html new file mode 100644 index 000000000..0bb3f084c --- /dev/null +++ b/guide/blockchain/transactions.html @@ -0,0 +1,40 @@ + + + + + + Transactions | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Transactions

    A transaction is a collection of instructions. The instructions within a transaction can be executed in a sequence or compiled into a WASM blob.

    All interactions in the blockchain are done via transactions.

    Here is an example of creating a new transaction with the Grant instruction. In this transaction, Mouse is granting Alice the specified role (role_id). Check the full example.

    rust
    let grant_role = GrantBox::new(role_id, alice_id);
    +let grant_role_tx =
    +    Transaction::new(mouse_id, vec![grant_role.into()].into(), 100_000)
    +    .sign(mouse_key_pair)?;
    let grant_role = GrantBox::new(role_id, alice_id);
    +let grant_role_tx =
    +    Transaction::new(mouse_id, vec![grant_role.into()].into(), 100_000)
    +    .sign(mouse_key_pair)?;
    + + + + \ No newline at end of file diff --git a/guide/blockchain/triggers.html b/guide/blockchain/triggers.html new file mode 100644 index 000000000..42a51851e --- /dev/null +++ b/guide/blockchain/triggers.html @@ -0,0 +1,124 @@ + + + + + + Triggers | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Triggers

    Certain things, such as changing the state of an entity, committing a block or executing an Iroha Special Instruction (ISI), can emit events, and you can attach triggers to these events.

    A trigger is a fairly basic entity that can be registered. Just like with Accounts, to register a trigger, you submit a RegisterBox::Trigger, which contains the necessary information:

    • an account ID, which should ideally be a brand new account that you register in the same transaction
    • an executable, which itself is either a Vec<Instruction> or a WASM blob
    • an EventFilter[1], which is something that combs through all[2] events and returns true when it finds the matching event to start the execution

    Let's take a closer look at how triggers work.

    The Anatomy of a Trigger

    A trigger has roughly the following form:

    rust
    struct Trigger {
    +  id: TriggerId,
    +  action: Action,
    +}
    struct Trigger {
    +  id: TriggerId,
    +  action: Action,
    +}

    Trigger.id

    The TriggerId is a simple wrapper around a single Name, a string with no whitespaces and no reserved characters (@, #, $).

    A typical domain-scoped trigger looks like trigger_id$domain_name, while a bare trigger looks like @@trigger_id, which makes these names easy to parse.

    Trigger.action

    An Action is the heart of the trigger. It is defined like this:

    rust
    struct Action {
    +  executable: Executable,
    +  repeats: Repeats,
    +  technical_account: AccountId,
    +  filter: EventFilter,
    +  metadata: Metadata,
    +}
    struct Action {
    +  executable: Executable,
    +  repeats: Repeats,
    +  technical_account: AccountId,
    +  filter: EventFilter,
    +  metadata: Metadata,
    +}

    Action.executable

    The executable linked to this action, either a Vec<Instruction> or a WASM binary.

    Action.repeats

    The Repeats is a universal enumeration of all possible repetition schemes.

    rust
    enum Repeats {
    +  Indefinitely,
    +  Exactly(u32),
    +}
    enum Repeats {
    +  Indefinitely,
    +  Exactly(u32),
    +}

    Action.technical_account

    A technical account is the account that would (in theory) be responsible for the execution environment and be the authority for Instruction execution.

    For now, you can leave this as the account that registered the trigger. If you have been following the tutorial, this is alice@wonderland. However, later on we will show you why you'd want to create a brand new account for those purposes.

    INFO

    Note that you can only use the account that already exists in order to be able to register a new trigger.

    Action.filter

    A filter is what determines what kind of trigger you're dealing with. All triggers respond to events, but the precise type of event that activates a trigger depends on which EventFilter was used.

    The reason why we chose this architecture is simple; front end code has an abundance of event filters, and so, your knowledge of filters is transferable to writing smart contracts.

    Action.metadata

    This Metadata is the same kind of Metadata that can be attached to accounts, domains, assets, or transactions. This is the storage for trigger data.

    You can learn more about metadata in a dedicated section.

    How Triggers Work

    As we already said, the filter that is used to register a trigger determines what kind of trigger this is. It is, of course, also determines how the trigger works, e.g. when it is executed. We will go into more details about the types of triggers in just a moment.

    First, we shall point out that there two other characteristics of a trigger that determine how this trigger works: its scope and repetition schema.

    Scope

    Triggers can be scoped and un-scoped. Iroha supports both un-scoped system-wide triggers as well as domain-scoped triggers. Since system-wide triggers scan all events, and domain-scoped triggers only scan events emitted in a certain domain, it is highly recommended to use domain-scoped triggers where possible.

    INFO

    Be mindful of the limitations. Currently triggers don't check for permissions , so they can do things your account wouldn't be allowed to. Un-scoped triggers process every event, and the amount of work grows quadratically.

    Domain-scoped Triggers

    While un-scoped triggers check all events of a specified type, domain-scoped triggers only look for events in a given domain. These triggers are more optimised compared to un-scoped triggers.

    You can use FindTriggersByDomainId query to find triggers for the given domain.

    When you register a domain-scoped trigger, you need to add the domain id to the trigger id using $ symbol: my_trigger$my_domain.

    Repetition Schema

    Each such trigger can be set to repeat either Indefinitely or Exactly(n) times, where n is a 32-bit integer. Once the number of repetitions reaches zero, the trigger is gone. That means that if your trigger got repeated exactly n times, you can't Mint new repetitions, you have to Register it again, with the same name.

    After a trigger is repeated for the last time, i.e. the execution count reaches 0, the trigger should be un-registered.

    Types of Triggers

    We shall cover the following basic types of triggers and provide you with the detailed information on how to use each of them:

    All triggers are essentially event triggers. The type of a trigger is determined by the type of an event that trigger is associated with. This, in turn, is determined by the filter used to register a trigger.

    Data Triggers

    This category includes the largest variety of triggers. The events that are associated with this trigger type account for the vast majority of events in Ethereum. These are data-related events, such as: an account got registered, an asset got transferred, the Queen of Hearts decided to burn all of her assets.

    Time Triggers

    Time triggers behave slightly differently compared to data triggers. There are two sub-types of this type: scheduled triggers and pre-commit triggers.

    Instead of processing all the events generated by normal transactions, all time triggers process one event: the block formation event.

    The filters of scheduled triggers are only interested in the timestamp provided in that event, but not the block height, and not the current time. They are executed according to a certain schedule. Pre-commit triggers, on the other hand, are executed right before a block is committed.

    Scheduled Triggers

    When going through consensus, all peers must agree on which triggers got executed. Scheduled triggers can't use real time, because then you can easily create a situation when they would never agree: e.g. by giving the Repeats::Indefinitely trigger a period that is smaller than the time it takes to pass consensus. It's really that simple.

    So instead of using the actual current time at each peer, we use the time when the block got started plus a small offset. All triggers before that point in time get executed. All triggers that would be executed after that time wait for the next block.

    Why we use the offset

    The reason why we add this offset has to do with Iroha being best effort.

    Imagine if we didn't have the offset... Normally, triggers would be set to nice round numbers; e.g. 12:00, 12:05, 11:55, etc. (as opposed to e.g. 11:59). However, the consensus can start at any point in time and could last a while.

    Suppose that the block started to form at 11:56 and consensus finished at 12:03 (which is optimistically quick). Let's consider different scenarios:

    • If your trigger was supposed to run at 11:55, you'd be happy, since your trigger got executed just 1 minute late.
    • If your trigger was supposed to run at 12:05, it will run in the next block, not the one that was formed at 11:56. If you're the author and you're looking at the time stamp of 12:03, it makes sense, your trigger wasn't supposed to run yet.
    • For the trigger scheduled for 12:00, the situation is different. You look at the clock, you see 12:03, which is when the blockchain explorer shows you the block data was committed, but you don't see your trigger. It was supposed to run, but didn't.

    So, the offset is meant to anticipate when the block would get added to the chain, so that people who were just 4 minutes early don't have to be potentially several hours late.

    Because more triggers get executed sooner, your throughput is also infinitesimally smaller.

    We could also say "you should aim to execute your trigger slightly earlier than consensus starts", but people writing smart contracts already have too much to worry about.

    Pre-commit Triggers

    This is a variant of timed triggers that gets run before blocks with transactions get committed. It leaves a special event to be triggered in the next block. Effectively, it's a delayed pre-commit that can track the behaviour of transactions in the pipeline.

    INFO

    These triggers are not meant for restricting the execution of transactions.

    If you want to stop your users from transferring more than X amount of Y to user Z, you really want a permission. While you could hack the pre-commit triggers to emulate the desired behaviour, this is not economical neither in terms of gas fees nor computation.

    Until Iroha 2 supports WASM-based permissions validators, however, your only choice is pre-commit triggers.

    By-call Triggers

    These triggers only get executed once the CallTrigger(trigger_name) instruction is executed. They can be useful if you want to achieve dynamic linkage between different smart contract modules.

    Space is precious, so you want to use as little of it as you can. Thus, you follow the UNIX design philosophy, and instead of creating one large smart contract, you create many smaller ones, and re-use as much logic as you can.

    INFO

    Of course, this is a rather exotic use case, so it shall be implemented last.

    Event Triggers by Example

    Now that we've gotten the theory out of the way, we want to sit down with the Mad Hatter, the March Hare, and the Dormouse and see if we can spin. Let's start with an event trigger that shows the basics.

    1. Register accounts

    We have mad_hatter@wonderland, dormouse@wonderland, march_hare@wonderland all of which share the fixed-point asset of tea#wonderland. The Mad Hatter has the tea pot, while the rest have a single cup of tea. When alice@wonderland had arrived, she got a nice cup of tea as well.

    The way we get them in Rust code looks like this:

    rust
    let tea = AssetDefinitionId::new("tea", "wonderland")?;
    +let mad_hatter = AccountId::new("mad_hatter", "wonderland")?;
    +let dormouse = AccountId::new("dormouse", "wonderland")?;
    +let march_hare = AccountId::new("march_hare", "wonderland")?;
    +vec![
    +  RegisterBox::new(IdentifiableBox::from(NewAccount::new(mad_hatter.clone()))),
    +  RegisterBox::new(IdentifiableBox::from(NewAccount::new(march_hare.clone()))),
    +  RegisterBox::new(IdentifiableBox::from(NewAccount::new(dormouse.clone()))),
    +  RegisterBox::new(IdentifiableBox::from(AssetDefinition::new_fixed(tea.clone()))),
    +  MintBox::new(Value::Fixed(100.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), mad_hatter.clone())))
    +  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), march_hare.clone())))
    +  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), dormouse.clone())))
    +  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), alice.clone())))
    +]
    let tea = AssetDefinitionId::new("tea", "wonderland")?;
    +let mad_hatter = AccountId::new("mad_hatter", "wonderland")?;
    +let dormouse = AccountId::new("dormouse", "wonderland")?;
    +let march_hare = AccountId::new("march_hare", "wonderland")?;
    +vec![
    +  RegisterBox::new(IdentifiableBox::from(NewAccount::new(mad_hatter.clone()))),
    +  RegisterBox::new(IdentifiableBox::from(NewAccount::new(march_hare.clone()))),
    +  RegisterBox::new(IdentifiableBox::from(NewAccount::new(dormouse.clone()))),
    +  RegisterBox::new(IdentifiableBox::from(AssetDefinition::new_fixed(tea.clone()))),
    +  MintBox::new(Value::Fixed(100.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), mad_hatter.clone())))
    +  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), march_hare.clone())))
    +  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), dormouse.clone())))
    +  MintBox::new(Value::Fixed(1.0_f64.try_into()?), IdBox::AssetId(AssetId::new(tea.clone(), alice.clone())))
    +]

    2. Register a trigger

    We want a smart contract that transfers some tea from mad_hatter@wonderland to alice@wonderland when her tea reduces by a single cup.

    For that we need to register a trigger. The boilerplate is straightforward:

    rust
    let id = TriggerId::new(Name::new("refresh_tea"));
    +
    +let metadata = Metadata::new();
    +
    +let executable = vec![
    +    TransferBox::new(
    +      IdBox::AssetId(AssetId::new(tea.clone(), mad_hatter.clone())),
    +      Value::Fixed(1_f64.try_into()?),
    +      IdBox::AssetId(AssetId::new(alice.clone(), mad_hatter.clone())),
    +    )
    +];
    +
    +let repeats = Repeats::Indefinitely;
    +
    +let technical_account = mad_hatter.clone();
    +
    +let filter = _ // ...
    let id = TriggerId::new(Name::new("refresh_tea"));
    +
    +let metadata = Metadata::new();
    +
    +let executable = vec![
    +    TransferBox::new(
    +      IdBox::AssetId(AssetId::new(tea.clone(), mad_hatter.clone())),
    +      Value::Fixed(1_f64.try_into()?),
    +      IdBox::AssetId(AssetId::new(alice.clone(), mad_hatter.clone())),
    +    )
    +];
    +
    +let repeats = Repeats::Indefinitely;
    +
    +let technical_account = mad_hatter.clone();
    +
    +let filter = _ // ...

    3. Define an event filter

    The event filter is where we need to spend some time and think. So far we've seen the Pipeline variety of filters. This time around, the filter is a Data kind. This type of filter is a tuple with a single variant, which is a FilterOpt of an EntityFilter:

    • FilterOpt stands for Optional Filter. It can either AcceptAll or accept BySome of another Filter.
    • An EntityFilter is a filter that matches ByAccount in our case, but can match by many other means. It wraps an AccountFilter, which matches various events produced on accounts.

    What we want to do is create an event filter for when alice@wonderland drinks some of her tea, or, in other words, reduces the tea asset by any amount. To do this with the current API, we need to work bottom up.

    An IdFilter is a filter that .matches(event) == true if and only if the identities are exactly the same. Everything that has an Id has a corresponding IdFilter.

    INFO

    An IdFilter is a parametric structure, an IdFilter that works on Peers has the type IdFilter<PeerId> and is not the same type as an IdFilter that works with AccountId; IdFilter<AccountId.

    Now if we wanted a filter that will match whenever tea gets reduced, either through a Transfer or a Burn instruction, we need an AssetFilter. It needs to look at what the Id of the asset is, hence IdFilter<AssetDefinitionId> and ByRemoved.

    rust
    use FilterOpt::{BySome, AcceptAll};
    +
    +let asset_filter = AssetFilter::new(BySome(IdFilter(tea.clone())), BySome(AssetEventFilter::ByRemoved));
    use FilterOpt::{BySome, AcceptAll};
    +
    +let asset_filter = AssetFilter::new(BySome(IdFilter(tea.clone())), BySome(AssetEventFilter::ByRemoved));

    So far so good?

    Next, we want a filter that looks for changes in an asset for an account. Specifically:

    rust
    let account_filter = AccountFilter::new(BySome(IdFilter(alice.clone())), BySome(asset_filter));
    let account_filter = AccountFilter::new(BySome(IdFilter(alice.clone())), BySome(asset_filter));

    Now, because of the way that parity_scale_codec works, we need to wrap this in various boxes.

    rust
    let filter = EventFilter::Data(BySome(EntityFilter::ByAccount(account_filter)));
    let filter = EventFilter::Data(BySome(EntityFilter::ByAccount(account_filter)));

    4. Create a Trigger instance

    After this somewhat laborious filter combination, we can create an Action

    rust
    let action = Action {
    +    executable, repeats, technical_account, filter, metadata
    +}
    let action = Action {
    +    executable, repeats, technical_account, filter, metadata
    +}

    Which allows us to create an instance of a Trigger.

    rust
    let trigger = Trigger::new(id, action);
    let trigger = Trigger::new(id, action);

    5. Create a transaction

    Finally, in order to get said trigger onto the blockchain, we create a transaction with the following single instruction:

    rust
    Instruction::Register(RegisterBox::new(IdentifiableBox::Trigger(Box::new(trigger))));
    Instruction::Register(RegisterBox::new(IdentifiableBox::Trigger(Box::new(trigger))));

    How it works

    The technical details of the created transaction are summarised as follows:

    • The (normal) instructions that either got submitted from WASM or directly from the client get executed. If there were any triggers that should have been registered, they get registered.
    • Using the total set of events that got generated during the execution of instructions, the triggers (including some that got registered just this round) get executed.
    • The events produced in the previous step get scheduled for execution in the next block.

    INFO

    The reason why the events caused by the execution of triggers get processed in the next block is that we don't want two triggers to inadvertently cause an infinite loop of instruction execution and break consensus.

    Now each time Alice drinks some tea, the Mad Hatter pours in a whole new cup. The keen eyed among you will have noticed that the amount that Alice drinks is irrelevant to how much tea will be transferred. Alice may take a tiny sip and still be poured a whole new cup's worth.

    INFO

    We intend to address this issue in the future so that an emitted event also has an attached Value. We also intend to provide more event filter types. For example, we will have filters that match when the asset:

    • Decreases by any amount (current behaviour)
    • Decreases by more than (or exactly) the specific amount in one instruction
    • Decreases to below a certain threshold

    Only the first type of event filter is implemented now, and the other two can be emulated using a WASM smart contract as the Executable.

    Why not WASM

    The above observation can be generalised. WASM can do any logic that a Turing complete machine could, using the data available via queries. So in theory for event-based triggers, you could create an AcceptAll event filter and do all of the processing using the key-value store as persistent storage, and then, determining if you want to execute using easy-to-understand Rust code, and not our admittedly cumbersome, EventFilters.

    We don't want that. WASM takes up significantly more space, and takes longer to execute compared to plain ISI, which are slower than EventFilters. We want you to want to use the EventFilters because they would make the process much more efficient, and we are working tirelessly to make the experience of using event filters much less gruelling.

    However, as was mentioned previously on several occasions, implementing a feature properly takes time and effort. Ergonomics must be balanced against safety and reliability, so we cannot just make things easier to use. We want them to retain many of the advantages of strong typing.

    This is all a work in progress. Our code is in flux. We need time to play around with a particular implementation to optimise it.

    Supported ISI

    All Iroha Special Instructions work with triggers, specifically:

    • Register<Trigger>: Create a trigger object and subscribe it to global events.

    • Unregister<Trigger>: Remove a trigger from the World State View and stop passing events through it.

    • Mint<Trigger, u32>: For triggers that repeat a certain number of times, increase the number of times that the trigger gets executed. Can be done from inside the executable of the trigger.

    • Burn<Trigger, u32>: For triggers that repeat a certain number of times, decrease the number of times that the trigger gets executed.

      WARNING

      If the number provided is greater than the remaining number of repetitions, the instruction will fail to execute, and the transaction that it is part of will be rejected.

    You can learn more about Iroha Special Instructions in the dedicated section.

    Supported Queries

    We list supported queries for triggers when we talk in more detail about queries in the next chapter.


    1. The documentation on the EventFilter types is under construction, as we are likely to make major changes to that particular architecture. For now, suffice it to say that you can look at the source code in iroha_data_model and see a few particularly interesting applications. ↩︎

    2. This behaviour is likely to change in future releases.

      ↩︎
    + + + + \ No newline at end of file diff --git a/guide/blockchain/wasm.html b/guide/blockchain/wasm.html new file mode 100644 index 000000000..05f700201 --- /dev/null +++ b/guide/blockchain/wasm.html @@ -0,0 +1,106 @@ + + + + + + WASM | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    WASM

    While we had initially assumed that all operations within Iroha will be handled with instructions and conditionals, however, there are a few problems with this approach.

    • The ISI syntax is verbose and ugly.
    • The ISI syntax is not familiar for most programmers.
    • While simple ISI smart contracts are compact (usually a few bytes), they need different kinds of manual optimisations.

    In the long run, all of these problems are taken care of by using a domain-specific language, which gets optimised and compiled into a sequence of instructions that executes fast and takes very little space in the blocks, but is also easy to understand. Something that looks like your traditional if statements and for loops.

    However, in the interim, we have decided to use another portable binary standard called Web assembly, or WASM.

    Working with WASM

    The main advantage of using the WASM format is that you can use any language you like (as long as it links statically against our helper library), and produce a 32-bit portable executable. The compilers take care of the optimisation, and you don't have to learn a new language (ahem... solidity... ahem), just to operate on the blockchain.

    You'd still need to use ISI from inside your WASM binary to do anything on-chain, as we explained earlier.

    In theory, you can do anything you want just using ISI as it is a Turing-complete language. However, it'll be less convenient and efficient since you'd need to use metadata as memory and write complex conditionals using just the tools that we've provided in the Expression and ISI infrastructure. We highly recommend choosing a well-known programming language, such as Rust, to build the necessary logic out of simple instructions. This is much easier than trying to reinvent the wheel using ISI.

    Moreover, as long as you fit within the limits of WASM runtime and the provided libraries, you can do anything (and everything) you want. The drawback is that this process is a tad more involved than just writing the ISI using the client libraries.

    Simple Rust Smart Contract Example

    WASM projects, just like any other binary in Rust, need to be separate crates. Don't worry, it doesn't have to be big.

    1. Create a new project

    To get started, create a new project:

    bash
    $ cargo new --lib
    $ cargo new --lib

    Yes! We need the lib type; more on that later.

    The Cargo.toml of your project should look something like this:

    toml
    [package]
    +name  = "smartcontract"
    +version = "0.1.0"
    +edition = "2021"
    +
    +[lib]
    +crate-type = ['cdylib']
    +
    +[dependencies]
    +iroha_wasm = { git = "https://github.com/hyperledger/iroha/", branch = "iroha2-dev" }
    [package]
    +name  = "smartcontract"
    +version = "0.1.0"
    +edition = "2021"
    +
    +[lib]
    +crate-type = ['cdylib']
    +
    +[dependencies]
    +iroha_wasm = { git = "https://github.com/hyperledger/iroha/", branch = "iroha2-dev" }

    Note that the crate type is cdylib. Most Rust code is linked in a non-portable architecture and OS-specific static manner, but WASM is a portable format. Since C ABI is the lingua franca of the programming world and there is no other stable Rust ABI (yet), Iroha relies on the C-linkage to generate WASM bindings. Thankfully, iroha_wasm takes care of everything related to foreign function interfaces (FFI), so you don't have to worry about things like unsafe, repr(C), padding, alignment, and others.

    The iroha_wasm crate contains all of the bindings, macros, and trait implementations that you'd need to write the program, most notably the iroha_wasm attribute macro. The crate also exposes our data_model, which contains all of the basic ISI and types. The chosen serialisation format is parity-scale-codec, though there is a strong possibility it'll get replaced with a different (custom) serialisation format in the near future, as it seems to dominate the binary size[1].

    2. Write a smart contract

    Now that we have the preliminaries nailed down, we get to write some code for our smart contract. In the src/lib.rs you should write the following:

    rust
    #![no_std]
    +#![no_main]
    +
    +extern crate alloc;
    +
    +use alloc::vec::Vec;
    +
    +use iroha_wasm::data_model::prelude::*;
    +
    +#[iroha_wasm::iroha_wasm]
    +fn smartcontract_entry_point(_account_id: AccountId) {
    +    let query = QueryBox::FindAllDomains(FindAllDomains {});
    +    let domains: Vec<Domain> = query.execute().try_into().unwrap();
    +
    +    for domain in domains {
    +        let new_account_id = AccountId {
    +            name: Name::new("mad_hatter").unwrap(),
    +            domain_id: domain.id,
    +        };
    +
    +        Instruction::Register(RegisterBox::new(NewAccount::new(new_account_id))).execute();
    +    }
    +}
    #![no_std]
    +#![no_main]
    +
    +extern crate alloc;
    +
    +use alloc::vec::Vec;
    +
    +use iroha_wasm::data_model::prelude::*;
    +
    +#[iroha_wasm::iroha_wasm]
    +fn smartcontract_entry_point(_account_id: AccountId) {
    +    let query = QueryBox::FindAllDomains(FindAllDomains {});
    +    let domains: Vec<Domain> = query.execute().try_into().unwrap();
    +
    +    for domain in domains {
    +        let new_account_id = AccountId {
    +            name: Name::new("mad_hatter").unwrap(),
    +            domain_id: domain.id,
    +        };
    +
    +        Instruction::Register(RegisterBox::new(NewAccount::new(new_account_id))).execute();
    +    }
    +}

    To submit the instruction and run it, execute the following command (be sure to have a peer up):

    cargo run --release
    cargo run --release

    What does this smart contract do? Let's see. It queries all of the currently existing domains and puts the results into a std::vec::Vec. In this case, std::vec::Vec has to be imported from alloc, as we use no_std (more on that later). It is then used to add the user named mad_hatter to all of the existing domains.

    Building the same logic out of Expression and If and Sequence would be significantly harder. Moreover, the actual low-level instructions that would run are very likely not going to be as well-optimised as what the compiler produces.

    Advanced Smart Contracts: Optimising for Size

    WASM smart contracts can get big. So big, in fact, that we might not let you store them in the blockchain. So how do you reduce the size? The most important modifications are done in Cargo.toml:

    toml
    [profile.release]
    +strip = "debuginfo" # Remove debugging info from the binary
    +panic = "abort"     # Abort panics as they are transcribed to Traps when compiling for WASM anyways
    +lto = true          # Use link-time-optimisation (it produces a notable decrease in binary size)
    +opt-level = "z"     # Optimise for size vs speed with "s"/"z"(removes vectorization)
    +codegen-units = 1   # Use one code generation unit (it further reduces the binary size but increases compilation time)
    [profile.release]
    +strip = "debuginfo" # Remove debugging info from the binary
    +panic = "abort"     # Abort panics as they are transcribed to Traps when compiling for WASM anyways
    +lto = true          # Use link-time-optimisation (it produces a notable decrease in binary size)
    +opt-level = "z"     # Optimise for size vs speed with "s"/"z"(removes vectorization)
    +codegen-units = 1   # Use one code generation unit (it further reduces the binary size but increases compilation time)

    Let's take a closer look at what you can do to reduce the size of the WASM binary.

    Remove debugging info

    Rust stores a lot of debug information (even when compiled in release mode), which is (as the name suggests) used for debugging a panic in your Rust application. As you would expect, this information increases the size of the compiled WASM significantly.

    Normally, this would be a worthwhile trade-off, but not in our case. Firstly, since the WASM is stored on-chain, it will be permanently recorded in some block and take space on every full node of an Iroha network. Iroha stores a lot of its information in RAM, so storage space for WASM is at a premium.

    Secondly, once the WASM smart contract is stored on-chain, the debug information is no longer accessible. Indeed, you shouldn't debug on a peer. Instead, you should replicate the conditions that caused the panic locally and debug on your personal machine.

    Work under a no_std environment

    Another step that we've already taken involves working under a no_std environment. All of our size-related woes stem from Rust being predominantly statically linked. As such, breaking the standard library into more manageable crates, like using alloc::vec instead of std::vec, can help us reduce the size and compilation time[2].

    Re-compile libcore

    Next, you're advised to re-compile libcore and any other standard library crate (e.g. alloc) to exclude the leftover panic-related code that comes with the prebuilt core library[3]:

    bash
    $ cargo +nightly build -Z build-std -Z build-std-features=panic_immediate_abort --target wasm32-unknown-unknown
    $ cargo +nightly build -Z build-std -Z build-std-features=panic_immediate_abort --target wasm32-unknown-unknown

    Unfortunately, this is an unstable feature. In other words, the developers of the Rust programming language can decide to change how this works, or remove this option entirely.

    Use tools to optimise WASM size

    Finally, you can use an automated tool to optimise the size of the WASM binary. You could use wasm-opt or twiggy to guide your manual optimisation efforts.

    We highly advise using wasm-opt because it will often significantly reduce your binary size:

    bash
    $ wasm-opt -Os -o output.wasm input.wasm
    $ wasm-opt -Os -o output.wasm input.wasm

    Conclusion

    At some point, unfortunately, the smallest size of your WASM blob is going to be determined by the libraries that you need to use. Using all of the above steps on the provided smart contract can reduce it down to a manageable (for the blockchain) size.


    1. Size is an important metric. We shall cover size-optimisation strategies as we go. ↩︎

    2. It should be noted that excluding the standard library is necessary for compiling to the wasm32 target, and is thus mandatory. ↩︎

    3. wasm-opt can also be used to remove the debug sections. ↩︎

    + + + + \ No newline at end of file diff --git a/guide/blockchain/world.html b/guide/blockchain/world.html new file mode 100644 index 000000000..dc35a9400 --- /dev/null +++ b/guide/blockchain/world.html @@ -0,0 +1,34 @@ + + + + + + World | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    World

    World is the global entity that contains other entities. The World consists of:

    When domains, peers, or roles are registered or unregistered, the World is the target of the (un)register instruction.

    World State View (WSV)

    World State View is the in-memory representation of the current blockchain state. This includes all currently loaded blocks, with all of their contents, as well as peers elected for the current epoch.

    + + + + \ No newline at end of file diff --git a/guide/configure/client-configuration.html b/guide/configure/client-configuration.html new file mode 100644 index 000000000..c504076e8 --- /dev/null +++ b/guide/configure/client-configuration.html @@ -0,0 +1,88 @@ + + + + + + Client Configuration | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Client Configuration

    Let's look at the client configuration options.

    Client configuration example
    json
    {
    +  "PUBLIC_KEY": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
    +  "PRIVATE_KEY": {
    +    "digest_function": "ed25519",
    +    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
    +  },
    +  "ACCOUNT_ID": "alice@wonderland",
    +  "BASIC_AUTH": {
    +    "web_login": "mad_hatter",
    +    "password": "ilovetea"
    +  },
    +  "TORII_API_URL": "http://127.0.0.1:8080/",
    +  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180/",
    +  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
    +  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
    +  "TRANSACTION_LIMITS": {
    +    "max_instruction_number": 4096,
    +    "max_wasm_size_bytes": 4194304
    +  },
    +  "ADD_TRANSACTION_NONCE": false
    +}
    {
    +  "PUBLIC_KEY": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
    +  "PRIVATE_KEY": {
    +    "digest_function": "ed25519",
    +    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
    +  },
    +  "ACCOUNT_ID": "alice@wonderland",
    +  "BASIC_AUTH": {
    +    "web_login": "mad_hatter",
    +    "password": "ilovetea"
    +  },
    +  "TORII_API_URL": "http://127.0.0.1:8080/",
    +  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180/",
    +  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
    +  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
    +  "TRANSACTION_LIMITS": {
    +    "max_instruction_number": 4096,
    +    "max_wasm_size_bytes": 4194304
    +  },
    +  "ADD_TRANSACTION_NONCE": false
    +}

    Generation

    You can use kagami to generate the default client configuration:

    bash
    $ kagami config client > client-config.json
    $ kagami config client > client-config.json

    Public and Private Keys

    The configs/client_cli/config.json client configuration file should contain a pair of the user's public PUBLIC_KEY and private PRIVATE_KEY cryptographic keys for their account's ACCOUNT_ID.

    For details on cryptographic keys, see Public Key Cryptography.

    User account

    The ACCOUNT_ID should be self-explanatory. The only thing you need to worry about is that the account must already exist in the blockchain. In other words, the account you provide here should already be registered.

    Note

    Iroha is case-sensitive, meaning that Alice@wonderland is different from alice@wonderland. It should go without saying that alice@wonderland is not the same as alice@looking_glass either, since these accounts belong to different domains, wonderland and looking_glass.

    Basic Authentication Credentials

    The idea of basic authentication credentials is to provide the access control using a web server with a reverse proxy like Nginx while these credentials are ignored by the Iroha peers.

    The login and password will be filled by the client and used for the Authorization HTTP header.

    Use this style of configuration to provide the basic authentication credentials (login and password):

    json
    "BASIC_AUTH": {
    +    "web_login": "mad_hatter",
    +    "password": "ilovetea"
    +  },
    "BASIC_AUTH": {
    +    "web_login": "mad_hatter",
    +    "password": "ilovetea"
    +  },

    Iroha Public Addresses

    TORII is the module in charge of handling incoming and outgoing connections. For client configuration, you can set up two addresses: TORII_API_URL and TORII_TELEMETRY_URL.

    TORII_API_URL

    First, the TORII_API_URL is the same as TORII API_URL in the peer configuration. This is the module responsible for handling incoming and outgoing connections. You should also add the prefix http:// or (preferably) https:// to the address. For example:

    json
    "TORII_API_URL": "http://127.0.0.1:8080"
    "TORII_API_URL": "http://127.0.0.1:8080"

    TORII_TELEMETRY_URL

    The TORII_TELEMETRY_URL is used to specify the prometheus endpoint address. You can set TORII_TELEMETRY_URL like this:

    json
    "TORII_TELEMETRY_URL": "http://127.0.0.1:8180"
    "TORII_TELEMETRY_URL": "http://127.0.0.1:8180"

    A GET request to the 127.0.0.1:8180/status will give you a JSON-encoded representation of the top-level metrics, while a GET request to 127.0.0.1:8180/metrics will give you a (somewhat verbose) list of all available metrics gathered in Iroha. You might want to change this if you're having trouble gathering metrics using prometheus.

    INFO

    Learn how to monitor Iroha performance using prometheus.

    Transaction Limits

    You can specify the transaction limits that each transaction must adhere to: the maximum number of instructions and the maximum size of a WASM blob (in bytes). For example:

    json
    {
    +  "max_instruction_number": 4096,
    +  "max_wasm_size_bytes": 4194304
    +}
    {
    +  "max_instruction_number": 4096,
    +  "max_wasm_size_bytes": 4194304
    +}

    Transaction TTL and Timeout

    Configure the time-to-live (TTL) for transactions and the timeout to wait for the status. Both values have to be provided in milliseconds. For example:

    json
    "TRANSACTION_TIME_TO_LIVE_MS": 100000,
    +"TRANSACTION_STATUS_TIMEOUT_MS": 15000,
    "TRANSACTION_TIME_TO_LIVE_MS": 100000,
    +"TRANSACTION_STATUS_TIMEOUT_MS": 15000,

    Transaction Nonce

    If you set ADD_TRANSACTION_NONCE to true, Iroha will create different hashes for transactions that occur repeatedly and simultaneously.

    + + + + \ No newline at end of file diff --git a/guide/configure/configuration-types.html b/guide/configure/configuration-types.html new file mode 100644 index 000000000..959566708 --- /dev/null +++ b/guide/configure/configuration-types.html @@ -0,0 +1,34 @@ + + + + + + Configuration Types | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Configuration Types

    Configuration options have different underlying types and default values, which are denoted in code as types wrapped in a single Option<..> or in a double Option<Option<..>>.

    In this section we explain the difference between Option<..> and Option<Option<..>> used for configuration types. You can find more about available configuration options in peer configuration. The full list of available options is in Iroha Configuration Reference.

    Option<..>

    A type wrapped in a single Option<..> signifies that in the corresponding json block there is a fallback value for this type, and that it only serves as a reference.

    If a default for such a type has a null value, it means that

    there is no meaningful fallback available for this particular value. It doesn't mean that you can omit the value. Quite the opposite, it must be set manually, either in the configuration file, or via the environment variables.

    All the default values can be freely obtained from a provided sample configuration file, but it is only a starting point. **If left unchanged, the sample configuration file will not work. All null values in place of public and private keys as well as endpoint URLs should be provided either by modifying the sample config file or as environment variables. No other overloading of configuration values happens besides reading them from a file and capturing the environment variables, and environment variables take precedence.

    For both types of configuration options wrapped in a single Option<..> (i.e. both those that have meaningful defaults and those that have null), failure to provide them in any of the above two ways results in an error.

    Option<Option<..>>

    Option<Option<..>> types should be distinguished from types wrapped in a single Option<..>. Only the double option ones are allowed to stay null, meaning that not providing them in an environment variable or a file will not cause an error.

    Thus, only these types are truly optional in the common sense of the word. An example of this distinction is genesis public and private keys. While the first one is a single Option<..> wrapped type, the latter is wrapped in Option<Option<..>>. This means that the genesis public key should always be provided by the user, be it via a file config or an environment variable, whereas the private key is only needed for the peer that submits the genesis block, and can be omitted for all others. The same logic goes for other double option fields such as logger file path.

    Sumeragi: default null values

    A special note about Sumeragi fields with null as default: only the trusted_peers field out of the three can be initialized via a provided file or an environment variable.

    The other two fields, namely key_pair and peer_id, go through a process of finalization where their values are derived from the corresponding ones in the uppermost Iroha config (using its public_key and private_key fields) or the Torii config (via its p2p_addr). This ensures that these linked fields stay in sync, and prevents the programmer error when different values are provided to these field pairs. Providing either sumeragi.key_pair or sumeragi.peer_id by hand will result in an error, as it should never be done directly. In later versions these configuration options shall be hidden completely.

    + + + + \ No newline at end of file diff --git a/guide/configure/genesis.html b/guide/configure/genesis.html new file mode 100644 index 000000000..04116cda6 --- /dev/null +++ b/guide/configure/genesis.html @@ -0,0 +1,434 @@ + + + + + + Genesis Block | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Genesis Block

    The genesis block is the first block in your blockchain. It's never empty, even if configs/peer/genesis.json is. Here's an example:

    Genesis Block Example: alice@wonderland
    json
    {
    +  "transactions": [
    +    [
    +      {
    +        "Register": {
    +          "NewDomain": {
    +            "id": "wonderland",
    +            "logo": null,
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "alice@wonderland",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "bob@wonderland",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAssetDefinition": {
    +            "id": "rose#wonderland",
    +            "value_type": "Quantity",
    +            "mintable": "Infinitely",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewDomain": {
    +            "id": "garden_of_live_flowers",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "carpenter@garden_of_live_flowers",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAssetDefinition": {
    +            "id": "cabbage#garden_of_live_flowers",
    +            "value_type": "Quantity",
    +            "mintable": "Infinitely",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Mint": {
    +          "object": "13_u32",
    +          "destination_id": {
    +            "AssetId": "rose##alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Mint": {
    +          "object": "44_u32",
    +          "destination_id": {
    +            "AssetId": "cabbage#garden_of_live_flowers#alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Grant": {
    +          "object": {
    +            "PermissionToken": {
    +              "definition_id": "CanSetParameters",
    +              "payload": null
    +            }
    +          },
    +          "destination_id": {
    +            "AccountId": "alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Sequence": [
    +          {
    +            "NewParameter": {
    +              "Parameter": "?MaxTransactionsInBlock=512"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?BlockTime=2000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?CommitTimeLimit=4000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?TransactionLimits=4096,4194304_TL"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAssetMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAccountMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVDomainMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVIdentLengthLimits=1,128_LL"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WASMFuelLimit=23000000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WASMMaxMemory=524288000"
    +            }
    +          }
    +        ]
    +      },
    +      {
    +        "Register": {
    +          "NewRole": {
    +            "id": "ALICE_METADATA_ACCESS",
    +            "permissions": [
    +              {
    +                "definition_id": "CanRemoveKeyValueInUserAccount",
    +                "payload": {
    +                  "account_id": "alice@wonderland"
    +                }
    +              },
    +              {
    +                "definition_id": "CanSetKeyValueInUserAccount",
    +                "payload": {
    +                  "account_id": "alice@wonderland"
    +                }
    +              }
    +            ]
    +          }
    +        }
    +      }
    +    ]
    +  ],
    +  "validator": "./validator.wasm"
    +}
    {
    +  "transactions": [
    +    [
    +      {
    +        "Register": {
    +          "NewDomain": {
    +            "id": "wonderland",
    +            "logo": null,
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "alice@wonderland",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "bob@wonderland",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAssetDefinition": {
    +            "id": "rose#wonderland",
    +            "value_type": "Quantity",
    +            "mintable": "Infinitely",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewDomain": {
    +            "id": "garden_of_live_flowers",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "carpenter@garden_of_live_flowers",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAssetDefinition": {
    +            "id": "cabbage#garden_of_live_flowers",
    +            "value_type": "Quantity",
    +            "mintable": "Infinitely",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Mint": {
    +          "object": "13_u32",
    +          "destination_id": {
    +            "AssetId": "rose##alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Mint": {
    +          "object": "44_u32",
    +          "destination_id": {
    +            "AssetId": "cabbage#garden_of_live_flowers#alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Grant": {
    +          "object": {
    +            "PermissionToken": {
    +              "definition_id": "CanSetParameters",
    +              "payload": null
    +            }
    +          },
    +          "destination_id": {
    +            "AccountId": "alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Sequence": [
    +          {
    +            "NewParameter": {
    +              "Parameter": "?MaxTransactionsInBlock=512"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?BlockTime=2000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?CommitTimeLimit=4000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?TransactionLimits=4096,4194304_TL"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAssetMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAccountMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVDomainMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVIdentLengthLimits=1,128_LL"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WASMFuelLimit=23000000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WASMMaxMemory=524288000"
    +            }
    +          }
    +        ]
    +      },
    +      {
    +        "Register": {
    +          "NewRole": {
    +            "id": "ALICE_METADATA_ACCESS",
    +            "permissions": [
    +              {
    +                "definition_id": "CanRemoveKeyValueInUserAccount",
    +                "payload": {
    +                  "account_id": "alice@wonderland"
    +                }
    +              },
    +              {
    +                "definition_id": "CanSetKeyValueInUserAccount",
    +                "payload": {
    +                  "account_id": "alice@wonderland"
    +                }
    +              }
    +            ]
    +          }
    +        }
    +      }
    +    ]
    +  ],
    +  "validator": "./validator.wasm"
    +}

    The genesis account is specified in the peer configuration file, configs/peer/config.json. This is the account that will submit the genesis block. The genesis account is like a super user account that has elevated privileges, but only during the genesis round. The genesis account should be signed by one of the peers, or, in other words, it should have the public key of this peer.

    If you look at the example of a genesis block above, you will see that it contains instructions for registering a new domain (wonderland), two new accounts (alice@wonderland and bob@wonderland), a new asset (rose#wonderland) and a Mint instruction for this asset, as well as several permission tokens and roles. Both new accounts are signed with the ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0 public key.

    Note

    Iroha is case-sensitive, meaning that Alice@wonderland is different from alice@wonderland. It should go without saying that alice@wonderland is not the same as alice@looking_glass either, since these accounts belong to different domains, wonderland and looking_glass.

    The accounts registered in the genesis block are just new accounts. As we said above, the genesis account is determined in the peer configuration. However, you can use the matching signature for the genesis account and for a new account in the genesis block. Since the genesis account only has privileges during the genesis round, it won't be a security issue.

    You can generate the default genesis block or create a custom one.

    If you need to recommit a genesis block, remove the previously stored blocks, then restart the Docker container. The new genesis block will be automatically recommited upon container restart.

    Generation

    You can add various instructions to the genesis block, such as registering new accounts or assets, as well as minting assets. You can also register permission tokens and roles, as well as grant them to the registered accounts.

    Generate default genesis block

    You can use kagami to generate the default genesis block:

    • Generate a genesis block in JSON format:

      bash
      $ kagami genesis
      $ kagami genesis
    • Generate a genesis block in JSON format and write the output to the specified file:

      bash
      $ kagami genesis > genesis.json
      $ kagami genesis > genesis.json
    • Generate a synthetic genesis block in JSON format and write the n domains, m accounts per domain and p assets per domain:

      bash
      $ kagami genesis --synthetic --domains n --accounts-per-domain m --assets-per-domain p
      $ kagami genesis --synthetic --domains n --accounts-per-domain m --assets-per-domain p

    The genesis block should be located in configs/peer/genesis.json.

    Configuration

    As we already explained, genesis account is specified in the peer configuration file, configs/peer/config.json. You can use the same configuration file to fine-tune other genesis block configurations.

    + + + + \ No newline at end of file diff --git a/guide/configure/keys-for-network-deployment.html b/guide/configure/keys-for-network-deployment.html new file mode 100644 index 000000000..08890f26f --- /dev/null +++ b/guide/configure/keys-for-network-deployment.html @@ -0,0 +1,34 @@ + + + + + + Keys for Network Deployment | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Keys for Network Deployment

    If you're deploying your own Iroha 2 network, your unique cryptographic keys must be specified in all three of the configuration files:

    1. Peer configuration file: configs/peer/config.json
    2. Client configuration file: configs/client_cli/config.json
    3. Genesis block file: configs/peer/genesis.json

    To learn more about cryptographic keys and their role, see Security > Public Key Cryptography.

    Setting Keys For a New Network

    1. Generate New Key Pairs

    To generate new key pairs for the peers, a wide variety of methods can be used. However, within the Iroha 2 framework, you can conveniently use the built-in kagami tool for generating cryptographic keys.

    To generate a new key pair run the following command from the project's root directory:

    bash
    $ cargo run --bin kagami --release -- crypto --json
    $ cargo run --bin kagami --release -- crypto --json

    Note

    The output cryptographic keys generated by kagami are customizable by using preferences. Note that in the example above the --json parameter is specified to generate a key pair in the JSON format.

    To learn more about generating cryptographic keys with kagami, available algorithms, and other parameters, see Generating Cryptographic Keys with Kagami.

    If you plan to use the generated private_key with one of our SDKs, note that even though cryptographic keys are commonly encoded using ASCII characters, both the payload value of the private_key and the string representation of the public_key are encoded as Hex.

    2. Update Keys For Peers

    If you want to set up your own network, you should change the keys for all your peers: in peer/config.json change PUBLIC_KEY and PRIVATE_KEY to the fresh pair. When you've done that, you should add the keys to the TRUSTED_PEERS array in the same configuration file. Every peer that wants to connect to the given peer from the outside must know its PRIVATE_KEY specified in the TRUSTED_PEERS section.

    To create a minimum BFT network one needs four peers, which means four different private keys split across four different configuration files (or environment variables).

    Each peer must have their own PUBLIC_KEY and PRIVATE_KEY variables specified. All four of the public keys—including the peer that is being configured—must be added to the TRUSTED_PEERS array. The same TRUSTED_PEERS array must be copied across all four of the configuration files. If either one of the peers is missing, or there's an extraneous peer or one of the peers has the incorrect key, the network will fail to start.

    After that, make sure that the peers agree on the GENESIS_ACCOUNT key pairs. Failure to do so will result in a network which cannot accept any transactions.

    Note

    Even though the private key for the genesis account is known to all peers, the account itself loses all privileges after the first block is committed.

    3. Register a Non-Genesis Account

    Finally, while the first client could use the genesis account to register new users, it's not a great idea for most networks. You should, instead, register a non-genesis account (for example, alice@wonderland).

    WARNING

    iroha_client_cli currently processes all of its instructions in the JSON format, it also provides a dedicated instruction to unregister accounts.

    If you plan on creating a private blockchain, you should consider writing your own client based on the client Rust crate, or any of the provided client libraries:

    + + + + \ No newline at end of file diff --git a/guide/configure/metadata-and-store-assets.html b/guide/configure/metadata-and-store-assets.html new file mode 100644 index 000000000..f0d550444 --- /dev/null +++ b/guide/configure/metadata-and-store-assets.html @@ -0,0 +1,34 @@ + + + + + + Choosing Between the Store and Metadata Assets | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content
    + + + + \ No newline at end of file diff --git a/guide/configure/modes.html b/guide/configure/modes.html new file mode 100644 index 000000000..cc1868eff --- /dev/null +++ b/guide/configure/modes.html @@ -0,0 +1,34 @@ + + + + + + Public and Private Blockchains | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Public and Private Blockchains

    Iroha can be ran in a variety of configurations. As the administrator of your own network, you can use different permission sets to decide what criteria must be met in order for some transaction to be accepted.

    We provide two major sets of permissions: called a private and public permission sets. These need to be added into the genesis.json before you start an Iroha peer.

    Below we outline the major differences in these two use cases.

    Permissions

    In a public blockchain, most accounts have the same set of permissions. In a private blockchain, most accounts are assumed not to be able to do anything outside of their own account or domain unless explicitly granted said permission.

    INFO

    Refer to the dedicated section on permissions for more details.

    Peers

    Any peer can join a public blockchain. For a private blockchain, automatic discovery of peers is turned off.

    INFO

    Refer to peer management for more details.

    Registering accounts

    Depending on how you decide to set up your genesis block (genesis.json), the process for registering an account might go one of two ways. To understand why, let's talk about permission first.

    By default, Iroha allows all instructions to go through, until a permission validator that can restrict instruction execution has been registered. You can add permission validators to your genesis block by registering built-in permission tokens that we thought would be useful for private and public blockchain use-cases. However, in that case, the process of registering accounts is different.

    When it comes to registering accounts, public and private blockchain have the following differences:

    • In a public blockchain, anyone should be able to register an account[1]. So, in theory, all that you need is a suitable client, a way to generate a private key of a suitable type (ED25519), and that's it.

    • In a private blockchain, you can have any process for setting up an account: it could be that the registering instruction has to be submitted by a specific account, or by a smart contract that asks for other details. It could be that in a private blockchain registering new accounts is only possible on specific dates, or limited by a non-mintable (finite) token.

    • In a typical private blockchain, i.e. a blockchain without any unique processes for registering accounts, you need an account to register another account.

    Built-in permission validators for private blockchains cover the `typical private blockchain use-case.

    INFO

    As of writing, the set of public blockchain permissions is incomplete, and as such Iroha source code needs to be modified to run it in the public mode.

    Refer to the section on instructions for more details about Register<Account> instructions.


    1. In fact, once we have finished with our key-centric address scheme for accounts, you don't register an account as much as claim it. ↩︎

    + + + + \ No newline at end of file diff --git a/guide/configure/overview.html b/guide/configure/overview.html new file mode 100644 index 000000000..95dc06880 --- /dev/null +++ b/guide/configure/overview.html @@ -0,0 +1,34 @@ + + + + + + Configuration and Management | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/guide/configure/peer-configuration.html b/guide/configure/peer-configuration.html new file mode 100644 index 000000000..ea13fdb88 --- /dev/null +++ b/guide/configure/peer-configuration.html @@ -0,0 +1,230 @@ + + + + + + Peer Configuration | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Peer Configuration

    The peer configuration file (configs/peer/config.json) determines how your blockchain operates.

    Here's an example of how peer configuration file looks like:

    Peer configuration example
    json
    {
    +  "PUBLIC_KEY": null,
    +  "PRIVATE_KEY": null,
    +  "DISABLE_PANIC_TERMINAL_COLORS": false,
    +  "KURA": {
    +    "INIT_MODE": "strict",
    +    "BLOCK_STORE_PATH": "./storage",
    +    "BLOCKS_PER_STORAGE_FILE": 1000,
    +    "ACTOR_CHANNEL_CAPACITY": 100,
    +    "DEBUG_OUTPUT_NEW_BLOCKS": false
    +  },
    +  "SUMERAGI": {
    +    "KEY_PAIR": null,
    +    "PEER_ID": null,
    +    "BLOCK_TIME_MS": 2000,
    +    "TRUSTED_PEERS": null,
    +    "COMMIT_TIME_LIMIT_MS": 4000,
    +    "MAX_TRANSACTIONS_IN_BLOCK": 512,
    +    "ACTOR_CHANNEL_CAPACITY": 100,
    +    "GOSSIP_BATCH_SIZE": 500,
    +    "GOSSIP_PERIOD_MS": 1000
    +  },
    +  "TORII": {
    +    "P2P_ADDR": null,
    +    "API_URL": null,
    +    "TELEMETRY_URL": null,
    +    "MAX_TRANSACTION_SIZE": 32768,
    +    "MAX_CONTENT_LEN": 16384000,
    +    "FETCH_SIZE": 10,
    +    "QUERY_IDLE_TIME_MS": 30000
    +  },
    +  "BLOCK_SYNC": {
    +    "GOSSIP_PERIOD_MS": 10000,
    +    "BLOCK_BATCH_SIZE": 4,
    +    "ACTOR_CHANNEL_CAPACITY": 100
    +  },
    +  "QUEUE": {
    +    "MAX_TRANSACTIONS_IN_QUEUE": 65536,
    +    "MAX_TRANSACTIONS_IN_QUEUE_PER_USER": 65536,
    +    "TRANSACTION_TIME_TO_LIVE_MS": 86400000,
    +    "FUTURE_THRESHOLD_MS": 1000
    +  },
    +  "LOGGER": {
    +    "MAX_LOG_LEVEL": "INFO",
    +    "TELEMETRY_CAPACITY": 1000,
    +    "COMPACT_MODE": false,
    +    "LOG_FILE_PATH": null,
    +    "TERMINAL_COLORS": true
    +  },
    +  "GENESIS": {
    +    "ACCOUNT_PUBLIC_KEY": null,
    +    "ACCOUNT_PRIVATE_KEY": null
    +  },
    +  "WSV": {
    +    "ASSET_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "ASSET_DEFINITION_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "ACCOUNT_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "DOMAIN_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "IDENT_LENGTH_LIMITS": {
    +      "min": 1,
    +      "max": 128
    +    },
    +    "TRANSACTION_LIMITS": {
    +      "max_instruction_number": 4096,
    +      "max_wasm_size_bytes": 4194304
    +    },
    +    "WASM_RUNTIME_CONFIG": {
    +      "FUEL_LIMIT": 23000000,
    +      "MAX_MEMORY": 524288000
    +    }
    +  },
    +  "NETWORK": {
    +    "ACTOR_CHANNEL_CAPACITY": 100
    +  },
    +  "TELEMETRY": {
    +    "NAME": null,
    +    "URL": null,
    +    "MIN_RETRY_PERIOD": 1,
    +    "MAX_RETRY_DELAY_EXPONENT": 4,
    +    "FILE": null
    +  },
    +  "SNAPSHOT": {
    +    "CREATE_EVERY_MS": 60000,
    +    "DIR_PATH": "./storage",
    +    "CREATION_ENABLED": true
    +  }
    +}
    {
    +  "PUBLIC_KEY": null,
    +  "PRIVATE_KEY": null,
    +  "DISABLE_PANIC_TERMINAL_COLORS": false,
    +  "KURA": {
    +    "INIT_MODE": "strict",
    +    "BLOCK_STORE_PATH": "./storage",
    +    "BLOCKS_PER_STORAGE_FILE": 1000,
    +    "ACTOR_CHANNEL_CAPACITY": 100,
    +    "DEBUG_OUTPUT_NEW_BLOCKS": false
    +  },
    +  "SUMERAGI": {
    +    "KEY_PAIR": null,
    +    "PEER_ID": null,
    +    "BLOCK_TIME_MS": 2000,
    +    "TRUSTED_PEERS": null,
    +    "COMMIT_TIME_LIMIT_MS": 4000,
    +    "MAX_TRANSACTIONS_IN_BLOCK": 512,
    +    "ACTOR_CHANNEL_CAPACITY": 100,
    +    "GOSSIP_BATCH_SIZE": 500,
    +    "GOSSIP_PERIOD_MS": 1000
    +  },
    +  "TORII": {
    +    "P2P_ADDR": null,
    +    "API_URL": null,
    +    "TELEMETRY_URL": null,
    +    "MAX_TRANSACTION_SIZE": 32768,
    +    "MAX_CONTENT_LEN": 16384000,
    +    "FETCH_SIZE": 10,
    +    "QUERY_IDLE_TIME_MS": 30000
    +  },
    +  "BLOCK_SYNC": {
    +    "GOSSIP_PERIOD_MS": 10000,
    +    "BLOCK_BATCH_SIZE": 4,
    +    "ACTOR_CHANNEL_CAPACITY": 100
    +  },
    +  "QUEUE": {
    +    "MAX_TRANSACTIONS_IN_QUEUE": 65536,
    +    "MAX_TRANSACTIONS_IN_QUEUE_PER_USER": 65536,
    +    "TRANSACTION_TIME_TO_LIVE_MS": 86400000,
    +    "FUTURE_THRESHOLD_MS": 1000
    +  },
    +  "LOGGER": {
    +    "MAX_LOG_LEVEL": "INFO",
    +    "TELEMETRY_CAPACITY": 1000,
    +    "COMPACT_MODE": false,
    +    "LOG_FILE_PATH": null,
    +    "TERMINAL_COLORS": true
    +  },
    +  "GENESIS": {
    +    "ACCOUNT_PUBLIC_KEY": null,
    +    "ACCOUNT_PRIVATE_KEY": null
    +  },
    +  "WSV": {
    +    "ASSET_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "ASSET_DEFINITION_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "ACCOUNT_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "DOMAIN_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "IDENT_LENGTH_LIMITS": {
    +      "min": 1,
    +      "max": 128
    +    },
    +    "TRANSACTION_LIMITS": {
    +      "max_instruction_number": 4096,
    +      "max_wasm_size_bytes": 4194304
    +    },
    +    "WASM_RUNTIME_CONFIG": {
    +      "FUEL_LIMIT": 23000000,
    +      "MAX_MEMORY": 524288000
    +    }
    +  },
    +  "NETWORK": {
    +    "ACTOR_CHANNEL_CAPACITY": 100
    +  },
    +  "TELEMETRY": {
    +    "NAME": null,
    +    "URL": null,
    +    "MIN_RETRY_PERIOD": 1,
    +    "MAX_RETRY_DELAY_EXPONENT": 4,
    +    "FILE": null
    +  },
    +  "SNAPSHOT": {
    +    "CREATE_EVERY_MS": 60000,
    +    "DIR_PATH": "./storage",
    +    "CREATION_ENABLED": true
    +  }
    +}

    Some of the configuration options are required, while others are used for fine-tuning. When you create a new peer, you are required to provide the following:

    For the full list of configuration options, refer to Iroha Configuration Reference.

    INFO

    Configuration options have different underlying types and default values, which are denoted in code as types wrapped in a single Option<..> or in a double Option<Option<..>>. Refer to configuration types for details.

    Generation

    You can use kagami to generate the default peer configuration:

    bash
    $ kagami config peer > peer-config.json
    $ kagami config peer > peer-config.json

    Public and private keys

    The configs/peer/config.json peer configuration file should contain a pair of the user's public PUBLIC_KEY and private PRIVATE_KEY cryptographic keys for their account's ACCOUNT_ID.

    For details on cryptographic keys, see Public Key Cryptography.

    Trusted Peers

    Iroha is a blockchain ledger. In order for it to work optimally and be Byzantine-fault tolerant with the maximum number of faults allowed, it needs to be started with a set number of peers: 4, 7, 10, ... 3f+1, where f is the allowed number of faults.

    So usually, when you want to start an Iroha deployment, you should already know a number of peers that you can trust and join their blockchain. The way it works in the examples is that you just specify in four config.json files four peers with their public keys and API addresses.

    Since Iroha has no automatic peer discovery, the only other way to make peers known to each other is to use the iroha_client_cli to register new peers). This is not too difficult with the provided client libraries. With Python's Beautiful Soup, the curated list of peers can be updated, registered, and un-registered on its own.

    The list of trusted peers is a part of SUMERAGI configuration. Here's an example of SUMERAGI_TRUSTED_PEERS environment variable to configure trusted peers:

    '[{"address":"iroha0:1337", "public_key": "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"}, {"address":"iroha1:1338", "public_key": "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"}, {"address": "iroha2:1339", "public_key": "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"}, {"address": "iroha3:1340", "public_key": "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'
    '[{"address":"iroha0:1337", "public_key": "ed01201c61faf8fe94e253b93114240394f79a607b7fa55f9e5a41ebec74b88055768b"}, {"address":"iroha1:1338", "public_key": "ed0120cc25624d62896d3a0bfd8940f928dc2abf27cc57cefeb442aa96d9081aae58a1"}, {"address": "iroha2:1339", "public_key": "ed0120faca9e8aa83225cb4d16d67f27dd4f93fc30ffa11adc1f5c88fd5495ecc91020"}, {"address": "iroha3:1340", "public_key": "ed01208e351a70b6a603ed285d666b8d689b680865913ba03ce29fb7d13a166c4e7f1f"}]'

    Iroha Public Addresses

    TORII is the module in charge of handling incoming and outgoing connections.

    Here we only cover the required configurations: API_URL, P2P_ADDR, and TELEMETRY_URL. Check TORII configuration reference for all available options.

    API_URL

    The API_URL is the location to which the client(s) make their requests. You can also use it to change some peer-specific configuration options.

    Most of the time, the only reason to change the API_URL is to change the port, in case 8080 is either closed, or if you want to randomise ports to avoid certain kinds of attacks.

    P2P_ADDR

    The P2P_ADDR is the internal address used for communication between peers. This address should be included in the TRUSTED_PEERS section of the configuration file.

    TELEMETRY_URL

    The TELEMETRY_URL is used to specify the prometheus endpoint address. It's set by adding "TELEMETRY_URL": "127.0.0.1:8180" to the TORII section of the configuration file.

    It's not meant to be human-readable. However, a GET request to the 127.0.0.1:8180/status will give you a JSON-encoded representation of the top-level metrics, while a GET request to 127.0.0.1:8180/metrics will give you a (somewhat verbose) list of all available metrics gathered in Iroha. You might want to change this if you're having trouble gathering metrics using prometheus.

    INFO

    Learn how to monitor Iroha performance using prometheus.

    Genesis

    When you configure a peer, you have to provide private and public keys for the genesis account.

    You can do this via the configuration file (ACCOUNT_PUBLIC_KEY, ACCOUNT_PRIVATE_KEY) or environment variables (IROHA_GENESIS_ACCOUNT_PUBLIC_KEY, IROHA_GENESIS_ACCOUNT_PRIVATE_KEY).

    To learn more about genesis block, genesis account, and cryptographic keys, see the following:

    Aside from the public and private keys for the genesis account, which are required configuration options, you can also fine-tune other genesis block configurations, such as:

    • WAIT_FOR_PEERS_RETRY_COUNT_LIMIT: the number of attempts to connect to peers before genesis block is submitted
    • WAIT_FOR_PEERS_RETRY_PERIOD_MS: how long to wait before retrying a connection to peers
    • GENESIS_SUBMISSION_DELAY_MS: the delay before the genesis block submission after the minimum number of peers were discovered.

    You can find more details in GENESIS Iroha Configuration Reference.

    Logger

    Let's cover the most useful LOGGER configurations, MAX_LOG_LEVEL and LOG_FILE_PATH.

    MAX_LOG_LEVEL

    The MAX_LOG_LEVEL is used to determine which messages are logged.

    With "MAX_LOG_LEVEL": "WARN" you won't get any messages unless they are either a warning or an error. Beside WARN, other available options are:

    • TRACE (log every time you enter a function)
    • DEBUG (use when you know something went wrong)
    • INFO (the default)
    • WARN (log everything that could be an error)
    • ERROR (to silence any logging except for error messages)

    LOG_FILE_PATH

    Another useful option is "LOG_FILE_PATH": bunyan.json. It creates (if it didn't already exist) a file called bunyan.json that contains the message log in a structured format.

    This is extremely useful for two reasons. Firstly, you can use the bunyan log viewer to filter information more precisely than Iroha would allow you to do. Do you only want messages from a specific module or package? You can do that with bunyan. Secondly, while copying logs is not too big of a problem if your instance is just a small setup, for bigger setups the log will be larger. Having it saved to a file makes much more sense in that case.

    INFO

    You can also set LOG_FILE_PATH to /dev/stdout if you want to use bunyan's logging facilities directly without saving the output.

    Kura

    Kura is the persistent storage engine of Iroha (Japanese for warehouse). The BLOCK_STORE_PATH specifies where the blocks are stored. You can change it to a custom location if for some reason the default location (./storage) is not available or desirable.

    For more details, check KURA configuration reference.

    + + + + \ No newline at end of file diff --git a/guide/configure/peer-management.html b/guide/configure/peer-management.html new file mode 100644 index 000000000..5bb08dbe8 --- /dev/null +++ b/guide/configure/peer-management.html @@ -0,0 +1,34 @@ + + + + + + Peer Management | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Peer Management

    If you followed any of the language-specific guides, you now have a well-functioning network that people will want to join.

    Public Blockchain

    Naturally, in a public blockchain, joining is a matter of installing the correct software and waiting until your node gets discovered.

    INFO

    Peer discovery is under construction.

    Private Blockchain

    In a bank setting, allowing everyone to join at their leisure is a security nightmare. For safety, automatic discovery of peers is turned off for Iroha 2 in the private blockchain configuration.

    Registering peers

    To add a peer to the network, it must be manually registered. Let's discuss the steps that should be taken in order to complete this process.

    1. Grant the user permissions

    The user that registers the peer must have the appropriate PermissionToken. This could be granted as part of a role, or as part of a one-time allowance.

    How to decide if you need to grant a role? Granting roles makes sense if a user is to serve as an administrator of sorts, where it's their responsibility to maintain the peers in the network long-term. A one-time permission grant is useful when the party registering the peer isn't responsible for registering peers in general, but the network administrator doesn't need to (or want to) spend time setting up a new peer.

    INFO

    Permissions for registering a peer are under construction.

    We discuss permissions and roles with more detail in a separate chapter.

    2. Set up a peer

    After a new peer was granted permissions, it must be set up.

    It's a good idea to request information about the peers' configuration in the network. Your best friend is the configuration endpoint of the API socket. Thus far querying is done manually. Until the bootstrapping procedure is implemented, you'll have to manually check that the timeouts and batch sizes match.

    To simplify the process, you can ask the network administrator for a redacted version of config.json, which excludes privileged information, such as PRIVATE_KEYs.

    3. Submit the instruction

    After your peer is running, you should submit the register peer instruction. The peer will go through the handshake process and start chatting with the network.

    TIP

    Submitting a Register<Peer> instruction does not (and cannot) instantiate a new peer process.

    Unregistering peers

    What about unregistering peers? For security reasons this process is one-sided. The network reaches consensus that it wants to remove a peer, but the peer itself doesn't know much about why nobody's talking to it.

    In most circumstances, if you want to unregister a peer, you want to do so because it is a Byzantine fault. Just "ghosting" this peer makes the life of the malicious actor on the network harder.

    + + + + \ No newline at end of file diff --git a/guide/configure/sample-configuration.html b/guide/configure/sample-configuration.html new file mode 100644 index 000000000..dbbe5e467 --- /dev/null +++ b/guide/configure/sample-configuration.html @@ -0,0 +1,670 @@ + + + + + + Sample Configuration Files | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Sample Configuration Files

    Here you can find sample configuration files for Iroha 2:

    • Peer Configuration (configs/peer/config.json). Refer to peer configuration for details.
    • Genesis Block (configs/peer/genesis.json). Refer to genesis block for details.
    • Client Configuration (configs/client_cli/config.json). Refer to client configuration for details.
    json
    {
    +  "PUBLIC_KEY": null,
    +  "PRIVATE_KEY": null,
    +  "DISABLE_PANIC_TERMINAL_COLORS": false,
    +  "KURA": {
    +    "INIT_MODE": "strict",
    +    "BLOCK_STORE_PATH": "./storage",
    +    "BLOCKS_PER_STORAGE_FILE": 1000,
    +    "ACTOR_CHANNEL_CAPACITY": 100,
    +    "DEBUG_OUTPUT_NEW_BLOCKS": false
    +  },
    +  "SUMERAGI": {
    +    "KEY_PAIR": null,
    +    "PEER_ID": null,
    +    "BLOCK_TIME_MS": 2000,
    +    "TRUSTED_PEERS": null,
    +    "COMMIT_TIME_LIMIT_MS": 4000,
    +    "MAX_TRANSACTIONS_IN_BLOCK": 512,
    +    "ACTOR_CHANNEL_CAPACITY": 100,
    +    "GOSSIP_BATCH_SIZE": 500,
    +    "GOSSIP_PERIOD_MS": 1000
    +  },
    +  "TORII": {
    +    "P2P_ADDR": null,
    +    "API_URL": null,
    +    "TELEMETRY_URL": null,
    +    "MAX_TRANSACTION_SIZE": 32768,
    +    "MAX_CONTENT_LEN": 16384000,
    +    "FETCH_SIZE": 10,
    +    "QUERY_IDLE_TIME_MS": 30000
    +  },
    +  "BLOCK_SYNC": {
    +    "GOSSIP_PERIOD_MS": 10000,
    +    "BLOCK_BATCH_SIZE": 4,
    +    "ACTOR_CHANNEL_CAPACITY": 100
    +  },
    +  "QUEUE": {
    +    "MAX_TRANSACTIONS_IN_QUEUE": 65536,
    +    "MAX_TRANSACTIONS_IN_QUEUE_PER_USER": 65536,
    +    "TRANSACTION_TIME_TO_LIVE_MS": 86400000,
    +    "FUTURE_THRESHOLD_MS": 1000
    +  },
    +  "LOGGER": {
    +    "MAX_LOG_LEVEL": "INFO",
    +    "TELEMETRY_CAPACITY": 1000,
    +    "COMPACT_MODE": false,
    +    "LOG_FILE_PATH": null,
    +    "TERMINAL_COLORS": true
    +  },
    +  "GENESIS": {
    +    "ACCOUNT_PUBLIC_KEY": null,
    +    "ACCOUNT_PRIVATE_KEY": null
    +  },
    +  "WSV": {
    +    "ASSET_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "ASSET_DEFINITION_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "ACCOUNT_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "DOMAIN_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "IDENT_LENGTH_LIMITS": {
    +      "min": 1,
    +      "max": 128
    +    },
    +    "TRANSACTION_LIMITS": {
    +      "max_instruction_number": 4096,
    +      "max_wasm_size_bytes": 4194304
    +    },
    +    "WASM_RUNTIME_CONFIG": {
    +      "FUEL_LIMIT": 23000000,
    +      "MAX_MEMORY": 524288000
    +    }
    +  },
    +  "NETWORK": {
    +    "ACTOR_CHANNEL_CAPACITY": 100
    +  },
    +  "TELEMETRY": {
    +    "NAME": null,
    +    "URL": null,
    +    "MIN_RETRY_PERIOD": 1,
    +    "MAX_RETRY_DELAY_EXPONENT": 4,
    +    "FILE": null
    +  },
    +  "SNAPSHOT": {
    +    "CREATE_EVERY_MS": 60000,
    +    "DIR_PATH": "./storage",
    +    "CREATION_ENABLED": true
    +  }
    +}
    {
    +  "PUBLIC_KEY": null,
    +  "PRIVATE_KEY": null,
    +  "DISABLE_PANIC_TERMINAL_COLORS": false,
    +  "KURA": {
    +    "INIT_MODE": "strict",
    +    "BLOCK_STORE_PATH": "./storage",
    +    "BLOCKS_PER_STORAGE_FILE": 1000,
    +    "ACTOR_CHANNEL_CAPACITY": 100,
    +    "DEBUG_OUTPUT_NEW_BLOCKS": false
    +  },
    +  "SUMERAGI": {
    +    "KEY_PAIR": null,
    +    "PEER_ID": null,
    +    "BLOCK_TIME_MS": 2000,
    +    "TRUSTED_PEERS": null,
    +    "COMMIT_TIME_LIMIT_MS": 4000,
    +    "MAX_TRANSACTIONS_IN_BLOCK": 512,
    +    "ACTOR_CHANNEL_CAPACITY": 100,
    +    "GOSSIP_BATCH_SIZE": 500,
    +    "GOSSIP_PERIOD_MS": 1000
    +  },
    +  "TORII": {
    +    "P2P_ADDR": null,
    +    "API_URL": null,
    +    "TELEMETRY_URL": null,
    +    "MAX_TRANSACTION_SIZE": 32768,
    +    "MAX_CONTENT_LEN": 16384000,
    +    "FETCH_SIZE": 10,
    +    "QUERY_IDLE_TIME_MS": 30000
    +  },
    +  "BLOCK_SYNC": {
    +    "GOSSIP_PERIOD_MS": 10000,
    +    "BLOCK_BATCH_SIZE": 4,
    +    "ACTOR_CHANNEL_CAPACITY": 100
    +  },
    +  "QUEUE": {
    +    "MAX_TRANSACTIONS_IN_QUEUE": 65536,
    +    "MAX_TRANSACTIONS_IN_QUEUE_PER_USER": 65536,
    +    "TRANSACTION_TIME_TO_LIVE_MS": 86400000,
    +    "FUTURE_THRESHOLD_MS": 1000
    +  },
    +  "LOGGER": {
    +    "MAX_LOG_LEVEL": "INFO",
    +    "TELEMETRY_CAPACITY": 1000,
    +    "COMPACT_MODE": false,
    +    "LOG_FILE_PATH": null,
    +    "TERMINAL_COLORS": true
    +  },
    +  "GENESIS": {
    +    "ACCOUNT_PUBLIC_KEY": null,
    +    "ACCOUNT_PRIVATE_KEY": null
    +  },
    +  "WSV": {
    +    "ASSET_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "ASSET_DEFINITION_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "ACCOUNT_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "DOMAIN_METADATA_LIMITS": {
    +      "max_len": 1048576,
    +      "max_entry_byte_size": 4096
    +    },
    +    "IDENT_LENGTH_LIMITS": {
    +      "min": 1,
    +      "max": 128
    +    },
    +    "TRANSACTION_LIMITS": {
    +      "max_instruction_number": 4096,
    +      "max_wasm_size_bytes": 4194304
    +    },
    +    "WASM_RUNTIME_CONFIG": {
    +      "FUEL_LIMIT": 23000000,
    +      "MAX_MEMORY": 524288000
    +    }
    +  },
    +  "NETWORK": {
    +    "ACTOR_CHANNEL_CAPACITY": 100
    +  },
    +  "TELEMETRY": {
    +    "NAME": null,
    +    "URL": null,
    +    "MIN_RETRY_PERIOD": 1,
    +    "MAX_RETRY_DELAY_EXPONENT": 4,
    +    "FILE": null
    +  },
    +  "SNAPSHOT": {
    +    "CREATE_EVERY_MS": 60000,
    +    "DIR_PATH": "./storage",
    +    "CREATION_ENABLED": true
    +  }
    +}
    json
    {
    +  "transactions": [
    +    [
    +      {
    +        "Register": {
    +          "NewDomain": {
    +            "id": "wonderland",
    +            "logo": null,
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "alice@wonderland",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "bob@wonderland",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAssetDefinition": {
    +            "id": "rose#wonderland",
    +            "value_type": "Quantity",
    +            "mintable": "Infinitely",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewDomain": {
    +            "id": "garden_of_live_flowers",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "carpenter@garden_of_live_flowers",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAssetDefinition": {
    +            "id": "cabbage#garden_of_live_flowers",
    +            "value_type": "Quantity",
    +            "mintable": "Infinitely",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Mint": {
    +          "object": "13_u32",
    +          "destination_id": {
    +            "AssetId": "rose##alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Mint": {
    +          "object": "44_u32",
    +          "destination_id": {
    +            "AssetId": "cabbage#garden_of_live_flowers#alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Grant": {
    +          "object": {
    +            "PermissionToken": {
    +              "definition_id": "CanSetParameters",
    +              "payload": null
    +            }
    +          },
    +          "destination_id": {
    +            "AccountId": "alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Sequence": [
    +          {
    +            "NewParameter": {
    +              "Parameter": "?MaxTransactionsInBlock=512"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?BlockTime=2000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?CommitTimeLimit=4000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?TransactionLimits=4096,4194304_TL"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAssetMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAccountMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVDomainMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVIdentLengthLimits=1,128_LL"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WASMFuelLimit=23000000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WASMMaxMemory=524288000"
    +            }
    +          }
    +        ]
    +      },
    +      {
    +        "Register": {
    +          "NewRole": {
    +            "id": "ALICE_METADATA_ACCESS",
    +            "permissions": [
    +              {
    +                "definition_id": "CanRemoveKeyValueInUserAccount",
    +                "payload": {
    +                  "account_id": "alice@wonderland"
    +                }
    +              },
    +              {
    +                "definition_id": "CanSetKeyValueInUserAccount",
    +                "payload": {
    +                  "account_id": "alice@wonderland"
    +                }
    +              }
    +            ]
    +          }
    +        }
    +      }
    +    ]
    +  ],
    +  "validator": "./validator.wasm"
    +}
    {
    +  "transactions": [
    +    [
    +      {
    +        "Register": {
    +          "NewDomain": {
    +            "id": "wonderland",
    +            "logo": null,
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "alice@wonderland",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "bob@wonderland",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {
    +              "key": {
    +                "String": "value"
    +              }
    +            }
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAssetDefinition": {
    +            "id": "rose#wonderland",
    +            "value_type": "Quantity",
    +            "mintable": "Infinitely",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewDomain": {
    +            "id": "garden_of_live_flowers",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAccount": {
    +            "id": "carpenter@garden_of_live_flowers",
    +            "signatories": [
    +              "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
    +            ],
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Register": {
    +          "NewAssetDefinition": {
    +            "id": "cabbage#garden_of_live_flowers",
    +            "value_type": "Quantity",
    +            "mintable": "Infinitely",
    +            "logo": null,
    +            "metadata": {}
    +          }
    +        }
    +      },
    +      {
    +        "Mint": {
    +          "object": "13_u32",
    +          "destination_id": {
    +            "AssetId": "rose##alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Mint": {
    +          "object": "44_u32",
    +          "destination_id": {
    +            "AssetId": "cabbage#garden_of_live_flowers#alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Grant": {
    +          "object": {
    +            "PermissionToken": {
    +              "definition_id": "CanSetParameters",
    +              "payload": null
    +            }
    +          },
    +          "destination_id": {
    +            "AccountId": "alice@wonderland"
    +          }
    +        }
    +      },
    +      {
    +        "Sequence": [
    +          {
    +            "NewParameter": {
    +              "Parameter": "?MaxTransactionsInBlock=512"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?BlockTime=2000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?CommitTimeLimit=4000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?TransactionLimits=4096,4194304_TL"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAssetMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAssetDefinitionMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVAccountMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVDomainMetadataLimits=1048576,4096_ML"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WSVIdentLengthLimits=1,128_LL"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WASMFuelLimit=23000000"
    +            }
    +          },
    +          {
    +            "NewParameter": {
    +              "Parameter": "?WASMMaxMemory=524288000"
    +            }
    +          }
    +        ]
    +      },
    +      {
    +        "Register": {
    +          "NewRole": {
    +            "id": "ALICE_METADATA_ACCESS",
    +            "permissions": [
    +              {
    +                "definition_id": "CanRemoveKeyValueInUserAccount",
    +                "payload": {
    +                  "account_id": "alice@wonderland"
    +                }
    +              },
    +              {
    +                "definition_id": "CanSetKeyValueInUserAccount",
    +                "payload": {
    +                  "account_id": "alice@wonderland"
    +                }
    +              }
    +            ]
    +          }
    +        }
    +      }
    +    ]
    +  ],
    +  "validator": "./validator.wasm"
    +}
    json
    {
    +  "PUBLIC_KEY": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
    +  "PRIVATE_KEY": {
    +    "digest_function": "ed25519",
    +    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
    +  },
    +  "ACCOUNT_ID": "alice@wonderland",
    +  "BASIC_AUTH": {
    +    "web_login": "mad_hatter",
    +    "password": "ilovetea"
    +  },
    +  "TORII_API_URL": "http://127.0.0.1:8080/",
    +  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180/",
    +  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
    +  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
    +  "TRANSACTION_LIMITS": {
    +    "max_instruction_number": 4096,
    +    "max_wasm_size_bytes": 4194304
    +  },
    +  "ADD_TRANSACTION_NONCE": false
    +}
    {
    +  "PUBLIC_KEY": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
    +  "PRIVATE_KEY": {
    +    "digest_function": "ed25519",
    +    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
    +  },
    +  "ACCOUNT_ID": "alice@wonderland",
    +  "BASIC_AUTH": {
    +    "web_login": "mad_hatter",
    +    "password": "ilovetea"
    +  },
    +  "TORII_API_URL": "http://127.0.0.1:8080/",
    +  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180/",
    +  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
    +  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
    +  "TRANSACTION_LIMITS": {
    +    "max_instruction_number": 4096,
    +    "max_wasm_size_bytes": 4194304
    +  },
    +  "ADD_TRANSACTION_NONCE": false
    +}
    + + + + \ No newline at end of file diff --git a/guide/get-started/bash.html b/guide/get-started/bash.html new file mode 100644 index 000000000..062737ddb --- /dev/null +++ b/guide/get-started/bash.html @@ -0,0 +1,442 @@ + + + + + + Bash Guide | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Bash Guide

    0. A brief primer on CLI applications

    This is as good a place as any to discuss what iroha_client_cli is and what you should expect it to be able to do.

    Most users think that everything that's run inside the terminal is a CLI program. This is not the case. A Command-line Interface is a glorified scripting language that you interact with using the shell.

    CLI programs are run multiple times and given different arguments: --flag1 file2 --help, etc., depending on what you want to do. A single session doesn't begin with you opening the program and end with you closing it. When you've stopped interacting, the iroha_client_cli session is finished.

    You also don't configure a CLI program in the usual sense of the word. Most people expect that it's possible to change some settings of a program from inside that same program, but most CLI programs are configured differently.

    First of all, if you want to make a certain --flag part of the way you invoke iroha_client_cli, you should create a shell alias. Some programs, but not iroha_client_cli, also use something called Environment variables.

    Finally, most programs store persistent information that is too big for either a shell alias or an environment variable in a separate file. iroha_client_cli does not yet follow the XDG_CONFIG_HOME specification. It only looks for a configuration file in one of two places:

    1. If the -c or --config command line flag is specified, the next argument interpreted as a path.

      For example: -c ~/Git/iroha/configs/peer/config.json. If that file doesn't exist, you will see an error, and iroha_client_cli won't look for a configuration file anywhere else.

    2. If neither -c nor --config were given, it will look in the current working directory.

    These defaults are not very ergonomic. They are artifacts of the way in which Iroha is being deployed, and the fact that a CLI interface is used exclusively for testing purposes.

    INFO

    This might change in the future, but likely not by fixing iroha_client_cli but rather by replacing it entirely with Iroha Python. The only thing stopping us from that today is that Iroha Python has not gotten the attention it deserves.

    It is possible that a user might be expecting iroha_client_cli to behave like a graphical program using terminal graphics: a Terminal User Interface. While we'd like to provide you with such a program, we don't think that it offers enough convenience over the amount of effort that we'd need to put in.

    INFO

    It is possible that in the future, once iroha_client_cli is phased out and iroha_python is the official scripting interface, we might use the excellent Python libraries for creating a useful TUI. For now, one must make do with what one has.

    1. Iroha 2 Client Setup

    Note, first, that we have already created the iroha_client_cli binary executable, when we ran the build command.

    Create a fresh directory for the client:

    bash
    $ mkdir -p test_docker
    $ mkdir -p test_docker

    Copy the configuration file to the client directory:

    bash
    $ cp ./configs/client/config.json test_docker/
    $ cp ./configs/client/config.json test_docker/

    TIP

    You could also use a file manager (e.g. finder) to do that. We prefer providing command-line instructions, because they are easier to follow step-by-step.

    To test Iroha 2 metadata capabilities, let's also create a dummy metadata.json file:

    bash
    $ echo '{"comment":{"String": "Hello Meta!"}}' > test_docker/metadata.json
    $ echo '{"comment":{"String": "Hello Meta!"}}' > test_docker/metadata.json

    To get the CLI started, copy the iroha_client_cli binary into the client directory:

    bash
    $ cp ./target/debug/iroha_client_cli test_docker/
    $ cp ./target/debug/iroha_client_cli test_docker/

    Make sure you bring up the test network as well.

    2. Configuring Iroha 2

    Now let's look at how to properly configure Iroha 2, and especially its Command-Line Interface client.

    Make sure that you have another terminal tab or window open with a running version, using the instructions above. You can use this screen to monitor the pipeline events as they are output.

    On a new terminal tab run:

    bash
    $ cd ~/Git/iroha/test_docker
    $ cd ~/Git/iroha/test_docker

    If you followed the steps correctly, this should contain the iroha_client_cli and config.json.

    TIP

    Use ls to make sure both files are there, and if not, return to Step 1.

    Run

    bash
    $ ./iroha_client_cli
    $ ./iroha_client_cli
    Expand to see the expected output
    iroha_client_cli 0.1.0
    +Soramitsu Iroha2 team (https, //github.com/orgs/soramitsu/teams/iroha2)
    +Iroha CLI Client provides an ability to interact with Iroha Peers Web API without direct network usage
    +
    +USAGE:
    +    iroha_client_cli [OPTIONS] <SUBCOMMAND>
    +
    +FLAGS:
    +    -h, --help       Prints help information
    +    -V, --version    Prints version information
    +
    +OPTIONS:
    +    -c, --config <config>    Sets a config file path [default: config.json]
    +
    +SUBCOMMANDS:
    +    account    The subcommand related to accounts
    +    asset      The subcommand related to assets
    +    blocks     The subcommand related to block streaming
    +    domain     The subcommand related to domains
    +    events     The subcommand related to event streaming
    +    help       Prints this message or the help of the given subcommand(s)
    +    json       The subcommand related to multi-instructions as Json
    +    peer       The subcommand related to p2p networking
    +    wasm       The subcommand related to Wasm
    iroha_client_cli 0.1.0
    +Soramitsu Iroha2 team (https, //github.com/orgs/soramitsu/teams/iroha2)
    +Iroha CLI Client provides an ability to interact with Iroha Peers Web API without direct network usage
    +
    +USAGE:
    +    iroha_client_cli [OPTIONS] <SUBCOMMAND>
    +
    +FLAGS:
    +    -h, --help       Prints help information
    +    -V, --version    Prints version information
    +
    +OPTIONS:
    +    -c, --config <config>    Sets a config file path [default: config.json]
    +
    +SUBCOMMANDS:
    +    account    The subcommand related to accounts
    +    asset      The subcommand related to assets
    +    blocks     The subcommand related to block streaming
    +    domain     The subcommand related to domains
    +    events     The subcommand related to event streaming
    +    help       Prints this message or the help of the given subcommand(s)
    +    json       The subcommand related to multi-instructions as Json
    +    peer       The subcommand related to p2p networking
    +    wasm       The subcommand related to Wasm

    To configure the Iroha client, run:

    bash
    $ ./iroha_client_cli --config ./test_docker/config.json
    $ ./iroha_client_cli --config ./test_docker/config.json

    It should be noted that this is not persistent configuration: each time you run iroha_client_cli, you must add the --config ./test_docker/config.json command-line argument.

    TIP

    Because the client checks the working directory for a file called config.json, it's always much easier to just copy (or link) the file into the working directory. Alternatively, you could also create a shell alias.

    Feel free to edit the file and see what each option does. The only thing that you shouldn't edit at this point is the account. You see, alice has to be pre-registered in the genesis block. Only she can interact with the blockchain, and if you change the value of the user account, you should also make sure that that user exists in the blockchain.

    To make sure that your configuration options worked, try to run a query, e.g.:

    bash
    $ ./iroha_client_cli domain list all
    $ ./iroha_client_cli domain list all

    If the output looks like some form of JSON (but not quite), then the configuration was successful!

    3. Registering a Domain

    To get started, you must register a domain:

    bash
    $ ./iroha_client_cli domain register --id="looking_glass"
    $ ./iroha_client_cli domain register --id="looking_glass"

    You will receive a confirmation of the domain creation. However, this information will not be clearly visible within the message. To confirm that the new domain looking_glass has been created successfully, run:

    bash
    $ ./iroha_client_cli domain list all
    $ ./iroha_client_cli domain list all

    The printout should contain the recently-created looking_glass domain

    rust
    Domain {
    +    name: "looking_glass",
    +    accounts: {},
    +    asset_definitions: {},
    +    metadata: Metadata {
    +        map: {},
    +    },
    +},
    Domain {
    +    name: "looking_glass",
    +    accounts: {},
    +    asset_definitions: {},
    +    metadata: Metadata {
    +        map: {},
    +    },
    +},

    With a domain available, it is time to register an account.

    4. Registering an Account

    To register a new account, you need a cryptographic key pair, a set of a public and private keys that establish a secure communication channel between a peer and the network (to learn more about cryptographic keys, see Public Key Cryptography).

    There is a number of different ways to generate a cryptographic key pair. For the convenience of our users, Iroha 2 is delivered with kagami, an in-built tool for generating keys. However, any user is free to generate their keys any way they like.

    To generate a new key pair with kagami, run the following command from your project's root directory:

    bash
    $ cargo build --bin kagami --release
    +$ ./target/release/kagami crypto
    $ cargo build --bin kagami --release
    +$ ./target/release/kagami crypto

    TIP

    To customize the generated keys, you can specify a number of parameters. For instance, kagami can use of one of four available algorithms to generate cryptographic keys.

    To learn more about generating cryptographic keys with kagami, available algorithms, and other parameters, see Generating Cryptographic Keys with Kagami

    For the purposes of this tutorial, we will use the following key pair for mad_hatter@looking_glass:

    bash
    Public key (multihash): ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5
    +Private key: 14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5
    +Digest function: ed25519
    Public key (multihash): ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5
    +Private key: 14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5
    +Digest function: ed25519

    To register a new account called mad_hatter within the looking_glass domain, run:

    bash
    $ ./iroha_client_cli account register \
    +    --id="mad_hatter@looking_glass" \
    +    --key="ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5"
    $ ./iroha_client_cli account register \
    +    --id="mad_hatter@looking_glass" \
    +    --key="ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5"

    The --id argument in the above code snippet specifies the account id, the unique name assigned to that account in the blockchain. This name includes the domain, which is a group of things like asset definitions, account ids, and other objects that we'll talk about later. The --key argument specifies the public key, which is the one we generated above using kagami.

    If the account registration is successful, you will receive a confirmation message. Like before, it is necessary to query the accounts to verify that mad_hatter has been registered.

    Now, let's switch to the newly created account, mad_hatter, and continue experimenting with it. For this, we need to modify the PUBLIC_KEY, PRIVATE_KEY, and ACCOUNT_ID in the config.json file with the ones we registered earlier, which is located in the same directory as iroha_client_cli.

    TIP

    Your updated config.json should look like this

    json
    {
    +  "PUBLIC_KEY": "ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5",
    +  "PRIVATE_KEY": {
    +    "digest_function": "ed25519",
    +    "payload": "14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5"
    +  },
    +  "ACCOUNT_ID": "mad_hatter@looking_glass",
    +  "BASIC_AUTH": {
    +    "web_login": "mad_hatter",
    +    "password": "ilovetea"
    +  },
    +  "TORII_API_URL": "http://127.0.0.1:8080",
    +  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180",
    +  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
    +  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
    +  "TRANSACTION_LIMITS": {
    +    "max_instruction_number": 4096,
    +    "max_wasm_size_bytes": 4194304
    +  },
    +  "ADD_TRANSACTION_NONCE": false
    +}
    {
    +  "PUBLIC_KEY": "ed01204595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5",
    +  "PRIVATE_KEY": {
    +    "digest_function": "ed25519",
    +    "payload": "14d382c5bd8c0bfbaefdef1133196b78839ed3c136e296e0d969b7a3fca2fb424595da8957c5598d4de20fe5f3eb4c14820678e1a1957a535db1fd4b3d1607c5"
    +  },
    +  "ACCOUNT_ID": "mad_hatter@looking_glass",
    +  "BASIC_AUTH": {
    +    "web_login": "mad_hatter",
    +    "password": "ilovetea"
    +  },
    +  "TORII_API_URL": "http://127.0.0.1:8080",
    +  "TORII_TELEMETRY_URL": "http://127.0.0.1:8180",
    +  "TRANSACTION_TIME_TO_LIVE_MS": 100000,
    +  "TRANSACTION_STATUS_TIMEOUT_MS": 15000,
    +  "TRANSACTION_LIMITS": {
    +    "max_instruction_number": 4096,
    +    "max_wasm_size_bytes": 4194304
    +  },
    +  "ADD_TRANSACTION_NONCE": false
    +}

    To see all the accounts on the network as mad_hatter, run:

    bash
    $ ./iroha_client_cli account list all
    $ ./iroha_client_cli account list all

    This will list the active accounts on the network, along with their assets. It will look like this:

    Expand to see the expected output
    rust
    Account {
    +    id: Id {
    +        name: "mad_hatter",
    +        domain_name: Id {
    +            name: "looking_glass",
    +        },
    +    },
    +    assets: {},
    +    signatories: {
    +        { digest: "ed25519", payload: "A753146E75B910AE5E2994DC8ADEA9E7D87E5D53024CFA310CE992F17106F92C",
    +        },
    +    },
    +    permission_tokens: {},
    +    signature_check_condition: SignatureCheckCondition(
    +        EvaluatesTo {
    +            expression: ContainsAny(
    +                ContainsAny {
    +                    collection: EvaluatesTo {
    +                        expression: ContextValue(
    +                            ContextValue {
    +                                value_name: "transaction_signatories",
    +                            },
    +                        ),
    +                        _value_type: PhantomData,
    +                    },
    +                    elements: EvaluatesTo {
    +                        expression: ContextValue(
    +                            ContextValue {
    +                                value_name: "account_signatories",
    +                            },
    +                        ),
    +                        _value_type: PhantomData,
    +                    },
    +                },
    +            ),
    +            _value_type: PhantomData,
    +        },
    +    ),
    +    metadata: Metadata {
    +        map: {},
    +    },
    +    roles: {},
    +}
    Account {
    +    id: Id {
    +        name: "mad_hatter",
    +        domain_name: Id {
    +            name: "looking_glass",
    +        },
    +    },
    +    assets: {},
    +    signatories: {
    +        { digest: "ed25519", payload: "A753146E75B910AE5E2994DC8ADEA9E7D87E5D53024CFA310CE992F17106F92C",
    +        },
    +    },
    +    permission_tokens: {},
    +    signature_check_condition: SignatureCheckCondition(
    +        EvaluatesTo {
    +            expression: ContainsAny(
    +                ContainsAny {
    +                    collection: EvaluatesTo {
    +                        expression: ContextValue(
    +                            ContextValue {
    +                                value_name: "transaction_signatories",
    +                            },
    +                        ),
    +                        _value_type: PhantomData,
    +                    },
    +                    elements: EvaluatesTo {
    +                        expression: ContextValue(
    +                            ContextValue {
    +                                value_name: "account_signatories",
    +                            },
    +                        ),
    +                        _value_type: PhantomData,
    +                    },
    +                },
    +            ),
    +            _value_type: PhantomData,
    +        },
    +    ),
    +    metadata: Metadata {
    +        map: {},
    +    },
    +    roles: {},
    +}

    Another way to create a user (and the user's keys) is as follows:

    1. Open a new tab and navigate to the /iroha directory, then run:

      bash
      $ ./target/debug/iroha_crypto_cli
      $ ./target/debug/iroha_crypto_cli
    2. Copy the public key and repeat the instructions to register a new account. Every time you run this command, you will generate a new key-pair.

    In this case, we will create an account for white_rabbit within the looking_glass domain, so we will run:

    bash
    $ ./iroha_client_cli account register \
    +    --id="white_rabbit@looking_glass" \
    +    --key="ed0120a4c4dadd9f18b0f63d6a420151fe0748d785475dec63034a15fcf999ceda1e65"
    $ ./iroha_client_cli account register \
    +    --id="white_rabbit@looking_glass" \
    +    --key="ed0120a4c4dadd9f18b0f63d6a420151fe0748d785475dec63034a15fcf999ceda1e65"

    And like before, the new active user will be listed on the network:

    Expand to see the expected output
    rust
    Account {
    +    id: Id {
    +        name: "white_rabbit",
    +        domain_name: Id {
    +            name: "looking_glass",
    +        },
    +    },
    +    assets: {},
    +    signatories: {
    +        { digest: "ed25519", payload: "A4C4DADD9F18B0F63D6A420151FE0748D785475DEC63034A15FCF999CEDA1E65",
    +        },
    +    },
    +    permission_tokens: {},
    +    signature_check_condition: SignatureCheckCondition(
    +        EvaluatesTo {
    +            expression: ContainsAny(
    +                ContainsAny {
    +                    collection: EvaluatesTo {
    +                        expression: ContextValue(
    +                            ContextValue {
    +                                value_name: "transaction_signatories",
    +                            },
    +                        ),
    +                        _value_type: PhantomData,
    +                    },
    +                    elements: EvaluatesTo {
    +                        expression: ContextValue(
    +                            ContextValue {
    +                                value_name: "account_signatories",
    +                            },
    +                        ),
    +                        _value_type: PhantomData,
    +                    },
    +                },
    +            ),
    +            _value_type: PhantomData,
    +        },
    +    ),
    +    metadata: Metadata {
    +        map: {},
    +    },
    +    roles: {},
    +}
    Account {
    +    id: Id {
    +        name: "white_rabbit",
    +        domain_name: Id {
    +            name: "looking_glass",
    +        },
    +    },
    +    assets: {},
    +    signatories: {
    +        { digest: "ed25519", payload: "A4C4DADD9F18B0F63D6A420151FE0748D785475DEC63034A15FCF999CEDA1E65",
    +        },
    +    },
    +    permission_tokens: {},
    +    signature_check_condition: SignatureCheckCondition(
    +        EvaluatesTo {
    +            expression: ContainsAny(
    +                ContainsAny {
    +                    collection: EvaluatesTo {
    +                        expression: ContextValue(
    +                            ContextValue {
    +                                value_name: "transaction_signatories",
    +                            },
    +                        ),
    +                        _value_type: PhantomData,
    +                    },
    +                    elements: EvaluatesTo {
    +                        expression: ContextValue(
    +                            ContextValue {
    +                                value_name: "account_signatories",
    +                            },
    +                        ),
    +                        _value_type: PhantomData,
    +                    },
    +                },
    +            ),
    +            _value_type: PhantomData,
    +        },
    +    ),
    +    metadata: Metadata {
    +        map: {},
    +    },
    +    roles: {},
    +}

    Now that the network and users are registered, it is possible to mint assets.

    5. Registering and minting assets

    In order to mint assets, you need to register the asset first. We are going to register the tea token within the looking_glass network. To do that, run:

    bash
    $ ./iroha_client_cli asset register \
    +    --id="tea#looking_glass" \
    +    --value-type=Quantity
    $ ./iroha_client_cli asset register \
    +    --id="tea#looking_glass" \
    +    --value-type=Quantity

    The tea asset is now registered within the looking_glass network. The output within the CLI is the same as with other commands, you will be able to see that there are new events in the pipeline.

    With the asset created, you can now mint tokens. Run:

    bash
    $ ./iroha_client_cli asset mint \
    +    --account="mad_hatter@looking_glass" \
    +    --asset="tea#looking_glass" \
    +    --quantity="100"
    $ ./iroha_client_cli asset mint \
    +    --account="mad_hatter@looking_glass" \
    +    --asset="tea#looking_glass" \
    +    --quantity="100"

    After minting one hundred tea, you will see more pipeline events in the logger, and you can also query the assets that you have just minted:

    bash
    $ ./iroha_client_cli asset list all
    $ ./iroha_client_cli asset list all

    After running this command, you will be able to see the tokens currently available on the network:

    rust
    [
    +    Asset {
    +        id: Id {
    +            definition_id: DefinitionId {
    +                name: "tea",
    +                domain_name: "looking_glass",
    +            },
    +            account_id: Id {
    +                name: "mad_hatter",
    +                domain_name: "looking_glass",
    +            },
    +        },
    +        value: Quantity(
    +            100,
    +        ),
    +    },
    +    Asset {
    +        id: Id {
    +            definition_id: DefinitionId {
    +                name: "rose",
    +                domain_name: "wonderland",
    +            },
    +            account_id: Id {
    +                name: "alice",
    +                domain_name: "wonderland",
    +            },
    +        },
    +        value: Quantity(
    +            13,
    +        ),
    +    },
    +]
    [
    +    Asset {
    +        id: Id {
    +            definition_id: DefinitionId {
    +                name: "tea",
    +                domain_name: "looking_glass",
    +            },
    +            account_id: Id {
    +                name: "mad_hatter",
    +                domain_name: "looking_glass",
    +            },
    +        },
    +        value: Quantity(
    +            100,
    +        ),
    +    },
    +    Asset {
    +        id: Id {
    +            definition_id: DefinitionId {
    +                name: "rose",
    +                domain_name: "wonderland",
    +            },
    +            account_id: Id {
    +                name: "alice",
    +                domain_name: "wonderland",
    +            },
    +        },
    +        value: Quantity(
    +            13,
    +        ),
    +    },
    +]

    INFO

    Iroha 2 currently doesn't validate the account names, so you could (in theory) add invalid characters to the name, e.g. spaces. We recommend sticking to English alphanumeric characters and underscores.

    6. Transferring assets

    After minting the assets, you can transfer some of Mad Hatter's tea to White Rabbit:

    bash
    $ ./iroha_client_cli asset transfer --from mad_hatter@looking_glass --to white_rabbit@looking_glass --asset-id tea#looking_glass --quantity 5
    $ ./iroha_client_cli asset transfer --from mad_hatter@looking_glass --to white_rabbit@looking_glass --asset-id tea#looking_glass --quantity 5

    7. Burning assets

    Burning assets is quite similar to minting them:

    bash
    $ ./iroha_client_cli asset burn \
    +    --account="mad_hatter@looking_glass" \
    +    --asset="tea#looking_glass" \
    +    --quantity="10"
    $ ./iroha_client_cli asset burn \
    +    --account="mad_hatter@looking_glass" \
    +    --asset="tea#looking_glass" \
    +    --quantity="10"

    8. Visualizing outputs

    Although you will get a constant data feed of the network within the terminal running docker compose, you can also configure an output to listen to events on the network.

    From a terminal tab/window run:

    bash
    $ ./iroha_client_cli events pipeline
    $ ./iroha_client_cli events pipeline

    This view will output all the events related to Iroha 2, such as transactions, block validations, or data events (e.g. when the in-memory representation of the blockchain gets committed to the hard disk).

    The output would look like this:

    rust
    Iroha Client CLI: build v0.0.1 [release]
    +User: alice@wonderland
    +{"PUBLIC_KEY":"ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0","PRIVATE_KEY":{"digest_function":"ed25519","payload":"9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"},"ACCOUNT_ID":{"name":"alice","domain_name":"wonderland"},"TORII_API_URL":"http://127.0.0.1:8080","TORII_STATUS_URL":"127.0.0.1:8180","TRANSACTION_TIME_TO_LIVE_MS":100000,"TRANSACTION_STATUS_TIMEOUT_MS":10000,"MAX_INSTRUCTION_NUMBER":4096,"ADD_TRANSACTION_NONCE":false,"LOGGER_CONFIGURATION":{"MAX_LOG_LEVEL":"INFO","TELEMETRY_CAPACITY":1000,"COMPACT_MODE":false,"LOG_FILE_PATH":null}}
    +Listening to events with filter: Pipeline(EventFilter { entity: None, hash: None })
    +Pipeline(
    +    Event {
    +        entity_type: Transaction,
    +        status: Validating,
    +        hash: 10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77,
    +    },
    +)
    +Pipeline(
    +    Event {
    +        entity_type: Block,
    +        status: Validating,
    +        hash: 944269f27e1ed8882c6c8c74bd641bc3551ef5651320f4e1e1be11a470b4e3c3,
    +    },
    +)
    +Pipeline(
    +    Event {
    +        entity_type: Transaction,
    +        status: Committed,
    +        hash: 10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77,
    +    },
    +)
    Iroha Client CLI: build v0.0.1 [release]
    +User: alice@wonderland
    +{"PUBLIC_KEY":"ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0","PRIVATE_KEY":{"digest_function":"ed25519","payload":"9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"},"ACCOUNT_ID":{"name":"alice","domain_name":"wonderland"},"TORII_API_URL":"http://127.0.0.1:8080","TORII_STATUS_URL":"127.0.0.1:8180","TRANSACTION_TIME_TO_LIVE_MS":100000,"TRANSACTION_STATUS_TIMEOUT_MS":10000,"MAX_INSTRUCTION_NUMBER":4096,"ADD_TRANSACTION_NONCE":false,"LOGGER_CONFIGURATION":{"MAX_LOG_LEVEL":"INFO","TELEMETRY_CAPACITY":1000,"COMPACT_MODE":false,"LOG_FILE_PATH":null}}
    +Listening to events with filter: Pipeline(EventFilter { entity: None, hash: None })
    +Pipeline(
    +    Event {
    +        entity_type: Transaction,
    +        status: Validating,
    +        hash: 10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77,
    +    },
    +)
    +Pipeline(
    +    Event {
    +        entity_type: Block,
    +        status: Validating,
    +        hash: 944269f27e1ed8882c6c8c74bd641bc3551ef5651320f4e1e1be11a470b4e3c3,
    +    },
    +)
    +Pipeline(
    +    Event {
    +        entity_type: Transaction,
    +        status: Committed,
    +        hash: 10fadf7b7fb8036d00bbd8cadc5358193b04ad6573537463acef2091ba4d0e77,
    +    },
    +)
    + + + + \ No newline at end of file diff --git a/guide/get-started/build.html b/guide/get-started/build.html new file mode 100644 index 000000000..6b01da034 --- /dev/null +++ b/guide/get-started/build.html @@ -0,0 +1,34 @@ + + + + + + Build Iroha 2 Client | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Build Iroha 2 Client

    After you have installed Iroha from GitHub, follow these instructions to build the Iroha 2 client:

    1. Install the Rust Toolchain
    2. Build Iroha 2 Client

    Install the Rust Toolchain

    You need a working Rust toolchain: cargo, rustc v1.60 and up. [1]

    Installing the Rust Toolchain is normally a straightforward process, but we've added troubleshooting details for each stage, in case you experience issues with the installation process.

    The easiest way to get the official rustup script is to run:

    bash
    $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

    Or, alternatively, you can install rustup via your operating system’s package manager.

    TIP

    If you know what you're doing, you can also install the Rust toolchain directly, without rustup.

    If you chose to use the one-line curl script, you will be guided through the setup process. Just go with the defaults.

    Build Iroha Client

    1. Navigate to the directory containing the Iroha repository. If you followed the installation instructions here, run:

      bash
      $ cd ~/Git/iroha
      $ cd ~/Git/iroha
    2. Build the Iroha 2 client using:

      bash
      $ cargo build -p iroha_client_cli --release
      $ cargo build -p iroha_client_cli --release

      Build artifacts are created in the ./target/debug/ directory.

      INFO

      We take pride in the fact that Iroha is extremely quick to compile. For reference, compiling hyperledger/substrate takes a good part of ten minutes on a modern M1 machine. Iroha, in comparison, compiles in around one minute.


    1. If you're having issues installing Rust compatible with our code (2021 edition), please consult the troubleshooting section. ↩︎

    + + + + \ No newline at end of file diff --git a/guide/get-started/index.html b/guide/get-started/index.html new file mode 100644 index 000000000..9316d85fd --- /dev/null +++ b/guide/get-started/index.html @@ -0,0 +1,34 @@ + + + + + + Get Started | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Get Started

    1. Install Iroha 2.
    2. Build Iroha 2
    3. Follow one of the language-specific guides to learn how to set up and configure Iroha 2, register a domain and an account, register and mind assets, and visualize outputs:

    If you have previously worked with Iroha, start with our comparison of Iroha 1 and Iroha 2. That will help you understand the differences between the two versions and upgrade to the newer one.

    Before you dive into tutorials and deploy Iroha 2, we suggest you read through the Security section to learn about core security principles and operational security measures that are necessary to ensure the safety and validity of data and assets. This section also covers cryptographic keys, how to generate them, and how to store them securely.

    Check the tutorial where you can follow one of the available language-specific guides in Bash, Rust, Kotlin, Javascript, or Python. The guides introduce you to the basic concepts and provide code snippets that you can run yourself.

    In the Blockchain chapter you can find documentation for Iroha features, such as Iroha Special Instructions, triggers, queries.

    The Configuration and Management section explains Iroha 2 configuration files in great detail and covers topics such as genesis blocks and accounts, client configuration, and public and private modes.

    + + + + \ No newline at end of file diff --git a/guide/get-started/install.html b/guide/get-started/install.html new file mode 100644 index 000000000..b7e8a78c7 --- /dev/null +++ b/guide/get-started/install.html @@ -0,0 +1,34 @@ + + + + + + Install Iroha 2 | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Install Iroha 2

    To install Iroha, follow these instructions:

    1. Choose the Iroha 2 version to work with
    2. Install prerequisites
    3. Install Iroha from GitHub

    Choose Version

    You can choose to work with one of the following versions of Iroha: dev, lts, or stable:

    VersionDescription
    iroha2-devThis is the latest state of Iroha and it is not meant to be used in production. This is an intermediate, untested, potentially broken state, and therefore we cannot guarantee that it is usable or stable.
    iroha2-stableThis is the latest released version of Iroha. Stable versions are tested and released once a month, in accordance with our release schedule. You can use the stable version in production, and we will offer you tech support.
    iroha2-ltsThis is the long-term supported version. We guarantee its compatibility with SDKs, toolsets, and block stores released when it came out. We recommend using the LTS version in production since it is the version that will not change much over time. Similarly to the stable version, we offer tech support for LTS.

    Install Prerequisites

    To install Iroha from GitHub, you need:

    Install OpenSSL

    Make sure you have OpenSSL installed. Note that in most Linux setups it is already available to you.

    • Install OpenSSL on Ubuntu:

      bash
      $ sudo apt-get install libssl-dev
      $ sudo apt-get install libssl-dev
    • Install OpenSSL on macOS using brew:

      bash
      $ brew install openssl
      $ brew install openssl

    Check the OpenSSL installation guide for details.

    Install Iroha from GitHub

    1. If you haven’t already, you might want to create a clean folder for Iroha 2, to keep things tidy.

      bash
      $ mkdir -p ~/Git
      $ mkdir -p ~/Git

      TIP

      On macOS, if you get fatal: could not create work tree dir 'iroha': Read-only file system, that’s because the home folder is not a real file system. The fix is to create the Git folder.

    2. Enter the directory you have just created using

      bash
      $ cd ~/Git
      $ cd ~/Git
    3. Then clone the Iroha git repository into the folder ~/Git/iroha and checkout the branch you prefer to work on. You can use the iroha2-lts branch, which is the long-term support release, or the latest stable release branch (iroha2-stable). To clone the repository and checkout the stable release, run:

      bash
      $ git clone https://github.com/hyperledger/iroha.git --branch iroha2-stable
      $ git clone https://github.com/hyperledger/iroha.git --branch iroha2-stable

      This will fetch all of Iroha, including Iroha 1, and the iroha2-dev branch, which we will touch upon later.

    What's next

    + + + + \ No newline at end of file diff --git a/guide/get-started/javascript.html b/guide/get-started/javascript.html new file mode 100644 index 000000000..34181a786 --- /dev/null +++ b/guide/get-started/javascript.html @@ -0,0 +1,1396 @@ + + + + + + JavaScript/TypeScript Guide | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    JavaScript/TypeScript Guide

    INFO

    This guide targets @iroha2/client and @iroha/data-model version ^5.0, which targets Iroha 2 stable (2.0.0-pre-rc.13, c4af68c4f7959b154eb5380aa93c894e2e63fe4e).

    INFO

    This guide assumes you are familiar with Node.js and NPM ecosystem.

    1. Client Installation

    The Iroha 2 JavaScript library consists of multiple packages:

    PackageDescription
    clientSubmits requests to Iroha Peer
    data-modelProvides SCALE (Simple Concatenated Aggregate Little-Endian)-codecs for the Iroha 2 Data Model
    crypto-coreContains cryptography types
    crypto-target-nodeProvides compiled crypto WASM (Web Assembly) for the Node.js environment
    crypto-target-webProvides compiled crypto WASM for native Web (ESM)
    crypto-target-bundlerProvides compiled crypto WASM to use with bundlers such as Webpack

    All of these are published under the @iroha2 scope into Iroha Nexus Registry. In the future, they will be published in the main NPM Registry.

    INFO

    You can also work with the sources in Iroha Javascript repository, where the active development is happening. Clone the repository and check out the iroha2 branch:

    bash
    $ git clone https://github.com/hyperledger/iroha-javascript.git --branch iroha2
    $ git clone https://github.com/hyperledger/iroha-javascript.git --branch iroha2

    Please note that this guide does not cover the details of this workflow.

    While we've taken great care to decouple the packages, so you could minimise their footprint, for the purposes of this tutorial, it's better to install everything.

    The installation consists of two steps: setting up a registry and then installing the packages you need.

    1. Set up a registry. In shell, run:

      bash
      $ echo "@iroha2:registry=https://nexus.iroha.tech/repository/npm-group/" > .npmrc
      $ echo "@iroha2:registry=https://nexus.iroha.tech/repository/npm-group/" > .npmrc
    2. Install Iroha 2 packages as any other NPM package. If you are following the tutorial, we recommend installing all of the following:

      bash
      $ npm i @iroha2/client
      +$ npm i @iroha2/data-model
      +$ npm i @iroha2/crypto-core
      +$ npm i @iroha2/crypto-target-node
      +$ npm i @iroha2/crypto-target-web
      +$ npm i @iroha2/crypto-target-bundler
      $ npm i @iroha2/client
      +$ npm i @iroha2/data-model
      +$ npm i @iroha2/crypto-core
      +$ npm i @iroha2/crypto-target-node
      +$ npm i @iroha2/crypto-target-web
      +$ npm i @iroha2/crypto-target-bundler

      INFO

      Note that you can use other package managers, such as yarn or pnpm, for a faster installation. For example:

      bash
      $ yarn add @iroha2/data-model
      +$ pnpm add @iroha2/crypto-target-web
      $ yarn add @iroha2/data-model
      +$ pnpm add @iroha2/crypto-target-web

      The set of packages that you need to install depends on what you are trying to achieve. If you only need to play with the Data Model to perform (de-)serialisation, the data-model package is sufficient. If you need to check on a peer in terms of its status or health, then you only need the client library.

    3. Install the following packages as well:

      bash
      $ npm i hada
      +$ npm i tsx -g
      $ npm i hada
      +$ npm i tsx -g
    4. If you are planning to use the Transaction or Query API, you'll also need to inject an appropriate crypto instance into the client at runtime. This has to be adjusted according to your particular environment.

      For example, Node.js users need the following:

      ts
      import { crypto } from '@iroha2/crypto-target-node'
      +import { setCrypto } from '@iroha2/client'
      +
      +setCrypto(crypto)
      import { crypto } from '@iroha2/crypto-target-node'
      +import { setCrypto } from '@iroha2/client'
      +
      +setCrypto(crypto)

      INFO

      Please refer to the documentation of the respective @iroha2/crypto-target-* package, because each case has specific configuration steps. For example, the web target needs to be initialised (via asynchronous init()) before you can use any cryptographic methods.

    NOTE

    When you are creating files in the following steps, you must place them in the same directory that contains node_modules, like so:

    ╭───┬───────────────────┬──────╮
    +│ # │       name        │ type │
    +├───┼───────────────────┼──────┤
    +│ 0 │ node_modules      │ dir  │
    +│ 1 │ addClient.ts      │ file │
    +│ 2 │ example.ts        │ file │
    +│ 3 │ package.json      │ file │
    +│ 4 │ pnpm-lock.yaml    │ file │
    +│ 5 │ registerDomain.ts │ file │
    +╰───┴───────────────────┴──────╯
    ╭───┬───────────────────┬──────╮
    +│ # │       name        │ type │
    +├───┼───────────────────┼──────┤
    +│ 0 │ node_modules      │ dir  │
    +│ 1 │ addClient.ts      │ file │
    +│ 2 │ example.ts        │ file │
    +│ 3 │ package.json      │ file │
    +│ 4 │ pnpm-lock.yaml    │ file │
    +│ 5 │ registerDomain.ts │ file │
    +╰───┴───────────────────┴──────╯

    We recommend using tsx to run the scripts you've created. For example:

    bash
    $ tsx example.ts
    $ tsx example.ts

    2. Client Configuration

    The JavaScript Client is fairly low-level in a sense that it doesn't expose any convenience features like a TransactionBuilder or a ConfigBuilder.

    INFO

    The work on implementing those is underway, and these features will very likely be available in the second round of this tutorial's release.

    Thus, on the plus side, configuration of the client is simple. On the downside, you have to prepare a lot manually.

    You may need to use transactions or queries, so before we initialize the client, let's set up this part. Let's assume that you have stringified public & private keys (more on that later). Thus, a key-pair generation could look like this:

    ts
    import { crypto } from '@iroha2/crypto-target-node'
    +
    +const keyPair = crypto.KeyPair.fromJSON({
    +  public_key: 'ed0120e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3',
    +  private_key: {
    +    digest_function: 'ed25519',
    +    payload:
    +      'de757bcb79f4c63e8fa0795edc26f86dfdba189b846e903d0b732bb644607720e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3',
    +  },
    +})
    import { crypto } from '@iroha2/crypto-target-node'
    +
    +const keyPair = crypto.KeyPair.fromJSON({
    +  public_key: 'ed0120e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3',
    +  private_key: {
    +    digest_function: 'ed25519',
    +    payload:
    +      'de757bcb79f4c63e8fa0795edc26f86dfdba189b846e903d0b732bb644607720e555d194e8822da35ac541ce9eec8b45058f4d294d9426ef97ba92698766f7d3',
    +  },
    +})

    When you have a key pair, you might create a Signer using the key pair:

    ts
    import { cryptoTypes } from '@iroha2/crypto-core'
    +import { Signer } from '@iroha2/client'
    +import { AccountId, DomainId } from '@iroha2/data-model'
    +
    +// Key pair from previous step
    +declare const keyPair: cryptoTypes.KeyPair
    +
    +const accountId = AccountId({
    +  // Account name
    +  name: 'alice',
    +  // The domain where this account is registered
    +  domain_id: DomainId({
    +    name: 'wonderland',
    +  }),
    +})
    +
    +const signer = new Signer(accountId, keyPair)
    import { cryptoTypes } from '@iroha2/crypto-core'
    +import { Signer } from '@iroha2/client'
    +import { AccountId, DomainId } from '@iroha2/data-model'
    +
    +// Key pair from previous step
    +declare const keyPair: cryptoTypes.KeyPair
    +
    +const accountId = AccountId({
    +  // Account name
    +  name: 'alice',
    +  // The domain where this account is registered
    +  domain_id: DomainId({
    +    name: 'wonderland',
    +  }),
    +})
    +
    +const signer = new Signer(accountId, keyPair)

    Now we're able to make signatures with signer.sign(binary)! However, to interact with Iroha, we need to be able to do more than just sign. We would need to send something to Iroha, like transactions or queries. Torii will help us with that.

    Torii handles HTTP / WebSocket communications with Iroha. We will use it to communicate with Iroha endpoints. With the help of Torii we can:

    • Submit transactions with Torii.submit()
    • Send queries with Torii.request()
    • Listen for events with Torii.listenForEvents()
    • Listen for blocks stream with Torii.listenForBlocksStream()
    • and so on

    Torii is a stateless object, a compendium of methods. You can look at it as if it is a class with only static methods. Each method has its own requirements to be passed in — some of them only need an HTTP transport and Iroha Torii Telemetry URL, others — a WebSocket transport and Iroha Torii API URL. To better understand how Torii is used, look at this example:

    ts
    import { Torii } from '@iroha2/client'
    +import { VersionedSignedQueryRequest } from '@iroha2/data-model'
    +
    +// --snip--
    +declare const query: VersionedSignedQueryRequest
    +
    +const result = await Torii.request(
    +  {
    +    fetch,
    +    apiURL: 'http://127.0.0.1:8080',
    +  },
    +  query,
    +)
    import { Torii } from '@iroha2/client'
    +import { VersionedSignedQueryRequest } from '@iroha2/data-model'
    +
    +// --snip--
    +declare const query: VersionedSignedQueryRequest
    +
    +const result = await Torii.request(
    +  {
    +    fetch,
    +    apiURL: 'http://127.0.0.1:8080',
    +  },
    +  query,
    +)

    In this example, we pass fetch (the HTTP transport) and apiURL as the first parameter, and the query itself as the second.

    To work with Torii, we need to know Iroha Torii URLs. Our Iroha Peer is configured to listen for API endpoints at http://127.0.0.1:8080 and for telemetry endpoints at http://127.0.0.1:8081. Then, we need to provide appropriate HTTP / WebSocket adapters which Torii will use[1]. These adapters depend on the environment in which you are going to use @iroha2/client.

    In Node.js, the full list of Torii requirements (i.e. covering all its methods) will look like this:

    ts
    import {
    +  ToriiRequirementsForApiHttp,
    +  ToriiRequirementsForApiWebSocket,
    +  ToriiRequirementsForTelemetry,
    +} from '@iroha2/client'
    +import { adapter as WS } from '@iroha2/client/web-socket/node'
    +
    +import nodeFetch from 'node-fetch'
    +// another alternative
    +import { fetch as undiciFetch } from 'undici'
    +
    +const toriiRequirements: ToriiRequirementsForApiHttp &
    +  ToriiRequirementsForApiWebSocket &
    +  ToriiRequirementsForTelemetry = {
    +  apiURL: 'http://127.0.0.1:8080',
    +  telemetryURL: 'http://127.0.0.1:8081',
    +  ws: WS,
    +  // type assertion is acceptable here
    +  // you can pass `undiciFetch` here as well
    +  fetch: nodeFetch as typeof fetch,
    +}
    import {
    +  ToriiRequirementsForApiHttp,
    +  ToriiRequirementsForApiWebSocket,
    +  ToriiRequirementsForTelemetry,
    +} from '@iroha2/client'
    +import { adapter as WS } from '@iroha2/client/web-socket/node'
    +
    +import nodeFetch from 'node-fetch'
    +// another alternative
    +import { fetch as undiciFetch } from 'undici'
    +
    +const toriiRequirements: ToriiRequirementsForApiHttp &
    +  ToriiRequirementsForApiWebSocket &
    +  ToriiRequirementsForTelemetry = {
    +  apiURL: 'http://127.0.0.1:8080',
    +  telemetryURL: 'http://127.0.0.1:8081',
    +  ws: WS,
    +  // type assertion is acceptable here
    +  // you can pass `undiciFetch` here as well
    +  fetch: nodeFetch as typeof fetch,
    +}

    TIP

    In the example above, we use node-fetch package which implements Fetch API in Node.js. However, you can use undici as well.

    INFO

    fetch: nodeFetch as typeof fetch type assertion is acceptable here for a reason. Torii expects the "classic", native fetch function, which is available natively in Browser. However, both node-fetch and undici don't provide fetch that is 100% compatible with the native one. Since Torii doesn't rely on those corner-features that are partially provided by node-fetch and undici, it's fine to ignore the TypeScript error here.

    And here is a sample of full Torii in-Browser requirements:

    ts
    import {
    +  ToriiRequirementsForApiHttp,
    +  ToriiRequirementsForApiWebSocket,
    +  ToriiRequirementsForTelemetry,
    +} from '@iroha2/client'
    +import { adapter as WS } from '@iroha2/client/web-socket/native'
    +
    +const toriiRequirements: ToriiRequirementsForApiHttp &
    +  ToriiRequirementsForApiWebSocket &
    +  ToriiRequirementsForTelemetry = {
    +  apiURL: 'http://127.0.0.1:8080',
    +  telemetryURL: 'http://127.0.0.1:8081',
    +  ws: WS,
    +  fetch:
    +    // passing globally available `fetch`, but binding it to `window`
    +    // to avoid `TypeError: "'fetch' called on an
    +    //           object that does not implement interface Window."`
    +    fetch.bind(window),
    +}
    import {
    +  ToriiRequirementsForApiHttp,
    +  ToriiRequirementsForApiWebSocket,
    +  ToriiRequirementsForTelemetry,
    +} from '@iroha2/client'
    +import { adapter as WS } from '@iroha2/client/web-socket/native'
    +
    +const toriiRequirements: ToriiRequirementsForApiHttp &
    +  ToriiRequirementsForApiWebSocket &
    +  ToriiRequirementsForTelemetry = {
    +  apiURL: 'http://127.0.0.1:8080',
    +  telemetryURL: 'http://127.0.0.1:8081',
    +  ws: WS,
    +  fetch:
    +    // passing globally available `fetch`, but binding it to `window`
    +    // to avoid `TypeError: "'fetch' called on an
    +    //           object that does not implement interface Window."`
    +    fetch.bind(window),
    +}

    NOTE

    We make fetch.bind(window) to avoid TypeError: "'fetch' called on an object that does not implement interface Window.".

    Great! Now we have signer and Torii requirements to work with. Finally, we can create a Client:

    ts
    import { Client, Signer, ToriiRequirementsForApiHttp } from '@iroha2/client'
    +import { Executable } from '@iroha2/data-model'
    +
    +// --snip--
    +declare const signer: Signer
    +declare const toriiRequirements: ToriiRequirementsForApiHttp
    +
    +const client = new Client({ signer })
    +
    +// `Client` will sign & wrap `Executable` into `VersionedSignedTransaction`
    +declare const exec: Executable
    +await client.submitExecutable(toriiRequirements, exec)
    import { Client, Signer, ToriiRequirementsForApiHttp } from '@iroha2/client'
    +import { Executable } from '@iroha2/data-model'
    +
    +// --snip--
    +declare const signer: Signer
    +declare const toriiRequirements: ToriiRequirementsForApiHttp
    +
    +const client = new Client({ signer })
    +
    +// `Client` will sign & wrap `Executable` into `VersionedSignedTransaction`
    +declare const exec: Executable
    +await client.submitExecutable(toriiRequirements, exec)

    Client provides useful utilities for transactions and queries. You can also use Torii to communicate with the endpoints directly. Signer is accessible with client.signer.

    3. Registering a Domain

    Here we see how similar the JavaScript code is to the Rust counterpart. It should be emphasised that the JavaScript library is a thin wrapper: It doesn't provide any special builder structures, meaning you have to work with bare-bones compiled Data Model structures and define all internal fields explicitly.

    Doubly so, since JavaScript employs many implicit conversions, we highly recommend that you employ TypeScript. This makes many errors far easier to debug, but, unfortunately, results in more boilerplates.

    Let's register a new domain named looking_glass using our current account, alice@wondeland.

    First, we need to import necessary models and a pre-configured client instance:

    ts
    import { Client, ToriiRequirementsForApiHttp } from '@iroha2/client'
    +import {
    +  DomainId,
    +  EvaluatesToRegistrableBox,
    +  Executable,
    +  Expression,
    +  IdentifiableBox,
    +  Instruction,
    +  MapNameValue,
    +  Metadata,
    +  NewDomain,
    +  OptionIpfsPath,
    +  QueryBox,
    +  RegisterBox,
    +  Value,
    +  VecInstruction,
    +} from '@iroha2/data-model'
    +
    +// --snip--
    +declare const client: Client
    +declare const toriiRequirements: ToriiRequirementsForApiHttp
    import { Client, ToriiRequirementsForApiHttp } from '@iroha2/client'
    +import {
    +  DomainId,
    +  EvaluatesToRegistrableBox,
    +  Executable,
    +  Expression,
    +  IdentifiableBox,
    +  Instruction,
    +  MapNameValue,
    +  Metadata,
    +  NewDomain,
    +  OptionIpfsPath,
    +  QueryBox,
    +  RegisterBox,
    +  Value,
    +  VecInstruction,
    +} from '@iroha2/data-model'
    +
    +// --snip--
    +declare const client: Client
    +declare const toriiRequirements: ToriiRequirementsForApiHttp

    To register a new domain, we need to submit a transaction with a single instruction: to register a new domain. Let's wrap it all in an async function:

    ts
    async function registerDomain(domainName: string) {
    +  const registerBox = RegisterBox({
    +    object: EvaluatesToRegistrableBox({
    +      expression: Expression(
    +        'Raw',
    +        Value(
    +          'Identifiable',
    +          IdentifiableBox(
    +            'NewDomain',
    +            NewDomain({
    +              id: DomainId({
    +                name: domainName, 
    +              }),
    +              metadata: Metadata({ map: MapNameValue(new Map()) }),
    +              logo: OptionIpfsPath('None'),
    +            }),
    +          ),
    +        ),
    +      ),
    +    }),
    +  })
    +
    +  await client.submitExecutable(
    +    toriiRequirements,
    +    Executable('Instructions', VecInstruction([Instruction('Register', registerBox)])),
    +  )
    +}
    async function registerDomain(domainName: string) {
    +  const registerBox = RegisterBox({
    +    object: EvaluatesToRegistrableBox({
    +      expression: Expression(
    +        'Raw',
    +        Value(
    +          'Identifiable',
    +          IdentifiableBox(
    +            'NewDomain',
    +            NewDomain({
    +              id: DomainId({
    +                name: domainName, 
    +              }),
    +              metadata: Metadata({ map: MapNameValue(new Map()) }),
    +              logo: OptionIpfsPath('None'),
    +            }),
    +          ),
    +        ),
    +      ),
    +    }),
    +  })
    +
    +  await client.submitExecutable(
    +    toriiRequirements,
    +    Executable('Instructions', VecInstruction([Instruction('Register', registerBox)])),
    +  )
    +}

    Which we use to register the domain like so:

    ts
    await registerDomain('looking_glass')
    await registerDomain('looking_glass')

    We can also use Query API to ensure that the new domain is created. Let's create another function that wraps that functionality:

    ts
    async function ensureDomainExistence(domainName: string) {
    +  // Query all domains
    +  const result = await client.requestWithQueryBox(
    +    toriiRequirements,
    +    QueryBox('FindAllDomains', null),
    +  )
    +
    +  // Display the request status
    +  console.log('%o', result)
    +
    +  // Obtain the domain
    +  const domain = result
    +    .as('Ok')
    +    .result.enum.as('Vec')
    +    .map((x) => x.enum.as('Identifiable').enum.as('Domain'))
    +    .find((x) => x.id.name === domainName) 
    +
    +  // Throw an error if the domain is unavailable
    +  if (!domain) throw new Error('Not found')
    +}
    async function ensureDomainExistence(domainName: string) {
    +  // Query all domains
    +  const result = await client.requestWithQueryBox(
    +    toriiRequirements,
    +    QueryBox('FindAllDomains', null),
    +  )
    +
    +  // Display the request status
    +  console.log('%o', result)
    +
    +  // Obtain the domain
    +  const domain = result
    +    .as('Ok')
    +    .result.enum.as('Vec')
    +    .map((x) => x.enum.as('Identifiable').enum.as('Domain'))
    +    .find((x) => x.id.name === domainName) 
    +
    +  // Throw an error if the domain is unavailable
    +  if (!domain) throw new Error('Not found')
    +}

    Now you can ensure that domain is created by calling:

    ts
    await ensureDomainExistence('looking_glass')
    await ensureDomainExistence('looking_glass')

    4. Registering an Account

    Registering an account is a bit more involved than registering a domain. With a domain, the only concern is the domain name. However, with an account, there are a few more things to worry about.

    First of all, we need to create an AccountId. Note that we can only register an account to an existing domain. The best UX design practices dictate that you should check if the requested domain exists now, and if it doesn't, suggest a fix to the user. After that, we can create a new account named white_rabbit.

    Imports we need:

    ts
    import {
    +  AccountId,
    +  DomainId,
    +  EvaluatesToRegistrableBox,
    +  Expression,
    +  IdentifiableBox,
    +  Instruction,
    +  MapNameValue,
    +  Metadata,
    +  NewAccount,
    +  PublicKey,
    +  RegisterBox,
    +  Value,
    +  VecPublicKey,
    +} from '@iroha2/data-model'
    import {
    +  AccountId,
    +  DomainId,
    +  EvaluatesToRegistrableBox,
    +  Expression,
    +  IdentifiableBox,
    +  Instruction,
    +  MapNameValue,
    +  Metadata,
    +  NewAccount,
    +  PublicKey,
    +  RegisterBox,
    +  Value,
    +  VecPublicKey,
    +} from '@iroha2/data-model'

    The AccountId structure:

    ts
    const accountId = AccountId({
    +  name: 'white_rabbit',
    +  domain_id: DomainId({
    +    name: 'looking_glass',
    +  }),
    +})
    const accountId = AccountId({
    +  name: 'white_rabbit',
    +  domain_id: DomainId({
    +    name: 'looking_glass',
    +  }),
    +})

    Second, you should provide the account with a public key. It is tempting to generate both it and the private key at this time, but it isn't the brightest idea. Remember that the white_rabbit trusts you, alice@wonderland, to create an account for them in the domain looking_glass, but doesn't want you to have access to that account after creation.

    If you gave white_rabbit a key that you generated yourself, how would they know if you don't have a copy of their private key? Instead, the best way is to ask white_rabbit to generate a new key-pair, and give you the public half of it.

    ts
    const pubKey = PublicKey({
    +  payload: new Uint8Array([
    +    /* put bytes here */
    +  ]),
    +  digest_function: 'some_digest',
    +})
    const pubKey = PublicKey({
    +  payload: new Uint8Array([
    +    /* put bytes here */
    +  ]),
    +  digest_function: 'some_digest',
    +})

    Only then do we build an instruction from it:

    ts
    const registerAccountInstruction = Instruction(
    +  'Register',
    +  RegisterBox({
    +    object: EvaluatesToRegistrableBox({
    +      expression: Expression(
    +        'Raw',
    +        Value(
    +          'Identifiable',
    +          IdentifiableBox(
    +            'NewAccount',
    +            NewAccount({
    +              id: accountId, 
    +              signatories: VecPublicKey([pubKey]),
    +              metadata: Metadata({ map: MapNameValue(new Map()) }),
    +            }),
    +          ),
    +        ),
    +      ),
    +    }),
    +  }),
    +)
    const registerAccountInstruction = Instruction(
    +  'Register',
    +  RegisterBox({
    +    object: EvaluatesToRegistrableBox({
    +      expression: Expression(
    +        'Raw',
    +        Value(
    +          'Identifiable',
    +          IdentifiableBox(
    +            'NewAccount',
    +            NewAccount({
    +              id: accountId, 
    +              signatories: VecPublicKey([pubKey]),
    +              metadata: Metadata({ map: MapNameValue(new Map()) }),
    +            }),
    +          ),
    +        ),
    +      ),
    +    }),
    +  }),
    +)

    Which is then wrapped in a transaction and submitted to the peer the same way as in the previous section when we registered a domain.

    5. Registering and minting assets

    Iroha has been built with few underlying assumptions about what the assets need to be in terms of their value type and characteristics (fungible or non-fungible, mintable or non-mintable).

    In JS, you can create a new asset with the following construction:

    ts
    import {
    +  AssetDefinition,
    +  AssetDefinitionId,
    +  AssetValueType,
    +  DomainId,
    +  EvaluatesToRegistrableBox,
    +  Expression,
    +  IdentifiableBox,
    +  Instruction,
    +  MapNameValue,
    +  Metadata,
    +  Mintable,
    +  RegisterBox,
    +  Value,
    +} from '@iroha2/data-model'
    +
    +const time = AssetDefinition({
    +  value_type: AssetValueType('Quantity'),
    +  id: AssetDefinitionId({
    +    name: 'time',
    +    domain_id: DomainId({ name: 'looking_glass' }),
    +  }),
    +  metadata: Metadata({ map: MapNameValue(new Map()) }),
    +  mintable: Mintable('Infinitely'), // If only we could mint more time.
    +})
    +
    +const register = Instruction(
    +  'Register',
    +  RegisterBox({
    +    object: EvaluatesToRegistrableBox({
    +      expression: Expression(
    +        'Raw',
    +        Value('Identifiable', IdentifiableBox('AssetDefinition', time)), 
    +      ),
    +    }),
    +  }),
    +)
    import {
    +  AssetDefinition,
    +  AssetDefinitionId,
    +  AssetValueType,
    +  DomainId,
    +  EvaluatesToRegistrableBox,
    +  Expression,
    +  IdentifiableBox,
    +  Instruction,
    +  MapNameValue,
    +  Metadata,
    +  Mintable,
    +  RegisterBox,
    +  Value,
    +} from '@iroha2/data-model'
    +
    +const time = AssetDefinition({
    +  value_type: AssetValueType('Quantity'),
    +  id: AssetDefinitionId({
    +    name: 'time',
    +    domain_id: DomainId({ name: 'looking_glass' }),
    +  }),
    +  metadata: Metadata({ map: MapNameValue(new Map()) }),
    +  mintable: Mintable('Infinitely'), // If only we could mint more time.
    +})
    +
    +const register = Instruction(
    +  'Register',
    +  RegisterBox({
    +    object: EvaluatesToRegistrableBox({
    +      expression: Expression(
    +        'Raw',
    +        Value('Identifiable', IdentifiableBox('AssetDefinition', time)), 
    +      ),
    +    }),
    +  }),
    +)

    Pay attention to the fact that we have defined the asset as Mintable('Not'). What this means is that we cannot create more of time. The late bunny will always be late, because even the super-user of the blockchain cannot mint more of time than already exists in the genesis block.

    This means that no matter how hard the white_rabbit tries, the time that he has is the time that was given to him at genesis. And since we haven't defined any time in the domain looking_glass at genesis and defined time in a non-mintable fashion afterwards, the white_rabbit is doomed to always be late.

    If we had set mintable: Mintable('Infinitely') on our time asset, we could mint it:

    ts
    import {
    +  AccountId,
    +  AssetDefinitionId,
    +  AssetId,
    +  DomainId,
    +  EvaluatesToIdBox,
    +  EvaluatesToValue,
    +  Expression,
    +  IdBox,
    +  Instruction,
    +  MintBox,
    +  NumericValue,
    +  Value,
    +} from '@iroha2/data-model'
    +
    +const mint = Instruction(
    +  'Mint',
    +  MintBox({
    +    object: EvaluatesToValue({
    +      expression: Expression('Raw', Value('Numeric', NumericValue('U32', 42))),
    +    }),
    +    destination_id: EvaluatesToIdBox({
    +      expression: Expression(
    +        'Raw',
    +        Value(
    +          'Id',
    +          IdBox(
    +            'AssetId',
    +            AssetId({
    +              account_id: AccountId({
    +                name: 'alice',
    +                domain_id: DomainId({
    +                  name: 'wonderland',
    +                }),
    +              }),
    +              definition_id: AssetDefinitionId({
    +                name: 'time',
    +                domain_id: DomainId({ name: 'looking_glass' }),
    +              }),
    +            }),
    +          ),
    +        ),
    +      ),
    +    }),
    +  }),
    +)
    import {
    +  AccountId,
    +  AssetDefinitionId,
    +  AssetId,
    +  DomainId,
    +  EvaluatesToIdBox,
    +  EvaluatesToValue,
    +  Expression,
    +  IdBox,
    +  Instruction,
    +  MintBox,
    +  NumericValue,
    +  Value,
    +} from '@iroha2/data-model'
    +
    +const mint = Instruction(
    +  'Mint',
    +  MintBox({
    +    object: EvaluatesToValue({
    +      expression: Expression('Raw', Value('Numeric', NumericValue('U32', 42))),
    +    }),
    +    destination_id: EvaluatesToIdBox({
    +      expression: Expression(
    +        'Raw',
    +        Value(
    +          'Id',
    +          IdBox(
    +            'AssetId',
    +            AssetId({
    +              account_id: AccountId({
    +                name: 'alice',
    +                domain_id: DomainId({
    +                  name: 'wonderland',
    +                }),
    +              }),
    +              definition_id: AssetDefinitionId({
    +                name: 'time',
    +                domain_id: DomainId({ name: 'looking_glass' }),
    +              }),
    +            }),
    +          ),
    +        ),
    +      ),
    +    }),
    +  }),
    +)

    Again it should be emphasised that an Iroha 2 network is strongly typed. You need to take special care to make sure that only unsigned integers are passed to the Value('U32', ...) factory method. Fixed precision values also need to be taken into consideration. Any attempt to add to or subtract from a negative Fixed-precision value will result in an error.

    6. Transferring assets

    After minting the assets, you can transfer them to another account. In the example below, Alice transfers to Mouse 100 units of time asset:

    ts
    import {
    +  AccountId,
    +  AssetDefinitionId,
    +  AssetId,
    +  DomainId,
    +  EvaluatesToIdBox,
    +  EvaluatesToValue,
    +  Expression,
    +  IdBox,
    +  Instruction,
    +  NumericValue,
    +  TransferBox,
    +  Value,
    +} from '@iroha2/data-model'
    +
    +const domainId = DomainId({
    +  name: 'wonderland',
    +})
    +
    +const assetDefinitionId = AssetDefinitionId({
    +  name: 'time',
    +  domain_id: domainId,
    +})
    +
    +const amountToTransfer = Value('Numeric', NumericValue('U32', 100))
    +
    +const fromAccount = AccountId({
    +  name: 'alice',
    +  domain_id: domainId,
    +})
    +
    +const toAccount = AccountId({
    +  name: 'mouse',
    +  domain_id: domainId,
    +})
    +
    +const evaluatesToAssetId = (assetId: AssetId): EvaluatesToIdBox =>
    +  EvaluatesToIdBox({
    +    expression: Expression('Raw', Value('Id', IdBox('AssetId', assetId))),
    +  })
    +
    +const transferAssetInstruction = Instruction(
    +  'Transfer',
    +  TransferBox({
    +    source_id: evaluatesToAssetId(
    +      AssetId({
    +        definition_id: assetDefinitionId,
    +        account_id: fromAccount,
    +      }),
    +    ),
    +    destination_id: evaluatesToAssetId(
    +      AssetId({
    +        definition_id: assetDefinitionId,
    +        account_id: toAccount,
    +      }),
    +    ),
    +    object: EvaluatesToValue({
    +      expression: Expression('Raw', amountToTransfer),
    +    }),
    +  }),
    +)
    import {
    +  AccountId,
    +  AssetDefinitionId,
    +  AssetId,
    +  DomainId,
    +  EvaluatesToIdBox,
    +  EvaluatesToValue,
    +  Expression,
    +  IdBox,
    +  Instruction,
    +  NumericValue,
    +  TransferBox,
    +  Value,
    +} from '@iroha2/data-model'
    +
    +const domainId = DomainId({
    +  name: 'wonderland',
    +})
    +
    +const assetDefinitionId = AssetDefinitionId({
    +  name: 'time',
    +  domain_id: domainId,
    +})
    +
    +const amountToTransfer = Value('Numeric', NumericValue('U32', 100))
    +
    +const fromAccount = AccountId({
    +  name: 'alice',
    +  domain_id: domainId,
    +})
    +
    +const toAccount = AccountId({
    +  name: 'mouse',
    +  domain_id: domainId,
    +})
    +
    +const evaluatesToAssetId = (assetId: AssetId): EvaluatesToIdBox =>
    +  EvaluatesToIdBox({
    +    expression: Expression('Raw', Value('Id', IdBox('AssetId', assetId))),
    +  })
    +
    +const transferAssetInstruction = Instruction(
    +  'Transfer',
    +  TransferBox({
    +    source_id: evaluatesToAssetId(
    +      AssetId({
    +        definition_id: assetDefinitionId,
    +        account_id: fromAccount,
    +      }),
    +    ),
    +    destination_id: evaluatesToAssetId(
    +      AssetId({
    +        definition_id: assetDefinitionId,
    +        account_id: toAccount,
    +      }),
    +    ),
    +    object: EvaluatesToValue({
    +      expression: Expression('Raw', amountToTransfer),
    +    }),
    +  }),
    +)

    7. Querying for Domains, Accounts and Assets

    TODO

    ts
    import { Client, ToriiRequirementsForApiHttp } from '@iroha2/client'
    +import { QueryBox } from '@iroha2/data-model'
    +
    +declare const client: Client
    +declare const toriiRequirements: ToriiRequirementsForApiHttp
    import { Client, ToriiRequirementsForApiHttp } from '@iroha2/client'
    +import { QueryBox } from '@iroha2/data-model'
    +
    +declare const client: Client
    +declare const toriiRequirements: ToriiRequirementsForApiHttp
    ts
    const result = await client.requestWithQueryBox(
    +  toriiRequirements,
    +  QueryBox('FindAllDomains', null),
    +)
    +
    +const domains = result
    +  .as('Ok')
    +  .result.enum.as('Vec')
    +  .map((x) => x.enum.as('Identifiable').enum.as('Domain'))
    +
    +for (const domain of domains) {
    +  console.log(
    +    `Domain "${domain.id.name}" has ${domain.accounts.size} accounts` +
    +      ` and ${domain.asset_definitions.size} asset definitions`,
    +  )
    +  // => Domain "wonderland" has 5 accounts and 3 asset definitions
    +}
    const result = await client.requestWithQueryBox(
    +  toriiRequirements,
    +  QueryBox('FindAllDomains', null),
    +)
    +
    +const domains = result
    +  .as('Ok')
    +  .result.enum.as('Vec')
    +  .map((x) => x.enum.as('Identifiable').enum.as('Domain'))
    +
    +for (const domain of domains) {
    +  console.log(
    +    `Domain "${domain.id.name}" has ${domain.accounts.size} accounts` +
    +      ` and ${domain.asset_definitions.size} asset definitions`,
    +  )
    +  // => Domain "wonderland" has 5 accounts and 3 asset definitions
    +}
    ts
    const result = await client.requestWithQueryBox(
    +  toriiRequirements,
    +  QueryBox('FindAllAccounts', null),
    +)
    +
    +const accounts = result
    +  .as('Ok')
    +  .result.enum.as('Vec')
    +  .map((x) => x.enum.as('Identifiable').enum.as('Account'))
    +
    +for (const account of accounts) {
    +  console.log(
    +    `Account "${account.id.name}@${account.id.domain_id.name}" ` +
    +      `has ${account.assets.size} assets`,
    +  )
    +  // => Account "alice@wonderland" has 3 assets
    +}
    const result = await client.requestWithQueryBox(
    +  toriiRequirements,
    +  QueryBox('FindAllAccounts', null),
    +)
    +
    +const accounts = result
    +  .as('Ok')
    +  .result.enum.as('Vec')
    +  .map((x) => x.enum.as('Identifiable').enum.as('Account'))
    +
    +for (const account of accounts) {
    +  console.log(
    +    `Account "${account.id.name}@${account.id.domain_id.name}" ` +
    +      `has ${account.assets.size} assets`,
    +  )
    +  // => Account "alice@wonderland" has 3 assets
    +}
    ts
    const result = await client.requestWithQueryBox(
    +  toriiRequirements,
    +  QueryBox('FindAllAssets', null),
    +)
    +
    +const assets = result
    +  .as('Ok')
    +  .result.enum.as('Vec')
    +  .map((x) => x.enum.as('Identifiable').enum.as('Asset'))
    +
    +for (const asset of assets) {
    +  console.log(
    +    `Asset "${asset.id.definition_id.name}#${asset.id.definition_id.domain_id.name}" ` +
    +      `at account "${asset.id.account_id.name}@${asset.id.account_id.domain_id.name}" ` +
    +      `has type "${asset.value.enum.tag}"`,
    +  )
    +  // => Asset "rose#wonderland" at account "alice@wonderland" has type "Quantity"
    +}
    const result = await client.requestWithQueryBox(
    +  toriiRequirements,
    +  QueryBox('FindAllAssets', null),
    +)
    +
    +const assets = result
    +  .as('Ok')
    +  .result.enum.as('Vec')
    +  .map((x) => x.enum.as('Identifiable').enum.as('Asset'))
    +
    +for (const asset of assets) {
    +  console.log(
    +    `Asset "${asset.id.definition_id.name}#${asset.id.definition_id.domain_id.name}" ` +
    +      `at account "${asset.id.account_id.name}@${asset.id.account_id.domain_id.name}" ` +
    +      `has type "${asset.value.enum.tag}"`,
    +  )
    +  // => Asset "rose#wonderland" at account "alice@wonderland" has type "Quantity"
    +}

    8. Visualizing outputs in Web UI

    Finally, we should talk about visualising data. The Rust API is currently the most complete in terms of available queries and instructions. After all, this is the language in which Iroha 2 was built.

    Let's build a small Vue 3 application that uses each API we've discovered in this guide!

    TIP

    In this guide, we are roughly recreating the project that is a part of iroha-javascript integration tests. If you want to see the full project, please refer to the @iroha2/client-test-web sources.

    Our app will consist of 3 main views:

    • Status checker that periodically requests peer status (e.g. current blocks height) and shows it;
    • Domain creator, which is a form to create a new domain with specified name;
    • Listener with a toggle to setup listening for events.

    You can use this folder structure as a reference:

    ╭───┬──────────────────────────────╮
    +│ # │             name             │
    +├───┼──────────────────────────────┤
    +│ 0 │ App.vue                      │
    +│ 1 │ client.ts                    │
    +│ 2 │ components/CreateDomain.vue  │
    +│ 3 │ components/Listener.vue      │
    +│ 4 │ components/StatusChecker.vue │
    +│ 5 │ config.json                  │
    +│ 6 │ crypto.ts                    │
    +│ 7 │ main.ts                      │
    +╰───┴──────────────────────────────╯
    ╭───┬──────────────────────────────╮
    +│ # │             name             │
    +├───┼──────────────────────────────┤
    +│ 0 │ App.vue                      │
    +│ 1 │ client.ts                    │
    +│ 2 │ components/CreateDomain.vue  │
    +│ 3 │ components/Listener.vue      │
    +│ 4 │ components/StatusChecker.vue │
    +│ 5 │ config.json                  │
    +│ 6 │ crypto.ts                    │
    +│ 7 │ main.ts                      │
    +╰───┴──────────────────────────────╯
    json
    {
    +  "torii": {
    +    "apiURL": "http://127.0.0.1:8080",
    +    "telemetryURL": "http://127.0.0.1:8081"
    +  },
    +  "account": {
    +    "name": "alice",
    +    "domain_id": {
    +      "name": "wonderland"
    +    }
    +  },
    +  "public_key": "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",
    +  "private_key": {
    +    "digest_function": "ed25519",
    +    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
    +  }
    +}
    {
    +  "torii": {
    +    "apiURL": "http://127.0.0.1:8080",
    +    "telemetryURL": "http://127.0.0.1:8081"
    +  },
    +  "account": {
    +    "name": "alice",
    +    "domain_id": {
    +      "name": "wonderland"
    +    }
    +  },
    +  "public_key": "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",
    +  "private_key": {
    +    "digest_function": "ed25519",
    +    "payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
    +  }
    +}
    ts
    import { crypto, init } from '@iroha2/crypto-target-web'
    +
    +await init()
    +
    +export { crypto }
    import { crypto, init } from '@iroha2/crypto-target-web'
    +
    +await init()
    +
    +export { crypto }
    ts
    import { Client, Signer } from '@iroha2/client'
    +import { adapter as WS } from '@iroha2/client/web-socket/native'
    +import { crypto } from './crypto'
    +import { client_config } from '../../config'
    +import { AccountId } from '@iroha2/data-model'
    +
    +const HOST = window.location.host
    +
    +export const toriiPre = {
    +  // proxified with vite
    +  apiURL: `http://${HOST}/torii/api`,
    +  telemetryURL: `http://${HOST}/torii/telemetry`,
    +  ws: WS,
    +  fetch: fetch.bind(window),
    +}
    +
    +const signer = new Signer(client_config.account as AccountId, crypto.KeyPair.fromJSON(client_config))
    +
    +export const client = new Client({ signer })
    import { Client, Signer } from '@iroha2/client'
    +import { adapter as WS } from '@iroha2/client/web-socket/native'
    +import { crypto } from './crypto'
    +import { client_config } from '../../config'
    +import { AccountId } from '@iroha2/data-model'
    +
    +const HOST = window.location.host
    +
    +export const toriiPre = {
    +  // proxified with vite
    +  apiURL: `http://${HOST}/torii/api`,
    +  telemetryURL: `http://${HOST}/torii/telemetry`,
    +  ws: WS,
    +  fetch: fetch.bind(window),
    +}
    +
    +const signer = new Signer(client_config.account as AccountId, crypto.KeyPair.fromJSON(client_config))
    +
    +export const client = new Client({ signer })
    vue
    <script setup lang="ts">
    +import { useIntervalFn } from '@vueuse/core'
    +import { useStaleState, useTask } from '@vue-kakuyaku/core'
    +import { toriiPre } from '../client'
    +import { Torii } from '@iroha2/client'
    +
    +const { state, run } = useTask(() => Torii.getStatus(toriiPre), { immediate: true })
    +const stale = useStaleState(state)
    +useIntervalFn(run, 1000)
    +</script>
    +
    +<template>
    +  <div>
    +    <h3>Status</h3>
    +
    +    <ul v-if="stale.fulfilled">
    +      <li>Blocks: {{ stale.fulfilled.value.blocks }}</li>
    +      <li>Uptime (sec): {{ stale.fulfilled.value.uptime.secs }}</li>
    +    </ul>
    +  </div>
    +</template>
    <script setup lang="ts">
    +import { useIntervalFn } from '@vueuse/core'
    +import { useStaleState, useTask } from '@vue-kakuyaku/core'
    +import { toriiPre } from '../client'
    +import { Torii } from '@iroha2/client'
    +
    +const { state, run } = useTask(() => Torii.getStatus(toriiPre), { immediate: true })
    +const stale = useStaleState(state)
    +useIntervalFn(run, 1000)
    +</script>
    +
    +<template>
    +  <div>
    +    <h3>Status</h3>
    +
    +    <ul v-if="stale.fulfilled">
    +      <li>Blocks: {{ stale.fulfilled.value.blocks }}</li>
    +      <li>Uptime (sec): {{ stale.fulfilled.value.uptime.secs }}</li>
    +    </ul>
    +  </div>
    +</template>
    vue
    <script setup lang="ts">
    +import {
    +  DomainId,
    +  EvaluatesToRegistrableBox,
    +  Executable,
    +  Expression,
    +  IdentifiableBox,
    +  Instruction,
    +  MapNameValue,
    +  Metadata,
    +  NewDomain,
    +  OptionIpfsPath,
    +  RegisterBox,
    +  Value,
    +  VecInstruction,
    +} from '@iroha2/data-model'
    +import { ref } from 'vue'
    +import { client, toriiPre } from '../client'
    +import { useTask } from '@vue-kakuyaku/core'
    +
    +const domainName = ref('')
    +
    +const { state, run: registerDomain } = useTask(async () => {
    +  await client.submitExecutable(
    +    toriiPre,
    +    Executable(
    +      'Instructions',
    +      VecInstruction([
    +        Instruction(
    +          'Register',
    +          RegisterBox({
    +            object: EvaluatesToRegistrableBox({
    +              expression: Expression(
    +                'Raw',
    +                Value(
    +                  'Identifiable',
    +                  IdentifiableBox(
    +                    'NewDomain',
    +                    NewDomain({
    +                      id: DomainId({
    +                        name: domainName.value,
    +                      }),
    +                      metadata: Metadata({ map: MapNameValue(new Map()) }),
    +                      logo: OptionIpfsPath('None'),
    +                    }),
    +                  ),
    +                ),
    +              ),
    +            }),
    +          }),
    +        ),
    +      ]),
    +    ),
    +  )
    +})
    +</script>
    +
    +<template>
    +  <div>
    +    <h3>Create Domain</h3>
    +    <p>
    +      <label for="domain">New domain name:</label> <input
    +        id="domain"
    +        v-model="domainName"
    +      >
    +    </p>
    +    <p>
    +      <button @click="registerDomain()">
    +        Register domain{{ state.pending ? '...' : '' }}
    +      </button>
    +    </p>
    +  </div>
    +</template>
    <script setup lang="ts">
    +import {
    +  DomainId,
    +  EvaluatesToRegistrableBox,
    +  Executable,
    +  Expression,
    +  IdentifiableBox,
    +  Instruction,
    +  MapNameValue,
    +  Metadata,
    +  NewDomain,
    +  OptionIpfsPath,
    +  RegisterBox,
    +  Value,
    +  VecInstruction,
    +} from '@iroha2/data-model'
    +import { ref } from 'vue'
    +import { client, toriiPre } from '../client'
    +import { useTask } from '@vue-kakuyaku/core'
    +
    +const domainName = ref('')
    +
    +const { state, run: registerDomain } = useTask(async () => {
    +  await client.submitExecutable(
    +    toriiPre,
    +    Executable(
    +      'Instructions',
    +      VecInstruction([
    +        Instruction(
    +          'Register',
    +          RegisterBox({
    +            object: EvaluatesToRegistrableBox({
    +              expression: Expression(
    +                'Raw',
    +                Value(
    +                  'Identifiable',
    +                  IdentifiableBox(
    +                    'NewDomain',
    +                    NewDomain({
    +                      id: DomainId({
    +                        name: domainName.value,
    +                      }),
    +                      metadata: Metadata({ map: MapNameValue(new Map()) }),
    +                      logo: OptionIpfsPath('None'),
    +                    }),
    +                  ),
    +                ),
    +              ),
    +            }),
    +          }),
    +        ),
    +      ]),
    +    ),
    +  )
    +})
    +</script>
    +
    +<template>
    +  <div>
    +    <h3>Create Domain</h3>
    +    <p>
    +      <label for="domain">New domain name:</label> <input
    +        id="domain"
    +        v-model="domainName"
    +      >
    +    </p>
    +    <p>
    +      <button @click="registerDomain()">
    +        Register domain{{ state.pending ? '...' : '' }}
    +      </button>
    +    </p>
    +  </div>
    +</template>
    vue
    <script setup lang="ts">
    +import { SetupEventsReturn, Torii } from '@iroha2/client'
    +import {
    +  FilterBox,
    +  OptionHash,
    +  OptionPipelineEntityKind,
    +  OptionPipelineStatusKind,
    +  PipelineEntityKind,
    +  PipelineEventFilter,
    +  PipelineStatus,
    +  PipelineStatusKind,
    +} from '@iroha2/data-model'
    +import { computed, onBeforeUnmount, shallowReactive, shallowRef } from 'vue'
    +import { toriiPre } from '../client'
    +
    +function bytesToHex(bytes: number[]): string {
    +  return bytes.map((byte) => byte.toString(16).padStart(2, '0')).join('')
    +}
    +
    +interface EventData {
    +  hash: string
    +  status: string
    +}
    +
    +const events = shallowReactive<EventData[]>([])
    +
    +const currentListener = shallowRef<null | SetupEventsReturn>(null)
    +
    +const isListening = computed(() => !!currentListener.value)
    +
    +function displayStatus(status: PipelineStatus): string {
    +  switch (status.enum.tag) {
    +    case 'Validating':
    +      return 'validating'
    +    case 'Committed':
    +      return 'committed'
    +    case 'Rejected':
    +      return 'rejected with some reason'
    +  }
    +}
    +
    +async function startListening() {
    +  currentListener.value = await Torii.listenForEvents(toriiPre, {
    +    filter: FilterBox(
    +      'Pipeline',
    +      PipelineEventFilter({
    +        entity_kind: OptionPipelineEntityKind('Some', PipelineEntityKind('Transaction')),
    +        status_kind: OptionPipelineStatusKind('Some', PipelineStatusKind('Committed')),
    +        hash: OptionHash('None'),
    +      }),
    +    ),
    +  })
    +
    +  currentListener.value.ee.on('event', (event) => {
    +    const { hash, status } = event.enum.as('Pipeline')
    +    events.push({
    +      hash: bytesToHex([...hash]),
    +      status: displayStatus(status),
    +    })
    +  })
    +}
    +
    +async function stopListening() {
    +  await currentListener.value?.stop()
    +  currentListener.value = null
    +}
    +
    +onBeforeUnmount(stopListening)
    +</script>
    +
    +<template>
    +  <div>
    +    <h3>Listening</h3>
    +
    +    <p>
    +      <button @click="isListening ? stopListening() : startListening()">
    +        {{ isListening ? 'Stop' : 'Listen' }}
    +      </button>
    +    </p>
    +
    +    <p>Events:</p>
    +
    +    <ul class="events-list">
    +      <li
    +        v-for="{ hash, status } in events"
    +        :key="hash"
    +      >
    +        Transaction <code>{{ hash }}</code> status:
    +        {{ status }}
    +      </li>
    +    </ul>
    +  </div>
    +</template>
    <script setup lang="ts">
    +import { SetupEventsReturn, Torii } from '@iroha2/client'
    +import {
    +  FilterBox,
    +  OptionHash,
    +  OptionPipelineEntityKind,
    +  OptionPipelineStatusKind,
    +  PipelineEntityKind,
    +  PipelineEventFilter,
    +  PipelineStatus,
    +  PipelineStatusKind,
    +} from '@iroha2/data-model'
    +import { computed, onBeforeUnmount, shallowReactive, shallowRef } from 'vue'
    +import { toriiPre } from '../client'
    +
    +function bytesToHex(bytes: number[]): string {
    +  return bytes.map((byte) => byte.toString(16).padStart(2, '0')).join('')
    +}
    +
    +interface EventData {
    +  hash: string
    +  status: string
    +}
    +
    +const events = shallowReactive<EventData[]>([])
    +
    +const currentListener = shallowRef<null | SetupEventsReturn>(null)
    +
    +const isListening = computed(() => !!currentListener.value)
    +
    +function displayStatus(status: PipelineStatus): string {
    +  switch (status.enum.tag) {
    +    case 'Validating':
    +      return 'validating'
    +    case 'Committed':
    +      return 'committed'
    +    case 'Rejected':
    +      return 'rejected with some reason'
    +  }
    +}
    +
    +async function startListening() {
    +  currentListener.value = await Torii.listenForEvents(toriiPre, {
    +    filter: FilterBox(
    +      'Pipeline',
    +      PipelineEventFilter({
    +        entity_kind: OptionPipelineEntityKind('Some', PipelineEntityKind('Transaction')),
    +        status_kind: OptionPipelineStatusKind('Some', PipelineStatusKind('Committed')),
    +        hash: OptionHash('None'),
    +      }),
    +    ),
    +  })
    +
    +  currentListener.value.ee.on('event', (event) => {
    +    const { hash, status } = event.enum.as('Pipeline')
    +    events.push({
    +      hash: bytesToHex([...hash]),
    +      status: displayStatus(status),
    +    })
    +  })
    +}
    +
    +async function stopListening() {
    +  await currentListener.value?.stop()
    +  currentListener.value = null
    +}
    +
    +onBeforeUnmount(stopListening)
    +</script>
    +
    +<template>
    +  <div>
    +    <h3>Listening</h3>
    +
    +    <p>
    +      <button @click="isListening ? stopListening() : startListening()">
    +        {{ isListening ? 'Stop' : 'Listen' }}
    +      </button>
    +    </p>
    +
    +    <p>Events:</p>
    +
    +    <ul class="events-list">
    +      <li
    +        v-for="{ hash, status } in events"
    +        :key="hash"
    +      >
    +        Transaction <code>{{ hash }}</code> status:
    +        {{ status }}
    +      </li>
    +    </ul>
    +  </div>
    +</template>
    vue
    <script setup lang="ts">
    +import CreateDomain from './components/CreateDomain.vue'
    +import EventListener from './components/EventListener.vue'
    +import StatusChecker from './components/StatusChecker.vue'
    +</script>
    +
    +<template>
    +  <StatusChecker />
    +  <hr>
    +  <CreateDomain />
    +  <hr>
    +  <EventListener />
    +</template>
    +
    +<style lang="scss">
    +#app {
    +  padding: 16px;
    +  font-family: sans-serif;
    +}
    +</style>
    <script setup lang="ts">
    +import CreateDomain from './components/CreateDomain.vue'
    +import EventListener from './components/EventListener.vue'
    +import StatusChecker from './components/StatusChecker.vue'
    +</script>
    +
    +<template>
    +  <StatusChecker />
    +  <hr>
    +  <CreateDomain />
    +  <hr>
    +  <EventListener />
    +</template>
    +
    +<style lang="scss">
    +#app {
    +  padding: 16px;
    +  font-family: sans-serif;
    +}
    +</style>
    ts
    import { createApp } from 'vue'
    +import App from './App.vue'
    +import { Logger } from '@iroha2/data-model'
    +import { crypto } from './crypto'
    +import { setCrypto } from '@iroha2/client'
    +
    +setCrypto(crypto)
    +new Logger().mount()
    +localStorage.debug = '*'
    +
    +createApp(App).mount('#app')
    import { createApp } from 'vue'
    +import App from './App.vue'
    +import { Logger } from '@iroha2/data-model'
    +import { crypto } from './crypto'
    +import { setCrypto } from '@iroha2/client'
    +
    +setCrypto(crypto)
    +new Logger().mount()
    +localStorage.debug = '*'
    +
    +createApp(App).mount('#app')

    INFO

    In client.ts, we import the configuration file like this:

    ts
    import { client_config } from '../../config'
    import { client_config } from '../../config'

    Note that you need to import the config in this way because this is how the source code of this application works. You can interpret this line as import client_config from 'config.json'.

    Demo

    Here is a small demo with the usage of this component:

    Demo of the sample Vue application

    9. Subscribing to Block Stream

    You can use /block/stream endpoint to send a subscription request for block streaming.

    Via this endpoint, the client first provides the starting block number (i.e. height) in the subscription request. After sending the confirmation message, the server starts streaming all the blocks from the given block number up to the current block, and continues to stream blocks as they are added to the blockchain.

    Here is an example of how to listen to the block stream:

    ts
    import { Torii, ToriiRequirementsForApiWebSocket } from '@iroha2/client'
    +
    +declare const requirements: ToriiRequirementsForApiWebSocket
    +
    +const stream = await Torii.listenForBlocksStream(requirements, {
    +  height: 0n,
    +})
    +
    +stream.ee.on('block', (block) => {
    +  const height = block.enum.as('V1').header.height
    +  console.log('Got block with height', height)
    +})
    import { Torii, ToriiRequirementsForApiWebSocket } from '@iroha2/client'
    +
    +declare const requirements: ToriiRequirementsForApiWebSocket
    +
    +const stream = await Torii.listenForBlocksStream(requirements, {
    +  height: 0n,
    +})
    +
    +stream.ee.on('block', (block) => {
    +  const height = block.enum.as('V1').header.height
    +  console.log('Got block with height', height)
    +})

    1. We have to pass environment-specific ws and fetch, because there is no way for Iroha Client to communicate with a peer in an environment-agnostic way. ↩︎

    + + + + \ No newline at end of file diff --git a/guide/get-started/kotlin-java.html b/guide/get-started/kotlin-java.html new file mode 100644 index 000000000..49ac7fde6 --- /dev/null +++ b/guide/get-started/kotlin-java.html @@ -0,0 +1,1002 @@ + + + + + + Kotlin/Java Guide | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Kotlin/Java Guide

    1. Iroha 2 Client Setup

    In this part we shall cover the main things to look out for if you want to use Iroha 2 in your Kotlin application. Instead of providing the complete basics, we shall assume knowledge of the most widely used concepts, explain the unusual, and provide some instructions for creating your own Iroha 2-compatible client.

    We assume that you know how to create a new package and have basic understanding of the fundamental Kotlin code. Specifically, we shall assume that you know how to build and deploy your program on the target platforms. To clone Iroha 2 JVM compatible SDKs, you can use Iroha Java.

    Without further ado, here's a part of an example build.gradle.kts file, specifically, the plugins, repositories and dependencies sections:

    kotlin
    plugins {
    +    kotlin("jvm") version "1.6.10"
    +    application
    +}
    +
    +group = "jp.co.soramitsu"
    +version = "1.0-SNAPSHOT"
    +
    +repositories {
    +    mavenCentral()
    +    maven(url = "https://jitpack.io")
    +}
    +
    +dependencies {
    +    val iroha2Ver by System.getProperties()
    +
    +    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
    +    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.0")
    +
    +    api("com.github.hyperledger.iroha-java:admin-client:SNAPSHOT")
    +    implementation("com.github.hyperledger.iroha-java:model:SNAPSHOT")
    +    implementation("com.github.hyperledger.iroha-java:block:SNAPSHOT")
    +
    +    implementation("net.i2p.crypto:eddsa:0.3.0")
    +    implementation("org.bouncycastle:bcprov-jdk15on:1.65")
    +    implementation("com.github.multiformats:java-multihash:1.3.0")
    +}
    plugins {
    +    kotlin("jvm") version "1.6.10"
    +    application
    +}
    +
    +group = "jp.co.soramitsu"
    +version = "1.0-SNAPSHOT"
    +
    +repositories {
    +    mavenCentral()
    +    maven(url = "https://jitpack.io")
    +}
    +
    +dependencies {
    +    val iroha2Ver by System.getProperties()
    +
    +    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
    +    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.0")
    +
    +    api("com.github.hyperledger.iroha-java:admin-client:SNAPSHOT")
    +    implementation("com.github.hyperledger.iroha-java:model:SNAPSHOT")
    +    implementation("com.github.hyperledger.iroha-java:block:SNAPSHOT")
    +
    +    implementation("net.i2p.crypto:eddsa:0.3.0")
    +    implementation("org.bouncycastle:bcprov-jdk15on:1.65")
    +    implementation("com.github.multiformats:java-multihash:1.3.0")
    +}

    You should replace the SNAPSHOT in the above configuration with the latest iroha-java snapshot.

    Snapshot versions match the Git commits. To get the latest snapshot, simply visit the iroha-java repository on the iroha-2-dev branch and copy the short hash of the last commit on the main page.

    You can also check the commit history and copy the commit hash of a previous commit.

    This will give you the latest development release of Iroha 2.

    2. Configuring Iroha 2

    At present, the Kotlin SDK doesn't have any classes to interact with the configuration. Instead, you are provided with a ready-made Iroha2Client that reads the configuration from the environment variables and/or the resident config.json in the working directory.

    If you are so inclined, you can have a look at the testcontainers module, and see how the Iroha2Config is implemented.

    kotlin
    package jp.co.soramitsu.iroha2.testengine
    +
    +import jp.co.soramitsu.iroha2.DEFAULT_API_PORT
    +import jp.co.soramitsu.iroha2.DEFAULT_P2P_PORT
    +import jp.co.soramitsu.iroha2.DEFAULT_TELEMETRY_PORT
    +import jp.co.soramitsu.iroha2.Genesis
    +import jp.co.soramitsu.iroha2.generateKeyPair
    +import jp.co.soramitsu.iroha2.generated.datamodel.peer.PeerId
    +import jp.co.soramitsu.iroha2.toIrohaPublicKey
    +import org.slf4j.LoggerFactory.getLogger
    +import org.testcontainers.containers.Network
    +import org.testcontainers.containers.Network.newNetwork
    +import org.testcontainers.containers.output.OutputFrame
    +import org.testcontainers.containers.output.Slf4jLogConsumer
    +import org.testcontainers.images.ImagePullPolicy
    +import org.testcontainers.images.PullPolicy
    +import java.security.KeyPair
    +import java.time.Duration
    +import java.util.function.Consumer
    +
    +/**
    + * Iroha configuration
    + */
    +class IrohaConfig(
    +    var networkToJoin: Network = newNetwork(),
    +    var logConsumer: Consumer<OutputFrame> = Slf4jLogConsumer(getLogger(IrohaContainer::class.java)),
    +    var genesis: Genesis = Genesis.getEmpty(),
    +    var imageTag: String = IrohaContainer.DEFAULT_IMAGE_TAG,
    +    var imageName: String = IrohaContainer.DEFAULT_IMAGE_NAME,
    +    var pullPolicy: ImagePullPolicy = PullPolicy.ageBased(Duration.ofMinutes(10)),
    +    var alias: String = IrohaContainer.NETWORK_ALIAS + DEFAULT_P2P_PORT,
    +    var keyPair: KeyPair = generateKeyPair(),
    +    var trustedPeers: List<PeerId> = listOf(
    +        PeerId(
    +            "$alias:$DEFAULT_P2P_PORT",
    +            keyPair.public.toIrohaPublicKey()
    +        )
    +    ),
    +    var ports: List<Int> = listOf(DEFAULT_P2P_PORT, DEFAULT_API_PORT, DEFAULT_TELEMETRY_PORT),
    +    var shouldCloseNetwork: Boolean = true,
    +    var waitStrategy: Boolean = true,
    +    var submitGenesis: Boolean = true
    +) {
    +    companion object {
    +        const val P2P_PORT_IDX = 0
    +        const val API_PORT_IDX = 1
    +        const val TELEMETRY_PORT_IDX = 2
    +    }
    +}
    package jp.co.soramitsu.iroha2.testengine
    +
    +import jp.co.soramitsu.iroha2.DEFAULT_API_PORT
    +import jp.co.soramitsu.iroha2.DEFAULT_P2P_PORT
    +import jp.co.soramitsu.iroha2.DEFAULT_TELEMETRY_PORT
    +import jp.co.soramitsu.iroha2.Genesis
    +import jp.co.soramitsu.iroha2.generateKeyPair
    +import jp.co.soramitsu.iroha2.generated.datamodel.peer.PeerId
    +import jp.co.soramitsu.iroha2.toIrohaPublicKey
    +import org.slf4j.LoggerFactory.getLogger
    +import org.testcontainers.containers.Network
    +import org.testcontainers.containers.Network.newNetwork
    +import org.testcontainers.containers.output.OutputFrame
    +import org.testcontainers.containers.output.Slf4jLogConsumer
    +import org.testcontainers.images.ImagePullPolicy
    +import org.testcontainers.images.PullPolicy
    +import java.security.KeyPair
    +import java.time.Duration
    +import java.util.function.Consumer
    +
    +/**
    + * Iroha configuration
    + */
    +class IrohaConfig(
    +    var networkToJoin: Network = newNetwork(),
    +    var logConsumer: Consumer<OutputFrame> = Slf4jLogConsumer(getLogger(IrohaContainer::class.java)),
    +    var genesis: Genesis = Genesis.getEmpty(),
    +    var imageTag: String = IrohaContainer.DEFAULT_IMAGE_TAG,
    +    var imageName: String = IrohaContainer.DEFAULT_IMAGE_NAME,
    +    var pullPolicy: ImagePullPolicy = PullPolicy.ageBased(Duration.ofMinutes(10)),
    +    var alias: String = IrohaContainer.NETWORK_ALIAS + DEFAULT_P2P_PORT,
    +    var keyPair: KeyPair = generateKeyPair(),
    +    var trustedPeers: List<PeerId> = listOf(
    +        PeerId(
    +            "$alias:$DEFAULT_P2P_PORT",
    +            keyPair.public.toIrohaPublicKey()
    +        )
    +    ),
    +    var ports: List<Int> = listOf(DEFAULT_P2P_PORT, DEFAULT_API_PORT, DEFAULT_TELEMETRY_PORT),
    +    var shouldCloseNetwork: Boolean = true,
    +    var waitStrategy: Boolean = true,
    +    var submitGenesis: Boolean = true
    +) {
    +    companion object {
    +        const val P2P_PORT_IDX = 0
    +        const val API_PORT_IDX = 1
    +        const val TELEMETRY_PORT_IDX = 2
    +    }
    +}

    3. Querying and Registering Domains

    Querying and Registering a domain are easier operations. The usual boilerplate code, that often only serves to instantiate a client from an on-disk configuration file, is unnecessary. We will immediately add all the necessary imports to implement this client:

    kotlin
    import jp.co.soramitsu.iroha2.*
    +import jp.co.soramitsu.iroha2.generated.crypto.PublicKey
    +import jp.co.soramitsu.iroha2.generated.datamodel.Value
    +import jp.co.soramitsu.iroha2.generated.datamodel.account.AccountId
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValue
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValueType
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.Mintable
    +import jp.co.soramitsu.iroha2.generated.datamodel.metadata.Metadata
    +import jp.co.soramitsu.iroha2.generated.datamodel.name.Name
    +import jp.co.soramitsu.iroha2.generated.datamodel.predicate.GenericValuePredicateBox
    +import jp.co.soramitsu.iroha2.generated.datamodel.predicate.value.ValuePredicate
    +import jp.co.soramitsu.iroha2.query.QueryBuilder
    +import kotlinx.coroutines.runBlocking
    +import kotlinx.coroutines.withTimeout
    +import java.net.URL
    +import java.security.KeyPair
    import jp.co.soramitsu.iroha2.*
    +import jp.co.soramitsu.iroha2.generated.crypto.PublicKey
    +import jp.co.soramitsu.iroha2.generated.datamodel.Value
    +import jp.co.soramitsu.iroha2.generated.datamodel.account.AccountId
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValue
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValueType
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.Mintable
    +import jp.co.soramitsu.iroha2.generated.datamodel.metadata.Metadata
    +import jp.co.soramitsu.iroha2.generated.datamodel.name.Name
    +import jp.co.soramitsu.iroha2.generated.datamodel.predicate.GenericValuePredicateBox
    +import jp.co.soramitsu.iroha2.generated.datamodel.predicate.value.ValuePredicate
    +import jp.co.soramitsu.iroha2.query.QueryBuilder
    +import kotlinx.coroutines.runBlocking
    +import kotlinx.coroutines.withTimeout
    +import java.net.URL
    +import java.security.KeyPair

    We shall write this example in the form of a test class, hence the presence of test-related packages. Note the presence of coroutines.runBlocking. Iroha makes extensive use of asynchronous programming (in Rust terminology), hence blocking is not necessarily the only mode of interaction with the Iroha 2 code.

    In order to make sure that the raised peers work correctly, you can do a simple operation to get all registered domains.

    Next, we will add wrappers to the classes created in this section.

    kotlin
    fun main(args: Array<String>): Unit = runBlocking{
    +    val peerUrl = "http://127.0.0.1:8080"
    +    val telemetryUrl = "http://127.0.0.1:8180"
    +    val admin = AccountId("bob".asName(), "wonderland".asDomainId())
    +    val adminKeyPair = keyPairFromHex("7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",
    +        "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e")
    +
    +    val client = AdminIroha2Client(URL(peerUrl), URL(telemetryUrl), log = true)
    +    val query = Query(client, admin, adminKeyPair)
    +
    +    query.findAllDomains()
    +        .also { println("ALL DOMAINS: ${it.map { d -> d.id.asString() }}") }
    +
    +}
    +
    +open class Query (private val client: AdminIroha2Client,
    +                  private val admin: AccountId,
    +                  private val keyPair: KeyPair) {
    +    
    +    suspend fun findAllDomains(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
    +        .findAllDomains(queryFilter)
    +        .account(admin)
    +        .buildSigned(keyPair)
    +        .let { client.sendQuery(it) }
    +}
    fun main(args: Array<String>): Unit = runBlocking{
    +    val peerUrl = "http://127.0.0.1:8080"
    +    val telemetryUrl = "http://127.0.0.1:8180"
    +    val admin = AccountId("bob".asName(), "wonderland".asDomainId())
    +    val adminKeyPair = keyPairFromHex("7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0",
    +        "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e")
    +
    +    val client = AdminIroha2Client(URL(peerUrl), URL(telemetryUrl), log = true)
    +    val query = Query(client, admin, adminKeyPair)
    +
    +    query.findAllDomains()
    +        .also { println("ALL DOMAINS: ${it.map { d -> d.id.asString() }}") }
    +
    +}
    +
    +open class Query (private val client: AdminIroha2Client,
    +                  private val admin: AccountId,
    +                  private val keyPair: KeyPair) {
    +    
    +    suspend fun findAllDomains(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
    +        .findAllDomains(queryFilter)
    +        .account(admin)
    +        .buildSigned(keyPair)
    +        .let { client.sendQuery(it) }
    +}

    The output in the terminal will contain a list of all domains that are currently registered.

    Expand to see the expected output
    ALL DOMAINS: [wonderland, genesis, garden_of_live_flowers]
    ALL DOMAINS: [wonderland, genesis, garden_of_live_flowers]

    To register a new domain, add the following lines to Main.kt:

    kotlin
    val sendTransaction = SendTransaction(client, admin, adminKeyPair)
    +
    +val domain = "looking_glass_${System.currentTimeMillis()}"
    +    sendTransaction.registerDomain(domain).also { println("DOMAIN $domain CREATED") }
    val sendTransaction = SendTransaction(client, admin, adminKeyPair)
    +
    +val domain = "looking_glass_${System.currentTimeMillis()}"
    +    sendTransaction.registerDomain(domain).also { println("DOMAIN $domain CREATED") }

    Then create new open class SendTransaction in your project:

    kotlin
    open class SendTransaction (private val client: AdminIroha2Client,
    +                            private val admin: AccountId,
    +                            private val keyPair: KeyPair,
    +                            private val timeout: Long = 10000) {
    +
    +    suspend fun registerDomain(
    +        id: String,
    +        metadata: Map<Name, Value> = mapOf(),
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.registerDomain(id.asDomainId(), metadata)
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }
    +}
    open class SendTransaction (private val client: AdminIroha2Client,
    +                            private val admin: AccountId,
    +                            private val keyPair: KeyPair,
    +                            private val timeout: Long = 10000) {
    +
    +    suspend fun registerDomain(
    +        id: String,
    +        metadata: Map<Name, Value> = mapOf(),
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.registerDomain(id.asDomainId(), metadata)
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }
    +}
    Expand to see the expected output
    DOMAIN looking_glass CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, wonderland]
    DOMAIN looking_glass CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, wonderland]

    4. Registering an Account

    Registering an account is more involved than the aforementioned functions. Previously, we only had to worry about submitting a single instruction, with a single string-based registration box (in Rust terminology, the heap-allocated reference types are all called boxes).

    When registering an account, there are a few more variables. The account can only be registered to an existing domain. Also, an account typically has to have a key pair.

    To register a new account, add the following lines to Main.kt:

    Kotlin
    val madHatter = "madHatter_${System.currentTimeMillis()}$ACCOUNT_ID_DELIMITER$domain"
    +    val madHatterKeyPair = generateKeyPair()
    +    sendTransaction.registerAccount(madHatter, listOf(madHatterKeyPair.public.toIrohaPublicKey()))
    +        .also { println("ACCOUNT $madHatter CREATED") }
    +
    +    query.findAllAccounts()
    +        .also { println("ALL ACCOUNTS: ${it.map { a -> a.id.asString() }}") }
    val madHatter = "madHatter_${System.currentTimeMillis()}$ACCOUNT_ID_DELIMITER$domain"
    +    val madHatterKeyPair = generateKeyPair()
    +    sendTransaction.registerAccount(madHatter, listOf(madHatterKeyPair.public.toIrohaPublicKey()))
    +        .also { println("ACCOUNT $madHatter CREATED") }
    +
    +    query.findAllAccounts()
    +        .also { println("ALL ACCOUNTS: ${it.map { a -> a.id.asString() }}") }

    Then implement new method for class SendTransaction in your project.

    Kotlin
    suspend fun registerAccount(
    +        id: String,
    +        signatories: List<PublicKey>,
    +        metadata: Map<Name, Value> = mapOf(),
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.registerAccount(id.asAccountId(), signatories, Metadata(metadata))
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }
    suspend fun registerAccount(
    +        id: String,
    +        signatories: List<PublicKey>,
    +        metadata: Map<Name, Value> = mapOf(),
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.registerAccount(id.asAccountId(), signatories, Metadata(metadata))
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }

    Also, a new method has been added to the Query class.

    Kotlin
    suspend fun findAllAccounts(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
    +        .findAllAccounts(queryFilter)
    +        .account(admin)
    +        .buildSigned(keyPair)
    +        .let {
    +            client.sendQuery(it)
    +    }
    suspend fun findAllAccounts(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
    +        .findAllAccounts(queryFilter)
    +        .account(admin)
    +        .buildSigned(keyPair)
    +        .let {
    +            client.sendQuery(it)
    +    }
    Expand to see the expected output
    DOMAIN looking_glass_1684835731653 CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, wonderland, looking_glass_1684835731653]
    +ACCOUNT madHatter_1684835733686@looking_glass_1684835731653 CREATED
    +ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
    DOMAIN looking_glass_1684835731653 CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, wonderland, looking_glass_1684835731653]
    +ACCOUNT madHatter_1684835733686@looking_glass_1684835731653 CREATED
    +ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]

    As you can see, for illustrative purposes, we have generated a new key-pair. We converted that key-pair into an Iroha-compatible format using toIrohaPublicKey, and added the public key to the instruction to register an account.

    5. Registering and minting assets

    Iroha has been built with few underlying assumptions about what the assets need to be in terms of their value type and characteristics (fungible or non-fungible, mintable or non-mintable).

    INFO

    The non-mintable assets are a relatively recent addition to Iroha 2, thus registering and minting such assets is not presently possible through the Kotlin SDK.

    To register new assets definition, add the following lines of code to main

    Kotlin
    val assetDefinition = "asset_time_${System.currentTimeMillis()}$ASSET_ID_DELIMITER$domain"
    +    sendTransaction.registerAssetDefinition(assetDefinition, AssetValueType.Quantity())
    +        .also { println("ASSET DEFINITION $assetDefinition CREATED") }
    val assetDefinition = "asset_time_${System.currentTimeMillis()}$ASSET_ID_DELIMITER$domain"
    +    sendTransaction.registerAssetDefinition(assetDefinition, AssetValueType.Quantity())
    +        .also { println("ASSET DEFINITION $assetDefinition CREATED") }

    Then implement new method for class SendTransaction in your project.

    Kotlin
    suspend fun registerAssetDefinition(
    +        id: String,
    +        type: AssetValueType = AssetValueType.Store(),
    +        metadata: Map<Name, Value> = mapOf(),
    +        mintable: Mintable = Mintable.Infinitely(),
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.registerAssetDefinition(id.asAssetDefinitionId(), type, Metadata(metadata), mintable)
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }
    suspend fun registerAssetDefinition(
    +        id: String,
    +        type: AssetValueType = AssetValueType.Store(),
    +        metadata: Map<Name, Value> = mapOf(),
    +        mintable: Mintable = Mintable.Infinitely(),
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.registerAssetDefinition(id.asAssetDefinitionId(), type, Metadata(metadata), mintable)
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }

    To mint new assets, add the following lines of code to main

    Kotlin
    val madHatterAsset = "$assetDefinition$ASSET_ID_DELIMITER$madHatter"
    +    sendTransaction.registerAsset(madHatterAsset, AssetValue.Quantity(100))
    +        .also { println("ASSET $madHatterAsset CREATED") }
    val madHatterAsset = "$assetDefinition$ASSET_ID_DELIMITER$madHatter"
    +    sendTransaction.registerAsset(madHatterAsset, AssetValue.Quantity(100))
    +        .also { println("ASSET $madHatterAsset CREATED") }

    Then implement new method for class SendTransaction in your project.

    Kotlin
    suspend fun registerAsset(
    +        id: String,
    +        value: AssetValue,
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +        ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.registerAsset(id.asAssetId(), value)
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +            }
    +        }
    suspend fun registerAsset(
    +        id: String,
    +        value: AssetValue,
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +        ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.registerAsset(id.asAssetId(), value)
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +            }
    +        }

    To check the result, add the following line of code to the class main

    Kotlin
    query.findAllAssets()
    +        .also { println("ALL ASSETS: ${it.map { a -> a.id.asString() }}") }
    query.findAllAssets()
    +        .also { println("ALL ASSETS: ${it.map { a -> a.id.asString() }}") }

    Also, a new method has been added to the open class Query

    Kotlin
    suspend fun findAllAssets(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
    +        .findAllAssets(queryFilter)
    +        .account(admin)
    +        .buildSigned(keyPair)
    +        .let { client.sendQuery(it) }
    suspend fun findAllAssets(queryFilter: GenericValuePredicateBox<ValuePredicate>? = null) = QueryBuilder
    +        .findAllAssets(queryFilter)
    +        .account(admin)
    +        .buildSigned(keyPair)
    +        .let { client.sendQuery(it) }
    Expand to see the expected output
    DOMAIN looking_glass_1684842996549 CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, looking_glass_1684842996549, wonderland, looking_glass_1684835731653]
    +ACCOUNT madHatter_1684842997930@looking_glass_1684842996549 CREATED
    +ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, madHatter_1684842997930@looking_glass_1684842996549, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
    +ASSET DEFINITION asset_time_1684842998891#looking_glass_1684842996549 CREATED
    +ASSET asset_time_1684842998891#looking_glass_1684842996549#madHatter_1684842997930@looking_glass_1684842996549 CREATED
    +ALL ASSETS: [asset_time_1684842998891#looking_glass_1684842996549#madHatter_1684842997930@looking_glass_1684842996549, cabbage#garden_of_live_flowers#alice@wonderland, rose#wonderland#alice@wonderland]```
    DOMAIN looking_glass_1684842996549 CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, looking_glass_1684842996549, wonderland, looking_glass_1684835731653]
    +ACCOUNT madHatter_1684842997930@looking_glass_1684842996549 CREATED
    +ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, madHatter_1684842997930@looking_glass_1684842996549, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
    +ASSET DEFINITION asset_time_1684842998891#looking_glass_1684842996549 CREATED
    +ASSET asset_time_1684842998891#looking_glass_1684842996549#madHatter_1684842997930@looking_glass_1684842996549 CREATED
    +ALL ASSETS: [asset_time_1684842998891#looking_glass_1684842996549#madHatter_1684842997930@looking_glass_1684842996549, cabbage#garden_of_live_flowers#alice@wonderland, rose#wonderland#alice@wonderland]```

    6. Transferring assets

    After we have registered and minted madHatter's assets, let's transfer some of them to another blockchain user. To do this, we will create a new user, register their asset with the main method and add transfer operations for the asset.

    Kotlin
    val whiteRabbit = "whiteRabbit_${System.currentTimeMillis()}$ACCOUNT_ID_DELIMITER$domain"
    +    val whiteRabbitKeyPair = generateKeyPair()
    +    sendTransaction.registerAccount(whiteRabbit, listOf(whiteRabbitKeyPair.public.toIrohaPublicKey()))
    +        .also { println("ACCOUNT $whiteRabbit CREATED") }
    +    
    +    val whiteRabbitAsset = "$assetDefinition$ASSET_ID_DELIMITER$whiteRabbit"
    +    sendTransaction.registerAsset(whiteRabbitAsset, AssetValue.Quantity(0))
    +        .also { println("ASSET $whiteRabbitAsset CREATED") }
    +    
    +    sendTransaction.transferAsset(madHatterAsset, 10, whiteRabbitAsset, madHatter.asAccountId(), madHatterKeyPair)
    +        .also { println("$madHatter TRANSFERRED FROM $madHatterAsset TO $whiteRabbitAsset: 10") }
    +    query.getAccountAmount(madHatter, madHatterAsset).also { println("$madHatterAsset BALANCE: $it") }
    +    query.getAccountAmount(whiteRabbit, whiteRabbitAsset).also { println("$whiteRabbitAsset BALANCE: $it") }
    val whiteRabbit = "whiteRabbit_${System.currentTimeMillis()}$ACCOUNT_ID_DELIMITER$domain"
    +    val whiteRabbitKeyPair = generateKeyPair()
    +    sendTransaction.registerAccount(whiteRabbit, listOf(whiteRabbitKeyPair.public.toIrohaPublicKey()))
    +        .also { println("ACCOUNT $whiteRabbit CREATED") }
    +    
    +    val whiteRabbitAsset = "$assetDefinition$ASSET_ID_DELIMITER$whiteRabbit"
    +    sendTransaction.registerAsset(whiteRabbitAsset, AssetValue.Quantity(0))
    +        .also { println("ASSET $whiteRabbitAsset CREATED") }
    +    
    +    sendTransaction.transferAsset(madHatterAsset, 10, whiteRabbitAsset, madHatter.asAccountId(), madHatterKeyPair)
    +        .also { println("$madHatter TRANSFERRED FROM $madHatterAsset TO $whiteRabbitAsset: 10") }
    +    query.getAccountAmount(madHatter, madHatterAsset).also { println("$madHatterAsset BALANCE: $it") }
    +    query.getAccountAmount(whiteRabbit, whiteRabbitAsset).also { println("$whiteRabbitAsset BALANCE: $it") }

    In the sendTransaction class, add a method for transferring assets.

    Kotlin
    suspend fun transferAsset(
    +        from: String,
    +        value: Int,
    +        to: String,
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.transferAsset(from.asAssetId(), value, to.asAssetId())
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }
    suspend fun transferAsset(
    +        from: String,
    +        value: Int,
    +        to: String,
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.transferAsset(from.asAssetId(), value, to.asAssetId())
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }

    To check the result of the asset transfer, add the getAccountAmount() method to the Query class:

    Kotlin
    suspend fun getAccountAmount(accountId: String, assetId: String): Long {
    +        return QueryBuilder.findAccountById(accountId.asAccountId())
    +            .account(admin)
    +            .buildSigned(keyPair)
    +            .let { query ->
    +                client.sendQuery(query).assets[assetId.asAssetId()]?.value
    +            }.let { value ->
    +                value?.cast<AssetValue.Quantity>()?.u32
    +            } ?: throw RuntimeException("NOT FOUND")
    +    }
    suspend fun getAccountAmount(accountId: String, assetId: String): Long {
    +        return QueryBuilder.findAccountById(accountId.asAccountId())
    +            .account(admin)
    +            .buildSigned(keyPair)
    +            .let { query ->
    +                client.sendQuery(query).assets[assetId.asAssetId()]?.value
    +            }.let { value ->
    +                value?.cast<AssetValue.Quantity>()?.u32
    +            } ?: throw RuntimeException("NOT FOUND")
    +    }

    The console output should contain similar information.

    Expand to see the expected output
    DOMAIN looking_glass_1684843200289 CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, looking_glass_1684843200289, looking_glass_1684842996549, wonderland, looking_glass_1684835731653]
    +ACCOUNT madHatter_1684843202389@looking_glass_1684843200289 CREATED
    +ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, madHatter_1684843202389@looking_glass_1684843200289, madHatter_1684842997930@looking_glass_1684842996549, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
    +ASSET DEFINITION asset_time_1684843203337#looking_glass_1684843200289 CREATED
    +ASSET asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 CREATED
    +ACCOUNT whiteRabbit_1684843205383@looking_glass_1684843200289 CREATED
    +ASSET asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289 CREATED
    +madHatter_1684843202389@looking_glass_1684843200289 TRANSFERRED FROM asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 TO asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289: 10
    +asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 BALANCE: 90
    +asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289 BALANCE: 10
    +ALL ASSETS: [asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289, cabbage#garden_of_live_flowers#alice@wonderland, rose#wonderland#alice@wonderland]
    DOMAIN looking_glass_1684843200289 CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, genesis, looking_glass_1684843200289, looking_glass_1684842996549, wonderland, looking_glass_1684835731653]
    +ACCOUNT madHatter_1684843202389@looking_glass_1684843200289 CREATED
    +ALL ACCOUNTS: [carpenter@garden_of_live_flowers, genesis@genesis, madHatter_1684843202389@looking_glass_1684843200289, madHatter_1684842997930@looking_glass_1684842996549, alice@wonderland, bob@wonderland, madHatter_1684835733686@looking_glass_1684835731653]
    +ASSET DEFINITION asset_time_1684843203337#looking_glass_1684843200289 CREATED
    +ASSET asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 CREATED
    +ACCOUNT whiteRabbit_1684843205383@looking_glass_1684843200289 CREATED
    +ASSET asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289 CREATED
    +madHatter_1684843202389@looking_glass_1684843200289 TRANSFERRED FROM asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 TO asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289: 10
    +asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289 BALANCE: 90
    +asset_time_1684843203337#looking_glass_1684843200289#whiteRabbit_1684843205383@looking_glass_1684843200289 BALANCE: 10
    +ALL ASSETS: [asset_time_1684843203337#looking_glass_1684843200289#madHatter_1684843202389@looking_glass_1684843200289, cabbage#garden_of_live_flowers#alice@wonderland, rose#wonderland#alice@wonderland]

    7. Burning assets

    Burning assets is quite similar to minting them. To get started, let's add the following lines to the main() method:

    Kotlin
    sendTransaction.burnAssets(madHatterAsset, 10, madHatter.asAccountId(), madHatterKeyPair)
    +        .also { println("${madHatterAsset} WAS BURN") }
    +
    +    query.getAccountAmount(madHatter, madHatterAsset)
    +        .also { println("$madHatterAsset BALANCE: $it AFTER ASSETS BURNING") }
    sendTransaction.burnAssets(madHatterAsset, 10, madHatter.asAccountId(), madHatterKeyPair)
    +        .also { println("${madHatterAsset} WAS BURN") }
    +
    +    query.getAccountAmount(madHatter, madHatterAsset)
    +        .also { println("$madHatterAsset BALANCE: $it AFTER ASSETS BURNING") }

    Then implement a wrapper over the burnAssets() method in the sendTransaction class:

    Kotlin
    suspend fun burnAssets(
    +        assetId: String,
    +        value: Int,
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.burnAsset(assetId.asAssetId(), value)
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }
    suspend fun burnAssets(
    +        assetId: String,
    +        value: Int,
    +        admin: AccountId = this.admin,
    +        keyPair: KeyPair = this.keyPair
    +    ) {
    +        client.sendTransaction {
    +            account(admin)
    +            this.burnAsset(assetId.asAssetId(), value)
    +            buildSigned(keyPair)
    +        }.also {
    +            withTimeout(timeout) { it.await() }
    +        }
    +    }
    Expand to see the expected output
    DOMAIN looking_glass_1684843511587 CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, looking_glass_1684843344208, genesis, looking_glass_1684843200289, looking_glass_1684842996549, wonderland, looking_glass_1684843511587, looking_glass_1684843451130, looking_glass_1684835731653]
    +ACCOUNT madHatter_1684843513272@looking_glass_1684843511587 CREATED
    +ALL ACCOUNTS: [carpenter@garden_of_live_flowers, madHatter_1684843345604@looking_glass_1684843344208, whiteRabbit_1684843348692@looking_glass_1684843344208, genesis@genesis, madHatter_1684835733686@looking_glass_1684835731653]
    +ASSET DEFINITION asset_time_1684843514251#looking_glass_1684843511587 CREATED
    +ASSET asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 CREATED
    +ACCOUNT whiteRabbit_1684843516303@looking_glass_1684843511587 CREATED
    +ASSET asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587 CREATED
    +madHatter_1684843513272@looking_glass_1684843511587 TRANSFERRED FROM asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 TO asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587: 10
    +asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 BALANCE: 90
    +asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587 BALANCE: 10
    +asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 WAS BURN
    +asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 BALANCE: 80 AFTER ASSETS BURNING
    +ALL ASSETS: [asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587, asset_time_1684843454049#looking_glass_1684843451130#madHatter_1684843453085@looking_glass_1684843451130, asset_time_1684843454049#looking_glass_1684843451130#whiteRabbit_1684843456091@looking_glass_1684843451130]
    DOMAIN looking_glass_1684843511587 CREATED
    +ALL DOMAINS: [looking_glass, garden_of_live_flowers, looking_glass_1684843344208, genesis, looking_glass_1684843200289, looking_glass_1684842996549, wonderland, looking_glass_1684843511587, looking_glass_1684843451130, looking_glass_1684835731653]
    +ACCOUNT madHatter_1684843513272@looking_glass_1684843511587 CREATED
    +ALL ACCOUNTS: [carpenter@garden_of_live_flowers, madHatter_1684843345604@looking_glass_1684843344208, whiteRabbit_1684843348692@looking_glass_1684843344208, genesis@genesis, madHatter_1684835733686@looking_glass_1684835731653]
    +ASSET DEFINITION asset_time_1684843514251#looking_glass_1684843511587 CREATED
    +ASSET asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 CREATED
    +ACCOUNT whiteRabbit_1684843516303@looking_glass_1684843511587 CREATED
    +ASSET asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587 CREATED
    +madHatter_1684843513272@looking_glass_1684843511587 TRANSFERRED FROM asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 TO asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587: 10
    +asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 BALANCE: 90
    +asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587 BALANCE: 10
    +asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 WAS BURN
    +asset_time_1684843514251#looking_glass_1684843511587#madHatter_1684843513272@looking_glass_1684843511587 BALANCE: 80 AFTER ASSETS BURNING
    +ALL ASSETS: [asset_time_1684843514251#looking_glass_1684843511587#whiteRabbit_1684843516303@looking_glass_1684843511587, asset_time_1684843454049#looking_glass_1684843451130#madHatter_1684843453085@looking_glass_1684843451130, asset_time_1684843454049#looking_glass_1684843451130#whiteRabbit_1684843456091@looking_glass_1684843451130]

    8. Visualizing outputs

    Finally, we should talk about visualising data. The Rust API is currently the most complete in terms of available queries and instructions. After all, this is the language in which Iroha 2 was built. Kotlin, by contrast, supports only some features.

    There are two possible event filters: PipelineEventFilter and DataEventFilter, we shall focus on the former. This filter sieves events pertaining to the process of submitting a transaction, executing a transaction and committing it to a block.

    kotlin
    import jp.co.soramitsu.iroha2.generated.datamodel.events.EventFilter.Pipeline
    +import jp.co.soramitsu.iroha2.generated.datamodel.events.pipeline.EventFilter
    +import jp.co.soramitsu.iroha2.generated.datamodel.events.pipeline.EntityType.Transaction
    +import jp.co.soramitsu.iroha2.generated.crypto.hash.Hash
    +
    +val hash: ByteArray
    +val eventFilter = Pipeline(EventFilter(Transaction(), Hash(hash)))
    import jp.co.soramitsu.iroha2.generated.datamodel.events.EventFilter.Pipeline
    +import jp.co.soramitsu.iroha2.generated.datamodel.events.pipeline.EventFilter
    +import jp.co.soramitsu.iroha2.generated.datamodel.events.pipeline.EntityType.Transaction
    +import jp.co.soramitsu.iroha2.generated.crypto.hash.Hash
    +
    +val hash: ByteArray
    +val eventFilter = Pipeline(EventFilter(Transaction(), Hash(hash)))

    What this short code snippet does is the following: It creates an event pipeline filter that checks if a transaction with the specified hash was submitted/rejected. This can then be used to see if the transaction we submitted was processed correctly and provide feedback to the end-user.

    9. Samples in pure Java

    java
    package jp.co.soramitsu.iroha2;
    +
    +import jp.co.soramitsu.iroha2.client.Iroha2AsyncClient;
    +import jp.co.soramitsu.iroha2.generated.datamodel.Value;
    +import jp.co.soramitsu.iroha2.generated.datamodel.account.Account;
    +import jp.co.soramitsu.iroha2.generated.datamodel.account.AccountId;
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetId;
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValue;
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValueType;
    +import jp.co.soramitsu.iroha2.generated.datamodel.domain.Domain;
    +import jp.co.soramitsu.iroha2.generated.datamodel.domain.DomainId;
    +import jp.co.soramitsu.iroha2.generated.datamodel.metadata.Metadata;
    +import jp.co.soramitsu.iroha2.generated.datamodel.name.Name;
    +import jp.co.soramitsu.iroha2.generated.datamodel.transaction.VersionedSignedTransaction;
    +import jp.co.soramitsu.iroha2.query.QueryAndExtractor;
    +import jp.co.soramitsu.iroha2.query.QueryBuilder;
    +import jp.co.soramitsu.iroha2.testengine.DefaultGenesis;
    +import jp.co.soramitsu.iroha2.testengine.IrohaTest;
    +import jp.co.soramitsu.iroha2.testengine.WithIroha;
    +import jp.co.soramitsu.iroha2.transaction.TransactionBuilder;
    +import org.junit.jupiter.api.Assertions;
    +import org.junit.jupiter.api.Test;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.concurrent.CompletableFuture;
    +import java.util.concurrent.ExecutionException;
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.TimeoutException;
    +
    +import static jp.co.soramitsu.iroha2.testengine.TestConstsKt.*;
    +
    +public class JavaTest extends IrohaTest<Iroha2AsyncClient> {
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void instructionFailed() {
    +        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .fail("FAIL MESSAGE")
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<byte[]> future = client.sendTransactionAsync(transaction);
    +        Assertions.assertThrows(ExecutionException.class,
    +            () -> future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS)
    +        );
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void registerDomainInstructionCommitted() throws ExecutionException, InterruptedException, TimeoutException {
    +        final DomainId domainId = new DomainId(new Name("new_domain_name"));
    +        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerDomain(domainId)
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(transaction).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Domain> query = QueryBuilder
    +            .findDomainById(domainId)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Domain> future = client.sendQueryAsync(query);
    +        final Domain domain = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +        Assertions.assertEquals(domain.getId(), domainId);
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void registerAccountInstructionCommitted() throws Exception {
    +        final AccountId accountId = new AccountId(
    +            new Name("new_account"),
    +            DEFAULT_DOMAIN_ID
    +        );
    +        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerAccount(accountId, new ArrayList<>())
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(transaction).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Account> query = QueryBuilder
    +            .findAccountById(accountId)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Account> future = client.sendQueryAsync(query);
    +        final Account account = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +        Assertions.assertEquals(account.getId(), accountId);
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void mintAssetInstructionCommitted() throws Exception {
    +        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Quantity())
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final VersionedSignedTransaction mintAssetTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .mintAsset(DEFAULT_ASSET_ID, 5)
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(mintAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Account> query = QueryBuilder
    +            .findAccountById(ALICE_ACCOUNT_ID)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Account> future = client.sendQueryAsync(query);
    +        final Account account = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +        final AssetValue value = account.getAssets().get(DEFAULT_ASSET_ID).getValue();
    +        Assertions.assertEquals(5, ((AssetValue.Quantity) value).getU32());
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void updateKeyValueInstructionCommitted() throws Exception {
    +        final Name assetMetadataKey = new Name("asset_metadata_key");
    +        final Value.String assetMetadataValue = new Value.String("some string value");
    +        final Value.String assetMetadataValue2 = new Value.String("some string value 2");
    +        final Metadata metadata = new Metadata(new HashMap<Name, Value>() {{
    +            put(assetMetadataKey, assetMetadataValue);
    +        }});
    +
    +        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Store(), metadata)
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final AssetId assetId = new AssetId(DEFAULT_ASSET_DEFINITION_ID, ALICE_ACCOUNT_ID);
    +        final VersionedSignedTransaction keyValueTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .setKeyValue(
    +                assetId,
    +                assetMetadataKey,
    +                assetMetadataValue2
    +            ).buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(keyValueTx).get(10, TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Value> assetDefinitionValueQuery = QueryBuilder
    +            .findAssetKeyValueByIdAndKey(assetId, assetMetadataKey)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Value> future = client.sendQueryAsync(assetDefinitionValueQuery);
    +
    +        final Value value = future.get(10, TimeUnit.SECONDS);
    +        Assertions.assertEquals(
    +            ((Value.String) value).getString(),
    +            assetMetadataValue2.getString()
    +        );
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void setKeyValueInstructionCommitted() throws Exception {
    +        final Value.String assetValue = new Value.String("some string value");
    +        final Name assetKey = new Name("asset_metadata_key");
    +
    +        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Store())
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final VersionedSignedTransaction keyValueTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .setKeyValue(
    +                DEFAULT_ASSET_DEFINITION_ID,
    +                assetKey,
    +                assetValue
    +            ).buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(keyValueTx).get(10, TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Value> assetDefinitionValueQuery = QueryBuilder
    +            .findAssetDefinitionKeyValueByIdAndKey(DEFAULT_ASSET_DEFINITION_ID, assetKey)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Value> future = client.sendQueryAsync(assetDefinitionValueQuery);
    +
    +        final Value value = future.get(10, TimeUnit.SECONDS);
    +        Assertions.assertEquals(
    +            ((Value.String) value).getString(),
    +            assetValue.getString()
    +        );
    +    }
    +}
    package jp.co.soramitsu.iroha2;
    +
    +import jp.co.soramitsu.iroha2.client.Iroha2AsyncClient;
    +import jp.co.soramitsu.iroha2.generated.datamodel.Value;
    +import jp.co.soramitsu.iroha2.generated.datamodel.account.Account;
    +import jp.co.soramitsu.iroha2.generated.datamodel.account.AccountId;
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetId;
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValue;
    +import jp.co.soramitsu.iroha2.generated.datamodel.asset.AssetValueType;
    +import jp.co.soramitsu.iroha2.generated.datamodel.domain.Domain;
    +import jp.co.soramitsu.iroha2.generated.datamodel.domain.DomainId;
    +import jp.co.soramitsu.iroha2.generated.datamodel.metadata.Metadata;
    +import jp.co.soramitsu.iroha2.generated.datamodel.name.Name;
    +import jp.co.soramitsu.iroha2.generated.datamodel.transaction.VersionedSignedTransaction;
    +import jp.co.soramitsu.iroha2.query.QueryAndExtractor;
    +import jp.co.soramitsu.iroha2.query.QueryBuilder;
    +import jp.co.soramitsu.iroha2.testengine.DefaultGenesis;
    +import jp.co.soramitsu.iroha2.testengine.IrohaTest;
    +import jp.co.soramitsu.iroha2.testengine.WithIroha;
    +import jp.co.soramitsu.iroha2.transaction.TransactionBuilder;
    +import org.junit.jupiter.api.Assertions;
    +import org.junit.jupiter.api.Test;
    +
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.concurrent.CompletableFuture;
    +import java.util.concurrent.ExecutionException;
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.TimeoutException;
    +
    +import static jp.co.soramitsu.iroha2.testengine.TestConstsKt.*;
    +
    +public class JavaTest extends IrohaTest<Iroha2AsyncClient> {
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void instructionFailed() {
    +        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .fail("FAIL MESSAGE")
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<byte[]> future = client.sendTransactionAsync(transaction);
    +        Assertions.assertThrows(ExecutionException.class,
    +            () -> future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS)
    +        );
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void registerDomainInstructionCommitted() throws ExecutionException, InterruptedException, TimeoutException {
    +        final DomainId domainId = new DomainId(new Name("new_domain_name"));
    +        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerDomain(domainId)
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(transaction).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Domain> query = QueryBuilder
    +            .findDomainById(domainId)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Domain> future = client.sendQueryAsync(query);
    +        final Domain domain = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +        Assertions.assertEquals(domain.getId(), domainId);
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void registerAccountInstructionCommitted() throws Exception {
    +        final AccountId accountId = new AccountId(
    +            new Name("new_account"),
    +            DEFAULT_DOMAIN_ID
    +        );
    +        final VersionedSignedTransaction transaction = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerAccount(accountId, new ArrayList<>())
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(transaction).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Account> query = QueryBuilder
    +            .findAccountById(accountId)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Account> future = client.sendQueryAsync(query);
    +        final Account account = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +        Assertions.assertEquals(account.getId(), accountId);
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void mintAssetInstructionCommitted() throws Exception {
    +        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Quantity())
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final VersionedSignedTransaction mintAssetTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .mintAsset(DEFAULT_ASSET_ID, 5)
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(mintAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Account> query = QueryBuilder
    +            .findAccountById(ALICE_ACCOUNT_ID)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Account> future = client.sendQueryAsync(query);
    +        final Account account = future.get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +        final AssetValue value = account.getAssets().get(DEFAULT_ASSET_ID).getValue();
    +        Assertions.assertEquals(5, ((AssetValue.Quantity) value).getU32());
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void updateKeyValueInstructionCommitted() throws Exception {
    +        final Name assetMetadataKey = new Name("asset_metadata_key");
    +        final Value.String assetMetadataValue = new Value.String("some string value");
    +        final Value.String assetMetadataValue2 = new Value.String("some string value 2");
    +        final Metadata metadata = new Metadata(new HashMap<Name, Value>() {{
    +            put(assetMetadataKey, assetMetadataValue);
    +        }});
    +
    +        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Store(), metadata)
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final AssetId assetId = new AssetId(DEFAULT_ASSET_DEFINITION_ID, ALICE_ACCOUNT_ID);
    +        final VersionedSignedTransaction keyValueTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .setKeyValue(
    +                assetId,
    +                assetMetadataKey,
    +                assetMetadataValue2
    +            ).buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(keyValueTx).get(10, TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Value> assetDefinitionValueQuery = QueryBuilder
    +            .findAssetKeyValueByIdAndKey(assetId, assetMetadataKey)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Value> future = client.sendQueryAsync(assetDefinitionValueQuery);
    +
    +        final Value value = future.get(10, TimeUnit.SECONDS);
    +        Assertions.assertEquals(
    +            ((Value.String) value).getString(),
    +            assetMetadataValue2.getString()
    +        );
    +    }
    +
    +    @Test
    +    @WithIroha(sources = DefaultGenesis.class)
    +    public void setKeyValueInstructionCommitted() throws Exception {
    +        final Value.String assetValue = new Value.String("some string value");
    +        final Name assetKey = new Name("asset_metadata_key");
    +
    +        final VersionedSignedTransaction registerAssetTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .registerAssetDefinition(DEFAULT_ASSET_DEFINITION_ID, new AssetValueType.Store())
    +            .buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(registerAssetTx).get(getTxTimeout().getSeconds(), TimeUnit.SECONDS);
    +
    +        final VersionedSignedTransaction keyValueTx = TransactionBuilder.Companion
    +            .builder()
    +            .account(ALICE_ACCOUNT_ID)
    +            .setKeyValue(
    +                DEFAULT_ASSET_DEFINITION_ID,
    +                assetKey,
    +                assetValue
    +            ).buildSigned(ALICE_KEYPAIR);
    +        client.sendTransactionAsync(keyValueTx).get(10, TimeUnit.SECONDS);
    +
    +        final QueryAndExtractor<Value> assetDefinitionValueQuery = QueryBuilder
    +            .findAssetDefinitionKeyValueByIdAndKey(DEFAULT_ASSET_DEFINITION_ID, assetKey)
    +            .account(ALICE_ACCOUNT_ID)
    +            .buildSigned(ALICE_KEYPAIR);
    +        final CompletableFuture<Value> future = client.sendQueryAsync(assetDefinitionValueQuery);
    +
    +        final Value value = future.get(10, TimeUnit.SECONDS);
    +        Assertions.assertEquals(
    +            ((Value.String) value).getString(),
    +            assetValue.getString()
    +        );
    +    }
    +}
    + + + + \ No newline at end of file diff --git a/guide/get-started/python.html b/guide/get-started/python.html new file mode 100644 index 000000000..e8ab5ea49 --- /dev/null +++ b/guide/get-started/python.html @@ -0,0 +1,106 @@ + + + + + + Python 3 Guide | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Python 3 Guide

    WARNING

    WIP: The iroha-python SDK only works with the iroha2-lts for now. It applies both to the iroha2-edge and the iroha2 branches. Our team recommends using the iroha2-edge branch while we update the iroha2 one.

    1. Iroha 2 Client Setup

    There are two versions of Iroha Python to choose from. In theory, the Iroha 1 version of Iroha Python (that also has the best documentation) should be compatible with an Iroha 2 deployment.

    Thus we should build and install the Iroha 2 compatible version of Iroha-python, using (for now) its GitHub repository.

    Let's create a separate folder for Iroha Python and clone its GitHub repository into it:

    bash
    $ cd ~/Git/
    +$ git clone https://github.com/hyperledger/iroha-python/ --branch iroha2
    +$ cd iroha-python
    $ cd ~/Git/
    +$ git clone https://github.com/hyperledger/iroha-python/ --branch iroha2
    +$ cd iroha-python

    Iroha Python is written in Rust using the PyO3 library. Thus, unlike most Python packages, you must build it first:

    bash
    $ pip install maturin
    +$ maturin build
    $ pip install maturin
    +$ maturin build

    After the build is complete, install it into your system:

    bash
    $ pip install ./target/wheels/iroha_python-*.whl
    $ pip install ./target/wheels/iroha_python-*.whl

    Finally, you will need a working client configuration:

    bash
    $ cp -vfr ~/Git/iroha/configs/client/config.json example/config.json
    $ cp -vfr ~/Git/iroha/configs/client/config.json example/config.json

    TIP

    You can also use the provided config.json in the example folder if you also call docker-compose up from that same folder. This has to do with the fact that the configuration for the Docker files in Iroha Python is slightly different.

    2. Configuring Iroha 2

    Unlike iroha_client_cli, finding the configuration file in a scripting language is the responsibility of the person writing the script. The easiest method is to de-serialise a dictionary type from the provided config.json.

    This is an example of how you could do that in Python:

    python
    import json
    +from iroha2 import Client
    +
    +cfg = json.loads(open("config.json").read())
    +cl = Client(cfg)
    import json
    +from iroha2 import Client
    +
    +cfg = json.loads(open("config.json").read())
    +cl = Client(cfg)

    If the configuration file is malformed, you can expect an exception to notify you. However, the client doesn't do any verification: if the account used in config.json is not in the blockchain or has the wrong private key, you won't know that until you try and execute a simple instruction. More on that in the following section.

    INFO

    It should also be noted that Iroha Python is under heavy development. It severely lacks in documentation and its API can be made more idiomatically Python.

    3. Registering a Domain

    It is important to remember that Iroha Python is wrapping Rust code. As such, many of Python idioms are not yet accommodated; for example, there's no duck-typing of the Register instruction.

    python
    from iroha2.data_model.isi import *
    +from iroha2.data_model.domain import *
    +
    +domain = Domain("looking_glass")
    +register = Register(Expression(Value(Identifiable(domain))))
    from iroha2.data_model.isi import *
    +from iroha2.data_model.domain import *
    +
    +domain = Domain("looking_glass")
    +register = Register(Expression(Value(Identifiable(domain))))

    Instead, we are creating a domain and wrapping it in multiple type-erasing constructs. A domain is wrapped in Identifiable (which would be a trait in Rust), which is wrapped in Value, which is wrapped in Expression, which finally is wrapped in the Register instruction. This is not entirely against Python conventions (it is strongly typed, after all), and not entirely counter-intuitive, once you see the corresponding Rust code.

    The instruction to register must be submitted, in order for anything to happen.

    python
    hash = cl.submit_isi(register)
    hash = cl.submit_isi(register)

    Note that we also keep track of the hash of the transaction. This will become useful when you visualize the output.

    4. Registering an Account

    Registering an account is similar to the process of registering a domain, except the wrapping structures are different. There are a couple of things to watch out for.

    First of all, we can only register an account to an existing domain. The best UX design practices dictate that you should check if the requested domain exists now, and if it doesn't, suggest a fix to the user.

    python
    from iroha2.data_model.isi import *
    +from iroha2.data_model.account import *
    +
    +public_key =# Get this from white_rabbit.
    +bunny = Account("white_rabbit@looking_glass", signatories=[public_key])
    +register = Register(Expression(Value(Identifiable(bunny))))
    from iroha2.data_model.isi import *
    +from iroha2.data_model.account import *
    +
    +public_key =# Get this from white_rabbit.
    +bunny = Account("white_rabbit@looking_glass", signatories=[public_key])
    +register = Register(Expression(Value(Identifiable(bunny))))

    Second, you should provide the account with a public key. It is tempting to generate both the public and the private key at this time, but it isn't the brightest idea. Remember that the white_rabbit trusts you, alice@wonderland, to create an account for them in the domain looking_glass, but doesn't want you to have access to that account after creation.

    If you gave white_rabbit a key that you generated yourself, how would they know if you don't have a copy of their private key? Instead, the best way is to ask white_rabbit to generate a new key-pair, and then give you the public half of it.

    After putting all of this together, we submit it as before:

    python
    hash = cl.submit_isi(register)
    hash = cl.submit_isi(register)

    5. Registering and minting assets

    Iroha has been built with few underlying assumptions about what the assets need to be in terms of their value type and characteristics (fungible or non-fungible, mintable or non-mintable).

    Asset creation is by far the most cumbersome:

    python
    import iroha2.data_model.asset as asset
    +from iroha2.sys.iroha_data_model import Value
    +
    +time = asset.Definition(
    +    value_type=asset.ValueType.Quantity,
    +    id=asset.DefinitionId(name="time", domain_name="looking_glass"),
    +    metadata={"a": Value.U32(10)},
    +    mintable=False
    +)
    import iroha2.data_model.asset as asset
    +from iroha2.sys.iroha_data_model import Value
    +
    +time = asset.Definition(
    +    value_type=asset.ValueType.Quantity,
    +    id=asset.DefinitionId(name="time", domain_name="looking_glass"),
    +    metadata={"a": Value.U32(10)},
    +    mintable=False
    +)

    Note the following; First, we used the **kwargs syntax to make everything more explicit.

    We have a value_type which must be specified. Python is duck-typed, while Rust isn't. Make sure that you track the types diligently, and make use of mypy annotations.

    The Quantity value type is an internal 32-bit unsigned integer. Your other options are BigQuantity, which is a 128-bit unsigned integer, and Fixed. All of these are unsigned. Any checked operation with a negative Fixed value (one that you got by converting a negative floating-point number), will result in an error.

    Continuing the theme of explicit typing, the asset.DefinitionId is its own type. We could have also written asset.DefinitionId.parse("time#looking_glass"), but making sure that you know what's going on is more useful in this case.

    Finally, we have mintable. By default this is set to True, however, setting it to False means that any attempt to mint more of time#looking_glass is doomed to fail. Unfortunately, since we didn't add any time at genesis, the white_rabbit will never have time. There just isn't any in his domain, and more can't be minted.

    OK. So how about a mint demonstration? Fortunately, alice@wonderland has an asset called roses#wonderland, which can be minted. For that we need to do something much simpler.

    python
    amount = Expression(Value(U32(42)))
    +destination = Expression(Value(Identifiable(asset.DefinitionId.parse("rose#wonderland"))))
    +mint_amount = Mint(amount, destination)
    +cl.submit_isi(mint_amount)
    amount = Expression(Value(U32(42)))
    +destination = Expression(Value(Identifiable(asset.DefinitionId.parse("rose#wonderland"))))
    +mint_amount = Mint(amount, destination)
    +cl.submit_isi(mint_amount)

    This would add 42 to the current tally of roses that Alice has.

    6. Visualizing outputs

    The paradigm that Iroha chose to allow monitoring some events is the filter-map paradigm. Let's look at what we need to do in order to know e.g. what happened to a submitted instruction.

    First, we'll need to remember the hash of the transaction that we want to track, next we create a filter:

    python
    filter = EventFilter.Pipeline(
    +    pipeline.EventFilter(
    +        entity=pipeline.EntityType.Transaction(),
    +        hash=None,
    +    ))
    filter = EventFilter.Pipeline(
    +    pipeline.EventFilter(
    +        entity=pipeline.EntityType.Transaction(),
    +        hash=None,
    +    ))

    And add a listener on that filter. Don't worry, the Rust side of the process is asynchronous, so barring issues with the GIL, you won't lock up your interpreter.

    Note the types. The EventFilter is a type that filters out anything that isn't an event (and non-event types are beyond the scope of this tutorial). The pipeline module helps us by providing a concrete type of EventFilter, namely one that listens for transactions. Note that we haven't used the hash here.

    Finally, we add a listening filter to the client:

    python
    listener = cl.listen(filter)
    listener = cl.listen(filter)

    Now we must actually listen for events:

    python
    for event in listener:
    +    print(event)
    +
    +    if event["Pipeline"]["status"] == "Committed" \
    +        and event["Pipeline"]["hash"] == hash:
    +        break
    for event in listener:
    +    print(event)
    +
    +    if event["Pipeline"]["status"] == "Committed" \
    +        and event["Pipeline"]["hash"] == hash:
    +        break

    And now, we have an infinite loop that will not quit until the event gets committed.

    WARNING

    Nobody should do this in production code, and instead monitor the event queue for (at least) the possibility that the transaction gets Rejected.

    + + + + \ No newline at end of file diff --git a/guide/get-started/quick-start.html b/guide/get-started/quick-start.html new file mode 100644 index 000000000..e5f8acf23 --- /dev/null +++ b/guide/get-started/quick-start.html @@ -0,0 +1,96 @@ + + + + + + Quick Start with Docker | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Quick Start with Docker

    1. Install the prerequisites:

    2. Install Iroha from GitHub.

    3. Run docker-compose to bring up a network of 4 containerised peers:

      bash
      $ docker-compose up
      $ docker-compose up

      Depending on your set-up, this might either pull the image off of DockerHub, or build the container locally. After this process is complete, you'll be greeted with,

      [+] Running 4/0
      + ⠿ Container iroha-iroha2-1  Created                                         0.0s
      + ⠿ Container iroha-iroha0-1  Created                                         0.0s
      + ⠿ Container iroha-iroha3-1  Created                                         0.0s
      + ⠿ Container iroha-iroha1-1  Created                                         0.0s
      +Attaching to iroha-iroha0-1, iroha-iroha1-1, iroha-iroha2-1, iroha-iroha3-1
      +iroha-iroha1-1  | 2023-02-27T12:43:08.540651Z  INFO iroha: Hyperledgerいろは2にようこそ!
      +iroha-iroha1-1  | 2023-02-27T12:43:08.542379Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
      +iroha-iroha1-1  | 2023-02-27T12:43:08.542906Z  INFO iroha: Starting peer listen_addr=iroha1:1338
      +iroha-iroha1-1  | 2023-02-27T12:43:08.543188Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha1:1338
      +iroha-iroha1-1  | 2023-02-27T12:43:08.551356Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha1:1338
      +iroha-iroha1-1  | 2023-02-27T12:43:08.569289Z  INFO iroha_core::kura: Loaded 0 blocks at init.
      +iroha-iroha1-1  | 2023-02-27T12:43:08.572457Z ERROR iroha: Telemetry did not start
      +iroha-iroha1-1  | 2023-02-27T12:43:08.594190Z  INFO iroha: Starting Iroha
      +iroha-iroha2-1  | 2023-02-27T12:43:08.698491Z  INFO iroha: Hyperledgerいろは2にようこそ!
      +iroha-iroha2-1  | 2023-02-27T12:43:08.700998Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
      +iroha-iroha2-1  | 2023-02-27T12:43:08.701624Z  INFO iroha: Starting peer listen_addr=iroha2:1339
      +iroha-iroha2-1  | 2023-02-27T12:43:08.701895Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha2:1339
      +iroha-iroha2-1  | 2023-02-27T12:43:08.707759Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha2:1339
      +iroha-iroha0-1  | 2023-02-27T12:43:08.719683Z  INFO iroha: Hyperledgerいろは2にようこそ!
      +iroha-iroha0-1  | 2023-02-27T12:43:08.722029Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
      +iroha-iroha2-1  | 2023-02-27T12:43:08.730201Z  INFO iroha_core::kura: Loaded 1 blocks at init.
      +iroha-iroha2-1  | 2023-02-27T12:43:08.746975Z ERROR iroha: Telemetry did not start
      +iroha-iroha0-1  | 2023-02-27T12:43:08.748879Z  INFO iroha: Starting peer listen_addr=iroha0:1337
      +iroha-iroha0-1  | 2023-02-27T12:43:08.749155Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha0:1337
      +iroha-iroha0-1  | 2023-02-27T12:43:08.754613Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha0:1337
      +iroha-iroha3-1  | 2023-02-27T12:43:08.753230Z  INFO iroha: Hyperledgerいろは2にようこそ!
      +iroha-iroha3-1  | 2023-02-27T12:43:08.754934Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
      +iroha-iroha3-1  | 2023-02-27T12:43:08.755503Z  INFO iroha: Starting peer listen_addr=iroha3:1340
      +iroha-iroha3-1  | 2023-02-27T12:43:08.755802Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha3:1340
      +iroha-iroha3-1  | 2023-02-27T12:43:08.760437Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha3:1340
      +iroha-iroha2-1  | 2023-02-27T12:43:08.763216Z  INFO iroha: Starting Iroha
      [+] Running 4/0
      + ⠿ Container iroha-iroha2-1  Created                                         0.0s
      + ⠿ Container iroha-iroha0-1  Created                                         0.0s
      + ⠿ Container iroha-iroha3-1  Created                                         0.0s
      + ⠿ Container iroha-iroha1-1  Created                                         0.0s
      +Attaching to iroha-iroha0-1, iroha-iroha1-1, iroha-iroha2-1, iroha-iroha3-1
      +iroha-iroha1-1  | 2023-02-27T12:43:08.540651Z  INFO iroha: Hyperledgerいろは2にようこそ!
      +iroha-iroha1-1  | 2023-02-27T12:43:08.542379Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
      +iroha-iroha1-1  | 2023-02-27T12:43:08.542906Z  INFO iroha: Starting peer listen_addr=iroha1:1338
      +iroha-iroha1-1  | 2023-02-27T12:43:08.543188Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha1:1338
      +iroha-iroha1-1  | 2023-02-27T12:43:08.551356Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha1:1338
      +iroha-iroha1-1  | 2023-02-27T12:43:08.569289Z  INFO iroha_core::kura: Loaded 0 blocks at init.
      +iroha-iroha1-1  | 2023-02-27T12:43:08.572457Z ERROR iroha: Telemetry did not start
      +iroha-iroha1-1  | 2023-02-27T12:43:08.594190Z  INFO iroha: Starting Iroha
      +iroha-iroha2-1  | 2023-02-27T12:43:08.698491Z  INFO iroha: Hyperledgerいろは2にようこそ!
      +iroha-iroha2-1  | 2023-02-27T12:43:08.700998Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
      +iroha-iroha2-1  | 2023-02-27T12:43:08.701624Z  INFO iroha: Starting peer listen_addr=iroha2:1339
      +iroha-iroha2-1  | 2023-02-27T12:43:08.701895Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha2:1339
      +iroha-iroha2-1  | 2023-02-27T12:43:08.707759Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha2:1339
      +iroha-iroha0-1  | 2023-02-27T12:43:08.719683Z  INFO iroha: Hyperledgerいろは2にようこそ!
      +iroha-iroha0-1  | 2023-02-27T12:43:08.722029Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
      +iroha-iroha2-1  | 2023-02-27T12:43:08.730201Z  INFO iroha_core::kura: Loaded 1 blocks at init.
      +iroha-iroha2-1  | 2023-02-27T12:43:08.746975Z ERROR iroha: Telemetry did not start
      +iroha-iroha0-1  | 2023-02-27T12:43:08.748879Z  INFO iroha: Starting peer listen_addr=iroha0:1337
      +iroha-iroha0-1  | 2023-02-27T12:43:08.749155Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha0:1337
      +iroha-iroha0-1  | 2023-02-27T12:43:08.754613Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha0:1337
      +iroha-iroha3-1  | 2023-02-27T12:43:08.753230Z  INFO iroha: Hyperledgerいろは2にようこそ!
      +iroha-iroha3-1  | 2023-02-27T12:43:08.754934Z  INFO iroha: (translation) Welcome to Hyperledger Iroha 2!
      +iroha-iroha3-1  | 2023-02-27T12:43:08.755503Z  INFO iroha: Starting peer listen_addr=iroha3:1340
      +iroha-iroha3-1  | 2023-02-27T12:43:08.755802Z  INFO iroha_p2p::network: Binding listener listen_addr=iroha3:1340
      +iroha-iroha3-1  | 2023-02-27T12:43:08.760437Z  INFO iroha_p2p::network: Starting network actor listen_addr=iroha3:1340
      +iroha-iroha2-1  | 2023-02-27T12:43:08.763216Z  INFO iroha: Starting Iroha
    4. Follow the Bash tutorial to check out Iroha's capabilities.

    5. When you're done with the test network, just hit Control + C to stop the containers (⌃ + C on Mac).

    Docker Options

    You might also be interested in other options for local compilation:

    • For testing Iroha code quickly, you can use docker-compose-single.yml, which starts a container with a single peer.
    • For testing Iroha code in normal conditions, you can use docker-compose-local.yml, which starts 4 connected containers with peers.

    INFO

    Please note that there is ongoing work to make our configurations for Docker even more customizable with the help of Swarm.

    + + + + \ No newline at end of file diff --git a/guide/get-started/rust.html b/guide/get-started/rust.html new file mode 100644 index 000000000..a33b3a497 --- /dev/null +++ b/guide/get-started/rust.html @@ -0,0 +1,216 @@ + + + + + + Rust Guide | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Rust Guide

    1. Iroha 2 Client Setup

    In this part we shall cover the process of using the Iroha 2 Rust libraries. Instead of providing the complete basics, we shall assume knowledge of the most widely used concepts, explain what's unusual about Iroha 2 specifically, and provide a step-by-step guide to creating your own Rust client for it.

    We assume that you know how to create a new package and have basic understanding of the fundamental Rust code; async functions, enum types, traits and borrowing/ownership, as well as the libraries that we use: serde, tokio, tracing, etc.

    TIP

    If you don't feel comfortable with any of the above, we recommend consulting the Rust book and docs.rs.

    Iroha 2 makes extensive use of workspaces. Currently, there are two workspaces, the one that contains the WASM support library and the one that contains the core support libraries, which go in a domain-first order. What that means is that instead of having a global constants crate, we have a crate for the blockchain data model (iroha_data_model), a crate with cryptographic primitives (iroha_crypto), and so on. These, individually, have a module for constants.

    If you add iroha_client to the other two crates, you get the minimum number of dependencies to start your own client, similar to iroha_client_cli.

    Once the initial v2.0.0 release is complete, we plan to create a package on crates.io with all the documentation. In the meantime, you could use the local copy that you've just created in the previous step as a local installation in your client's Cargo.toml:

    toml
    [dependencies]
    +iroha_client = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/client" }
    +iroha_data_model = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/data_model" }
    +iroha_crypto = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/crypto" }
    +iroha_config = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/config" }
    [dependencies]
    +iroha_client = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/client" }
    +iroha_data_model = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/data_model" }
    +iroha_crypto = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/crypto" }
    +iroha_config = { version = "=2.0.0-pre-rc.13", path = "~/Git/iroha/config" }

    The added benefit of using a local copy is that you have access to the minimal BFT network in the form of docker-compose.yml, which allows you to experiment. The drawbacks are mitigated by the fact that Rust links statically by default, so we recommend you experiment with the local set up first.

    INFO

    You could also make use of our test_network crate, which is available via GitHub but not via crates.io.

    You would also benefit from having immediate access to the example configurations in the ~/Git/iroha/configs folder.

    So let's copy the example client configuration somewhere useful:

    bash
    $ cp -vfr ~/Git/iroha/configs/client/config.json example/config.json
    $ cp -vfr ~/Git/iroha/configs/client/config.json example/config.json

    We recommend looking through it to familiarise yourself with the key pieces of information that every Iroha 2 client needs. Specifically, each client operates on behalf of a pre-existing account. These accounts are identified by a name@domain_name ID and can only be accessed provided that you know their specific key.

    2. Configuring Iroha 2

    Your application written in Rust needs to instantiate a client. The client typically needs specific configuration options, which you could either generate or load from the provided config.json. Let's do that now:

    rs
    use iroha_config::client::Configuration;
    +use iroha_data_model::TryToValue;
    use iroha_config::client::Configuration;
    +use iroha_data_model::TryToValue;
    rs
    let config_loc = "../configs/client/config.json";
    +let file = File::open(config_loc)
    +    .wrap_err("Unable to load the configuration file at `.....`")
    +    .expect("Config file is loading normally.");
    +let config: Configuration = serde_json::from_reader(file)
    +    .wrap_err("Failed to parse `../configs/client/config.json`")
    +    .expect("Verified in tests");
    let config_loc = "../configs/client/config.json";
    +let file = File::open(config_loc)
    +    .wrap_err("Unable to load the configuration file at `.....`")
    +    .expect("Config file is loading normally.");
    +let config: Configuration = serde_json::from_reader(file)
    +    .wrap_err("Failed to parse `../configs/client/config.json`")
    +    .expect("Verified in tests");

    Using said configuration, instantiate a client:

    rs
    // Create an Iroha client
    +let iroha_client: Client = Client::new(&config)?;
    // Create an Iroha client
    +let iroha_client: Client = Client::new(&config)?;

    Note that it used to be necessary to create a mutable client. Sending and receiving messages affects the client's internal state, but now that state is hidden behind interior mutable smart pointers.

    Of course, depending on your application, you might want to de-serialise your ClientConfiguration structure from a different location. Perhaps, you might want to build the configuration in place using the command-line arguments, or perhaps, you're using the XDG specification to store the file persistently in a different location. For this purpose, it's useful to try and construct an instance of ClientConfiguration:

    rust
    use iroha_core::prelude::*;
    +use iroha_data_model::prelude::*;
    +
    +let kp = KeyPair::new(
    +    PublicKey::from_str(
    +        r#"ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"#,
    +    )?,
    +    PrivateKey::from_hex(
    +        Algorithm::Ed25519,
    +        "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
    +            .into(),
    +    )?
    +)?;
    +
    +let (public_key, private_key) = kp.clone().into();
    +let account_id: AccountId = "alice@wonderland".parse()?;
    +
    +let config = ClientConfiguration {
    +    public_key,
    +    private_key,
    +    account_id,
    +    torii_api_url: SmallStr::from_string(iroha_config::torii::uri::DEFAULT_API_URL.to_owned()),
    +    ..ClientConfiguration::default()
    +};
    use iroha_core::prelude::*;
    +use iroha_data_model::prelude::*;
    +
    +let kp = KeyPair::new(
    +    PublicKey::from_str(
    +        r#"ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"#,
    +    )?,
    +    PrivateKey::from_hex(
    +        Algorithm::Ed25519,
    +        "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
    +            .into(),
    +    )?
    +)?;
    +
    +let (public_key, private_key) = kp.clone().into();
    +let account_id: AccountId = "alice@wonderland".parse()?;
    +
    +let config = ClientConfiguration {
    +    public_key,
    +    private_key,
    +    account_id,
    +    torii_api_url: SmallStr::from_string(iroha_config::torii::uri::DEFAULT_API_URL.to_owned()),
    +    ..ClientConfiguration::default()
    +};

    INFO

    Note that the keys in client configuration are given in multi-hash format. If you are experiencing issues parsing the keys in this format, check the troubleshooting section.

    3. Registering a Domain

    Registering a domain is a relatively easy operation. Most of the boilerplate code has to do with setting up the Iroha 2 client and deserialising its configuration.

    rs
    use iroha_client::client::Client;
    +use iroha_data_model::{
    +    metadata::UnlimitedMetadata,
    +    prelude::{Domain, DomainId, InstructionBox, RegisterBox},
    +};
    use iroha_client::client::Client;
    +use iroha_data_model::{
    +    metadata::UnlimitedMetadata,
    +    prelude::{Domain, DomainId, InstructionBox, RegisterBox},
    +};

    To register a domain, you need the domain name:

    rs
    // Create a domain Id
    +let looking_glass: DomainId = "looking_glass".parse()?;
    // Create a domain Id
    +let looking_glass: DomainId = "looking_glass".parse()?;

    Which we convert into an instruction:

    rs
    // Create an ISI
    +let create_looking_glass = RegisterBox::new(Domain::new(looking_glass));
    // Create an ISI
    +let create_looking_glass = RegisterBox::new(Domain::new(looking_glass));

    Note that we use RegisterBox and IdentifiableBox. Despite what your instincts as a Rust developer might suggest, we're not actually using any kind of dynamic dispatch. There's no dyn anywhere, and RegisterBox isn't an alias for Box<dyn Register>.

    A RegisterBox is a specialised enum that uses static dispatch to achieve what looks like dynamic dispatch, without any heap allocation. If you want to add more types to RegisterBox you must either open an issue on GitHub, or do that by yourself on a local fork of Iroha.

    The instruction is then batched into a transaction:

    rs
    // Prepare a transaction
    +let metadata = UnlimitedMetadata::default();
    +let instructions: Vec<InstructionBox> = vec![create_looking_glass.into()];
    +let tx = iroha_client
    +    .build_transaction(instructions, metadata)
    +    .wrap_err("Error building a domain registration transaction")?;
    // Prepare a transaction
    +let metadata = UnlimitedMetadata::default();
    +let instructions: Vec<InstructionBox> = vec![create_looking_glass.into()];
    +let tx = iroha_client
    +    .build_transaction(instructions, metadata)
    +    .wrap_err("Error building a domain registration transaction")?;

    Which is then submitted into the pipeline:

    rs
    // Submit a prepared domain registration transaction
    +iroha_client
    +    .submit_transaction(&tx)
    +    .wrap_err("Failed to submit transaction")?;
    // Submit a prepared domain registration transaction
    +iroha_client
    +    .submit_transaction(&tx)
    +    .wrap_err("Failed to submit transaction")?;

    Note the question mark here. This will return an Err variant if there's something immediately and obviously wrong with the transaction: for example, if it couldn't submit the transaction to the peer (e.g. there's no connection), or if the transaction got rejected with an error. The cost is that the submit_transaction function is synchronous.

    We could have also done the following:

    rust
    iroha_client
    +    .submit_with_metadata(create_looking_glass, UnlimitedMetadata::default())?;
    iroha_client
    +    .submit_with_metadata(create_looking_glass, UnlimitedMetadata::default())?;

    or

    rust
    iroha_client.submit(create_looking_glass)?;
    iroha_client.submit(create_looking_glass)?;

    The latter style is just syntactic sugar. Every submission comes in the form of a transaction that has metadata.

    While the latter is a convenient shorthand that we shall use frequently, we strongly advise using explicit construction in production code.

    INFO

    It is likely that we shall replace most if not all instances of submit in our code base with explicit transactions.

    4. Registering an Account

    Registering an account is a bit more involved than registering a domain. With a domain, the only concern is the domain name. However, with an account, there are a few more things to worry about.

    First of all, we need to create an AccountId. Note that we can only register an account to an existing domain. The best UX design practices dictate that you should check if the requested domain exists now, and if it doesn't, suggest a fix to the user. After that, we can create a new account named white_rabbit.

    rs
    use iroha_data_model::prelude::AccountId;
    +
    +// Create an `iroha_data_model::AccountId` instance
    +// with a DomainId instance and a Domain ID for an account
    +let longhand_account_id = AccountId::new("white_rabbit".parse()?, "looking_glass".parse()?);
    +let account_id: AccountId = "white_rabbit@looking_glass"
    +    .parse()
    +    .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after");
    +
    +// Check that two ways to define an account match
    +assert_eq!(account_id, longhand_account_id);
    use iroha_data_model::prelude::AccountId;
    +
    +// Create an `iroha_data_model::AccountId` instance
    +// with a DomainId instance and a Domain ID for an account
    +let longhand_account_id = AccountId::new("white_rabbit".parse()?, "looking_glass".parse()?);
    +let account_id: AccountId = "white_rabbit@looking_glass"
    +    .parse()
    +    .expect("Valid, because the string contains no whitespace, has a single '@' character and is not empty after");
    +
    +// Check that two ways to define an account match
    +assert_eq!(account_id, longhand_account_id);

    Second, you should provide the account with a public key. It is tempting to generate both it and the private key at this time, but it isn't the brightest idea. Remember, that the white_rabbit trusts you, alice@wonderland, to create an account for them in the domain _looking_glass, but doesn't want you to have access to that account after creation.

    If you gave white_rabbit a key that you generated yourself, how would they know if you don't have a copy of their private key? Instead, the best way is to ask white_rabbit to generate a new key-pair, and give you the public half of it:

    rust
    let key: PublicKey = get_key_from_white_rabbit();
    let key: PublicKey = get_key_from_white_rabbit();

    Only then do we build an instruction from it:

    rust
    let create_account =
    +    RegisterBox::new(IdentifiableBox::from(NewAccount::with_signatory(id, key)));
    let create_account =
    +    RegisterBox::new(IdentifiableBox::from(NewAccount::with_signatory(id, key)));

    Which is then wrapped in a transaction and submitted to the peer as in the previous section.

    5. Registering and minting assets

    Iroha has been built with few underlying assumptions about what the assets need to be in terms of their value type and characteristics (fungible or non-fungible, mintable or non-mintable).

    To register an asset, we first construct an iroha_data_model::asset::DefinitionId like so:

    rs
    // Create an asset
    +let asset_def_id = AssetDefinitionId::from_str("time#looking_glass")
    +    .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after");
    // Create an asset
    +let asset_def_id = AssetDefinitionId::from_str("time#looking_glass")
    +    .expect("Valid, because the string contains no whitespace, has a single '#' character and is not empty after");

    INFO

    Note that we use # symbol to separate the name of the asset from the domain to which it belongs. This is intentional. This reflects the rule that there can be many alices in many domains, with only one alice per domain, and there can be an asset that is also named alice, but there can be only one, regardless of type.

    Then construct an instruction:

    rs
    // Initialise the registration time
    +let register_time =
    +    RegisterBox::new(AssetDefinition::fixed(asset_def_id.clone()).mintable_once());
    +
    +// Submit a registration time
    +iroha_client.submit(register_time)?;
    // Initialise the registration time
    +let register_time =
    +    RegisterBox::new(AssetDefinition::fixed(asset_def_id.clone()).mintable_once());
    +
    +// Submit a registration time
    +iroha_client.submit(register_time)?;

    This creates an asset time that can only be minted once and has the type fixed. AssetDefinition::fixed just like its other cousins (quantity and big_quantity) returns a builder of an AssetDefinition.

    This asset is mintable_once, which means that the next time we mint it, we have to specify the entire amount that is going to exist for the rest of the existence of the blockchain.

    rs
    // Create a MintBox using a previous asset and account
    +let mint = MintBox::new(
    +    12.34_f64.try_to_value()?,
    +    IdBox::AssetId(AssetId::new(asset_def_id, account_id)),
    +);
    +
    +// Submit a minting transaction
    +iroha_client.submit_all([mint])?;
    // Create a MintBox using a previous asset and account
    +let mint = MintBox::new(
    +    12.34_f64.try_to_value()?,
    +    IdBox::AssetId(AssetId::new(asset_def_id, account_id)),
    +);
    +
    +// Submit a minting transaction
    +iroha_client.submit_all([mint])?;

    Now imagine that the white_rabbit@looking_glass was not very keen and didn't notice that he wanted 123.4_f64 as the amount of time. Now white rabbit notices the problem and thinks "oh dear, not a lot of time has passed, perhaps I can give myself some more", and submits another mint request with 111.06_f64 instead of the original 12.34_f64. But, alas, no such luck. The white rabbit cannot mint more time and is thus perpetually late.

    Roses, by contrast, are already registered in the network during the genesis round, and belong to alice@wonderland. Moreover, when they were registered, we didn't add the restriction, so we can mint them again and again as alice:

    rs
    let mint_roses = MintBox::new(
    +    42_u32.to_value(),
    +    IdBox::AssetId(AssetId::new(roses, alice)),
    +);
    let mint_roses = MintBox::new(
    +    42_u32.to_value(),
    +    IdBox::AssetId(AssetId::new(roses, alice)),
    +);

    Then submit the instruction as usual:

    rs
    iroha_client
    +    .submit(mint_roses)
    +    .wrap_err("Failed to submit transaction")?;
    iroha_client
    +    .submit(mint_roses)
    +    .wrap_err("Failed to submit transaction")?;

    INFO

    Our assets are strongly typed. As such, when you create a MintBox, you need to check that the asset has the correct underlying type. If you don't know the type, query it. This is also why we specifically annotate numerical literals with their type.

    Contrary to what you might think, this restriction isn't just for pedantry. Implicit conversion errors are the bane of all programmers, if you got the AssetValueType incorrect, how do you know that it was the only mistake in that transaction?

    6. Burning assets

    Burning assets is quite similar to minting. First, you create the burn instruction indicating which asset to burn and its quantity.

    rs
    // Burn the Asset instance
    +let burn_roses = BurnBox::new(
    +    10_u32.to_value(),
    +    IdBox::AssetId(AssetId::new(roses, alice)),
    +);
    // Burn the Asset instance
    +let burn_roses = BurnBox::new(
    +    10_u32.to_value(),
    +    IdBox::AssetId(AssetId::new(roses, alice)),
    +);

    Then submit this instruction:

    rust
    iroha_client.submit(burn_roses)?;
    iroha_client.submit(burn_roses)?;

    7. Visualising outputs

    Finally, we should talk about visualising data. The Rust API is currently the most complete in terms of available queries and instructions. After all, this is the language in which Iroha 2 was built.

    We shall, however, leave most of the aforementioned advanced features down the rabbit hole, up to the reader's own devices to discover. This document can easily get out of sync with the state of the API features. By contrast, the online documentation is always up to date. Plus a short tutorial wouldn't be able to do all these features justice. Instead, we shall retain parity with other language tutorials and introduce you to pipeline filters.

    There are two possible event filters: PipelineEventFilter and DataEventFilter, we shall focus on the former. This filter sieves events pertaining to the process of submitting a transaction, executing a transaction, and committing it to a block.

    First, let's build a filter:

    rust
    use iroha_data_model::prelude::*;
    +
    +let filter = FilterBox::Pipeline(PipelineEventFilter::identity());
    use iroha_data_model::prelude::*;
    +
    +let filter = FilterBox::Pipeline(PipelineEventFilter::identity());

    Then, we start listening for events in an infinite loop:

    rust
    for event in iroha_client.listen_for_events(filter)? {
    +    match event {
    +        Ok(event) => println!("Success: {:#?}", event),
    +        Err(err) => println!("Sadness:( {:#?}",  err),
    +    }
    +};
    for event in iroha_client.listen_for_events(filter)? {
    +    match event {
    +        Ok(event) => println!("Success: {:#?}", event),
    +        Err(err) => println!("Sadness:( {:#?}",  err),
    +    }
    +};

    Needless to say, an synchronous infinite blocking loop is bad UX for anything but a command-line program, but for illustration purposes, this would create a nice printout, just like in iroha_client_cli.

    + + + + \ No newline at end of file diff --git a/guide/get-started/tutorials.html b/guide/get-started/tutorials.html new file mode 100644 index 000000000..fc64ef208 --- /dev/null +++ b/guide/get-started/tutorials.html @@ -0,0 +1,34 @@ + + + + + + Introduction | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Introduction

    Welcome to the Hyperledger Iroha 2 tutorial. This document is designed to help you get started with Iroha 2, regardless of your knowledge of Hyperledger technology, coding experience or familiarity with blockchains.

    Preamble

    This tutorial is suitable for both experienced developers, prospective users, and people casually curious about blockchain technology. The level of detail is sufficient so that you wouldn't need any supplementary guide. However, should you want to learn more, we have more detailed documentation in the works.

    In this guide, we shall

    • walk you through starting an Iroha network, either with docker (recommended) or using one of the provided scripts;
    • introduce you to the client libraries;
    • take a small detour into the basic concepts of Iroha special instructions, and how they interact with the world state.

    We invite you to follow the tutorial in this order:

    1. If you are already familiar with Hyperledger Iroha, read about the differences between the two versions of Iroha.
    2. Install Iroha 2.
    3. Follow one of the language-specific guides to learn how to set up and configure Iroha 2, register a domain and an account, register and mind assets, and visualize outputs:

    Tutorial Updates

    The current iteration of the Iroha 2 tutorial is a constant work in progress. We are updating the tutorial with each release to reflect the state of Iroha and the newly added features. While we do our best to keep this tutorial up to date, it can go out of sync by a few days or maybe a week.

    + + + + \ No newline at end of file diff --git a/guide/introduction.html b/guide/introduction.html new file mode 100644 index 000000000..a4dd737e7 --- /dev/null +++ b/guide/introduction.html @@ -0,0 +1,34 @@ + + + + + + Iroha 2 | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Iroha 2

    Iroha is a fully-featured blockchain ledger. With Iroha you can:

    • Create and manage custom fungible assets, such as currencies, gold, and others
    • Create and manage non-fungible assets
    • Manage user accounts with a domain hierarchy and multi-signature transactions
    • Use efficient portable smart contracts implemented either via WebAssembly or Iroha Special Instructions
    • Use both permissioned and permission-less blockchain deployments

    Learn More

    For more information on Iroha 2, please take a look at the Iroha 2 Whitepaper, as well as the Hyperledger Iroha section within the Hyperledger Foundation Wiki.

    For more information on Iroha 1, take a look at the Iroha 1 documentation.

    TIP

    If you want to contribute to Hyperledger Iroha, please look at our Contributing Guide.

    + + + + \ No newline at end of file diff --git a/guide/iroha-2.html b/guide/iroha-2.html new file mode 100644 index 000000000..1a82b2cf1 --- /dev/null +++ b/guide/iroha-2.html @@ -0,0 +1,34 @@ + + + + + + Iroha 2 vs. Iroha 1 | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Iroha 2 vs. Iroha 1

    Iroha 2 is a complete re-write of Hyperledger Iroha in Rust. As of writing, the two projects are developed concurrently.

    In this introduction we outline the differences between the two versions of Iroha and highlight the new features of Iroha 2. It should be of particular interest to those who are already familiar with Iroha but wish to upgrade and start using the newer version.

    INFO

    Note that Iroha 1 and Iroha 2 are not compatible. Both projects are very different in their approaches and implementations as we present in the comparison below.

    Among other changes, cryptography and account structures are implemented differently, meaning that users would have to generate new keys and link to their old account through a centralized service and no data from Iroha 1 would be accessible from Iroha 2.

    Fault Tolerance

    Iroha 2 learned a great deal from the development of the original Iroha. Of particular importance is the new and improved Byzantine-fault-tolerant consensus algorithm: Sumeragi. This new consensus allowed us to expand what could be done on a blockchain without any security risks.

    INFO

    The first version of Iroha used a consensus algorithm called Yac. Yac is crash-fault-tolerant, which means that it can survive a set number of nodes crashing: i.e. losing power, being cut off from the network, or being destroyed with a sledgehammer.

    Sumeragi, by contrast, was designed to be Byzantine-fault-tolerant. This means that Iroha 2 can tolerate not only peers being inactive on the network, but also running malicious software and actively trying to falsify data in the blockchain.

    We can mathematically prove that Iroha 2 can work when up to 33% of its nodes are actively trying to stop Iroha 2 from working properly or at all. In other words, even if someone gained control of a third of all of your network nodes, an Iroha 2 deployment is mathematically guaranteed to keep working.

    Minimalist Code Base

    Iroha 2 is a minimalist code base. We take great care to vet our dependencies and avoid large inter-dependent chunks of code.

    We provide a few telemetry APIs, including prometheus tooling, structured logging in JSON, as well as compatibility with standard tools used in Parity Substrate.

    Our data is strongly-typed, our methods are statically dispatched. We make use of the best that Rust has to offer: serde and parity_scale_codec for serialisation, tokio for co-operative multi-threading, as well as testing, bench-marking, static analysis and code auditing tools that come packaged with the exemplary cargo.

    Our code is easy to reason about, and quick to compile, whilst also being ergonomic to use and thoughtfully crafted. We have no panics and no unsafe code.

    Flexibility

    Iroha 2 is also more flexible than the original Iroha due to its modular design.

    It is possible to add or remove features based on a particular use-case. If you want the blockchain to be extremely fast and work on embedded hardware, just compile Iroha 2 without the expensive-metrics feature. Don't use telemetry at all? Remove it entirely and enjoy even more performance. Permission sets are plugins that can be upgraded during run-time.

    We have an extensive module system as well as a robust WASM runtime framework.

    Smart Contracts

    Iroha 2 is an event-driven ledger. Each change in the state of the blockchain is necessarily accompanied by its own event that can trigger a smart contract: complex logic designed for use in on-chain scripting.

    For smart contracts, Iroha 2 supports two approaches:

    The first approach is useful when you want very simple transparent logic and also want to minimise the footprint in the blockchain. All interactions with the World state, that is, the state of the blockchain at this point in time, has to be done using the aforementioned instructions. There is also rudimentary support for domain-specific conditional logic.

    If you want to learn more about smart contracts in Iroha 2, please consult our Wiki.

    Static and Dynamic Linking

    Iroha 2 smartly chooses when to use dynamic linking. This strikes a balance between it being easy to patch a Critical security vulnerability in a vendored library like OpenSSL, but also remaining reproducible and portable across platforms, architectures, and deployments.

    INFO

    You get the best of both worlds. Patching a security vulnerability is as easy as running sudo apt upgrade. On the other hand, only security-critical dependencies are linked dynamically, so it is highly unlikely that any of the smaller and less important libraries can break Iroha after an upgrade.

    Testing

    Iroha 2 is extensively tested. Despite being in active development, Iroha has 80% line coverage. Keep in mind that line coverage includes documentation comments, some of which are also tests.

    INFO

    There are plans to include Fuzz testing, property-based testing, and failure-point testing to ensure that Iroha is reliable.

    + + + + \ No newline at end of file diff --git a/guide/reports/csd-rtgs.html b/guide/reports/csd-rtgs.html new file mode 100644 index 000000000..51c67c7dd --- /dev/null +++ b/guide/reports/csd-rtgs.html @@ -0,0 +1,34 @@ + + + + + + CSD/RTGS linkages Proof of concept | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    CSD/RTGS linkages Proof of concept

    In this document we will describe the CSD/RTGS linkages PoC execution via Iroha. This is a project which was done in collaboration with the Asian Development Bank, and Fujitsu. Other participants used technologies such as R3 Corda, Hyperledger Cactus (Cacti), Hyperledger Fabric and many other popular blockchain solutions.

    + + + + \ No newline at end of file diff --git a/guide/security/generating-cryptographic-keys.html b/guide/security/generating-cryptographic-keys.html new file mode 100644 index 000000000..4ed576923 --- /dev/null +++ b/guide/security/generating-cryptographic-keys.html @@ -0,0 +1,122 @@ + + + + + + Generating Cryptographic Keys | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Generating Cryptographic Keys

    In the realm of blockchain technology, cryptographic keys play a crucial role in upholding the security and authenticity of data transactions. With Iroha 2, you can create these vital keys to safeguard your digital assets and communications.

    This section describes how to generate keys using the kagami tool, shipped alongside Iroha 2.

    In the future, alternative methods of generating public keys shall be added.

    Generating Cryptographic Keys with Kagami

    TIP

    Since kagami does not come with a manual page, you can use the --help (-h) command to retrieve a brief summary of all the usable kagami parameters within the CLI you are using.

    After installing Iroha, run the following command from the project's root directory to generate a new key pair:

    bash
    $ cargo run --bin kagami --release -- crypto
    $ cargo run --bin kagami --release -- crypto

    You can specify a number of different parameters to tailor the generated key pair to your specific needs. The following parameters are available:

    • --algorithm (-a): Specifies the algorithm used for the key pair generation and encryption. If no algorithm is specified, ed25519 is used by default. Can be one of the following:

      • ed25519ECC an algorithm that utilises the Ed25519 curve, offering efficient and secure cryptographic operations for digital signatures and key exchange. If no algorithm is specified in a request, then ed25519 is used by default. Learn more:

      • secp256k1ECC an algorithm known for its application in blockchain systems like Bitcoin. It provides a robust foundation for secure key generation, digital signatures, and encryption.

      Learn more:
      Secp256k1 (Bitcoin Wiki)

      • bls_small — The Boneh-Lynn-Shacham algorithm with a small parameter configuration. This variant of the BLS cryptographic scheme is optimised for efficiency in certain resource-constrained environments while maintaining fundamental security properties.

      Learn more:
      BLS digital signature (Wikipedia)

      • bls_normal — The Boneh-Lynn-Shacham algorithm with a standard parameter configuration. This configuration of the BLS cryptographic scheme offers a balanced approach between efficiency and security, making it suitable for a wide range of applications in blockchain and cryptographic protocols.

      Learn more:
      BLS digital signature (Wikipedia)

    • --seed (-s): Specifies a string that serves as a deterministic starting point for the key pair generation. If a seed string is specified, kagami will generate the same key for the same string. If no seed is specified, a random seed value is chosen, and each invocation of kagami crypto will result in a different key. This parameter accepts a valid string of Unicode characters. For example, the seed string can contain not only numeric and latin, but also cyrillic, logographic (e.g., Japanese kanji characters) and ideographic (e.g., emojis) characters, as well as any font-related variations of those characters introduced to Unicode over the years.

      TIP

      If one chooses to use a seed, it must be treated as if it were a password: more randomness and longer seed strings make the cryptographic keys more resilient to dictionary attacks.

    • --private-key (-p): Specifies an existing private key as a string in the multihash format that is used to generate a public key.

    • --json (-j): Specifies that the output must be generated in the JSON format, which is mostly helpful for copy-and-pasting into the config.json file.

    • --compact (-c): Specifies that the output private and public keys are displayed on separate lines and are not labeled.

    Examples

    bash
    # Input
    +$ cargo run --bin kagami crypto
    +
    +# Possible Output (same layout, different keys)
    +Public key (multihash): "ed01206B0F56F58761060056355DBA0E0FC489CFB2F974481ED64873082E6032796235"
    +Private key (ed25519): "F71EA9D897C4338CBF4F1DC7B492AAD0BF6CE896B803D7CDB9CF25ECC15109826B0F56F58761060056355DBA0E0FC489CFB2F974481ED64873082E6032796235"
    # Input
    +$ cargo run --bin kagami crypto
    +
    +# Possible Output (same layout, different keys)
    +Public key (multihash): "ed01206B0F56F58761060056355DBA0E0FC489CFB2F974481ED64873082E6032796235"
    +Private key (ed25519): "F71EA9D897C4338CBF4F1DC7B492AAD0BF6CE896B803D7CDB9CF25ECC15109826B0F56F58761060056355DBA0E0FC489CFB2F974481ED64873082E6032796235"
    bash
    # Input
    +$ kagami crypto -a secp256k1
    +
    +# Possible output
    +Public key (multihash): "e70121022A9D6E0D54022C0E2752E43ADD91ADA28259E1F2CE0C6D4E9183FB2882DE6749"
    +Private key (secp256k1): "7687B1433FB6731E6DC635A376B3EB3B5FCD1E02C9775C1642E7FD5DA035EC75"
    # Input
    +$ kagami crypto -a secp256k1
    +
    +# Possible output
    +Public key (multihash): "e70121022A9D6E0D54022C0E2752E43ADD91ADA28259E1F2CE0C6D4E9183FB2882DE6749"
    +Private key (secp256k1): "7687B1433FB6731E6DC635A376B3EB3B5FCD1E02C9775C1642E7FD5DA035EC75"
    bash
    # Input
    +$ kagami crypto -s 1729
    +
    +# Exact output
    +Public key (multihash): "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
    +Private key (ed25519): "2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
    # Input
    +$ kagami crypto -s 1729
    +
    +# Exact output
    +Public key (multihash): "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
    +Private key (ed25519): "2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
    bash
    # Input
    +$ kagami crypto -p 2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
    +
    +# Exact output
    +Public key (multihash): "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
    +Private key (ed25519): "2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
    # Input
    +$ kagami crypto -p 2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
    +
    +# Exact output
    +Public key (multihash): "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
    +Private key (ed25519): "2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04"
    bash
    # Input
    +$ cargo run --bin kagami crypto -j
    +
    +# Possible output
    +{
    +  "public_key": "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04",
    +  "private_key": {
    +    "digest_function": "ed25519",
    +    "payload": "2669bb1099477b970e1d7d7c54e345a64a54213fcfba2465cbcd6d4e5091a71db678073cfae6e247a58b442661c7da0e13bac5031cbc6343ef566b8718d47d04"
    +  }
    +}
    # Input
    +$ cargo run --bin kagami crypto -j
    +
    +# Possible output
    +{
    +  "public_key": "ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04",
    +  "private_key": {
    +    "digest_function": "ed25519",
    +    "payload": "2669bb1099477b970e1d7d7c54e345a64a54213fcfba2465cbcd6d4e5091a71db678073cfae6e247a58b442661c7da0e13bac5031cbc6343ef566b8718d47d04"
    +  }
    +}
    bash
    # Input
    +$ cargo run --bin kagami crypto -c
    +
    +# Possible output
    +ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
    +2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
    +ed25519
    # Input
    +$ cargo run --bin kagami crypto -c
    +
    +# Possible output
    +ed0120B678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
    +2669BB1099477B970E1D7D7C54E345A64A54213FCFBA2465CBCD6D4E5091A71DB678073CFAE6E247A58B442661C7DA0E13BAC5031CBC6343EF566B8718D47D04
    +ed25519
    bash
    # Input
    +$ cargo run --bin kagami crypto -a bls_normal -s 2048
    +
    +# Exact output
    +Public key (multihash): "ea01610402A54ABCC40819F15E3553CC8D42D628EEAD7E1B10724BD2AFE523A7C0446EB1CB3F14D4500BD68C997784136FD056BA04215DFD2D3FDC7883B43AE94AC52B7D01525F5A80B41C01701502B46DBB9F0384CC7BE037DC2CBC928014E52A4C5C3B"
    +Private key (bls_normal): "0000000000000000000000000000000035D9120A174E35E966DD92DE90B2446D4B060C8B72018B3917A1C97D7E93EAEC"
    # Input
    +$ cargo run --bin kagami crypto -a bls_normal -s 2048
    +
    +# Exact output
    +Public key (multihash): "ea01610402A54ABCC40819F15E3553CC8D42D628EEAD7E1B10724BD2AFE523A7C0446EB1CB3F14D4500BD68C997784136FD056BA04215DFD2D3FDC7883B43AE94AC52B7D01525F5A80B41C01701502B46DBB9F0384CC7BE037DC2CBC928014E52A4C5C3B"
    +Private key (bls_normal): "0000000000000000000000000000000035D9120A174E35E966DD92DE90B2446D4B060C8B72018B3917A1C97D7E93EAEC"

    Other Operations with Kagami

    1. Building kagami

    The Iroha 2 node binary and all supporting tools are supplied in the official docker image. However, using it like this is cumbersome, as kagami is meant to be used as a standalone external tool, so building it from a source may be helpful.

    To build kagami, run the following:

    bash
    $ cargo build --bin kagami
    $ cargo build --bin kagami

    This will produce a single statically linked executable in the target/debug directory, that still links dynamically against the system-provided standard C-library.

    Note

    Iroha and all supporting tools can also be built to statically link against the musl standard library, which allows the application to run on any POSIX-compliant ELF-capable system (all GNU+Linux distributions, some BSD variants).

    2. Installing the source-built kagami into /bin

    There are multiple ways to make your command line be able to use the kagami version that you have just compiled. One of the easiest ways that should work on most systems is to move or link the binary into the /bin directory on UNIX systems.

    bash
    $ sudo mv target/debug/kagami /bin
    $ sudo mv target/debug/kagami /bin

    3. Moving kagami to the .local/bin directory

    To circumvent the requirement of having the binary in the global binary folder, and thus necessarily exposing the binary to all other users, as well as requiring root authentication (which is not always available), one can instead install the application as a regular user.

    To move kagami to the authenticated user's .local/bin directory, making it uniquely accessible only by that user, run the following:

    bash
    $ mv target/debug/kagami ~/.local/bin
    $ mv target/debug/kagami ~/.local/bin

    This method works on most GNU Linux distributions, but is not guaranteed to do so. If it doesn't, consult the next subtopic.

    Making the <username>/.local/bin directory available to the shell

    To make the <username>/.local/bin directory explicitly available to your shell's .rc file, perform the following:

    1. Check if kagami is available by running the following:
    bash
    $ whereis kagami
    +kagami:
    $ whereis kagami
    +kagami:
    1. Depending on the shell that you are using, perform one of the following:
    • If using Bash: Fix the PATH variable for the shell and then reload the .bashrc script by running the following:

      bash
      $ echo "export PATH='${HOME}/.local/bin:${PATH}'" >> ~/.bashrc
      +$ source ~/.bashrc
      $ echo "export PATH='${HOME}/.local/bin:${PATH}'" >> ~/.bashrc
      +$ source ~/.bashrc
    • If using Zsh: Fix the PATH variable for the shell and reload the .zshrc script by running the following:

      bash
      $ echo "export PATH='${HOME}/.local/bin:${PATH}'" >> ~/.zshrc
      +$ source ~/.zshrc
      $ echo "export PATH='${HOME}/.local/bin:${PATH}'" >> ~/.zshrc
      +$ source ~/.zshrc
    • If using fish: Fix the PATH variable for the shell variable permanently by running the following:

      bash
      $ fish_add_path ~/.local/bin
      $ fish_add_path ~/.local/bin

    ::: note

    In addition to the methods listed above, consult documentation for the shell you're using or consider adding the PATH variant to your terminal's session configuration.

    :::

    + + + + \ No newline at end of file diff --git a/guide/security/index.html b/guide/security/index.html new file mode 100644 index 000000000..bcaeb4dbf --- /dev/null +++ b/guide/security/index.html @@ -0,0 +1,34 @@ + + + + + + Security | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Security

    When utilizing Iroha 2—or any other blockchain ledger for that matter—security is paramount for financial organizations, as it forms the foundation of trust in an industry where sensitive financial data and transactions are routine. A successful security breach performed by a malicious party can lead to devastating consequences for you. Therefore establishing preemptive security measures is essential to protect the integrity and confidentiality of your sensitive data.

    In this section you can learn about various aspects of securing your Iroha 2 network. To learn more, choose one of the following topics:

    • Security Principles:

      The core security principles that individuals and organizations can adopt to protect their data and decrease the chance of a breach and/or leak.

    • Operational Security:

      Best practices for securing the day-to-day operations of your network, including access controls, monitoring, incident responses, the use of browsers, etc.

    • Password Security:

      A deep-dive into password entropy, creating strong passwords and avoiding password vulnerabilities.

    • Public Key Cryptography:

      An introduction into public key cryptography, encryption, signatures, and their role in establishing secure communication within the blockchain.

      • Generating Cryptographic Keys:

        Instructions on how to generate cryptographic keys and use kagami (a supporting tool shipped alongside Iroha 2).

      • Storing Cryptographic Keys:

        Best practices for securing your cryptographic keys with a number of different approaches that can also be combined.

    + + + + \ No newline at end of file diff --git a/guide/security/operational-security.html b/guide/security/operational-security.html new file mode 100644 index 000000000..374550e57 --- /dev/null +++ b/guide/security/operational-security.html @@ -0,0 +1,34 @@ + + + + + + Operational Security | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Operational Security

    Operational Security (OPSEC) is a systematic approach to security and risk management, which is essentially a collection of strategies and advice adopted for specific use-cases with the aim of preventing unauthorized access and data leakage.

    OPSEC is the standard practice for most companies to guarantee the availability and stability of their assets. This includes considering such factors as physical security (e.g., making sure that unattended post-it notes do not contain sensitive data), secure communication protocols (e.g., not sending sensitive data over unencrypted SMS), threat analysis (e.g., determining potential malicious parties, learning about the latest attack methods), personnel training (e.g., without employees following OPSEC measures, they will, sooner or later, prove to be ineffective), and risk mitigation (e.g., encrypting your hard drives and USB devices).

    Since Iroha 2 is likely to be deployed as a financial ledger, OPSEC measures and practices must be taken seriously. This topic describes strategies and approaches that individuals and organizations using Iroha 2 in their operations should consider as part of their extensive security protocol.

    Following and adopting the guidelines in this topic is a necessary step towards achieving total security, however, it is not sufficient on its own. To further improve your security, learn more throughout the rest of the Security section and specifically the following topics:

    • Stay vigilant. The most likely way in which one can lose their assets in a blockchain is by giving away their sensitive details.

    • Encrypt your disks. Encrypting boot devices allows them to protect your data even if an attacker have gained access to the hardware. Doing it for your portable devices is twice as important.

    • Use trusted software. Software that ships via reproducible binary builds, and that you build from source, is the most trustworthy. Proprietary or open-source software that hasn't been audited is a potential risk that must be taken seriously.

    • Never leave portable devices with sensitive data unattended. A split second is enough to steal your device.

    • Verify the signatures on binary packages. This is not too different from the public key cryptography used inside Iroha v2.

    • To prevent unauthorized access, always secure your laptop or personal computer when leaving it unattended. Use strong passwords, lock the screen, and follow best practices for securing your devices.

    • Establish a secure air-gapped location for your keys. First, encrypt the keys, then store them in an offline-only device, ideally with electromagnetic shielding installed. Hardware keys are specifically designed this purpose.

    • Always keep your software updated to their latest version across all devices, including computers and phones. Regular updates help patch vulnerabilities and minimise potential risks associated with outdated software, even before such vulnerabilities are disclosed.

    • Develop a routine for periodically updating passwords and cryptographic keys. This proactive approach significantly contributes to enhancing overall security posture, since it is much harder to hit a moving target.

    Using Browsers

    If an application connected to Iroha 2 features a web UI, your browser can either aid the security or pose a potential threat. It is essential to exercise caution, especially when it comes to the plugins you choose to install.

    Consider the following measures to enhance your browsing security:

    • Avoid using browsers that are known for having bad security models and for leaking their users' data.

      You can look up privacy violations and security issues for any browser. For example, this article on browser privacy discusses a variety of browsers and how secure they are. Note that proprietary browsers (such as Chrome, Safari, Opera, Vivaldi, Edge, and others) are generally tremendously harder to audit due to their code being hidden from public, which means that you cannot be sure how secure they are.

    • Give preference to browsers with solid history of valuing and protecting their users' privacy and security:

      • Librewolf, Icecat, Firedragon, etc. — well established forks of Mozilla Firefox with added security features.
      • Ungoogled chromium — a highly audited open-source version of Google Chrome that is enhanced with additional security measures and has all of the Google-related web services removed.
      • Brave — a highly audited open-source version of Google Chromium that is enhanced with additional security measures; has a built-in VPN and ad blocker functionality.
      • Falkon — an open-source Qt-based web browser (built on QtWebEngine, a wrapper for Google Chromium) with known track record of being secure; has a number of extensions available for download from its KDE store page.
      • Qutebrowser — an open-source Qt-based web browser (built on QtWebEngine, a wrapper for Google Chromium) with known track record of being secure; has a unique keyboard-focused approach with minimalist GUI; considered to be a browser of choice for many security specialists.
    • Avoid enabling JavaScript unless necessary.

    • Use the browser's built-in confinement mechanism for plugins to restrict the access rights that the installed plugins have.

    • Clear cookies before and after important operations. Be mindful not to enable the Keep Me Signed In or Remember me feature. Keep in mind that some websites have this feature enabled by default.

    • Use an ad blocker. These not only block ads but also disable site tracking features. Depending on the browser you use, an ad blocker may not be a built-in feature.

    • Be mindful of lookalike characters (e.g., 0, θ, O, О, and ߀ are six different characters). Paying attention to details like this may save you from a phishing attack.

    • Avoid web UI email clients in favour of desktop clients. Before using it, set up your desktop email client to sign and verify GPG key signatures.

    • Avoid using web-based messaging services. For instance, Discord (built with the infamous electron framework) is susceptible to many of the same attacks as would a Google Chromium window with the web version of Discord open.

    • Update your browser to the latest version whenever possible. Updates often include critical security patches that address vulnerabilities.

    • Be cautious of what browser extensions you install. Only use well-known and trusted extensions from reputable sources. Rogue extensions can compromise your data and privacy.

    • Create separate browser profiles for various tasks. Use one profile for everyday browsing and another for activities involving high security and sensitive data. This way, extensions installed on the profile for everyday browsing cannot access the sensitive data from the secure one.

    • Use a portable version of your browser copied to a USB flash drive. This method ensures that even if a security bug grants one of the installed plugins with access to data between the profiles, your security-related profile remains on a separate and removable device.

    • Periodically clear your browser's cache and cookies to remove potentially sensitive data that may accidentally be stored on your device.

    Recovery Plan

    In the event of an emergency, such as losing a key or facing a security breach, a well-structured and prepared in advance recovery plan is an essential lifeline. Creating a clear set of steps to follow can help mitigate potential damage and promptly reinstate security.

    Organizations should consider the following key aspects when developing their recovery plan:

    • Outline step-by-step procedures to be followed in case of key loss or other security incidents. Ensure that these steps are easily accessible and understandable by the users and/or employees.

    • Establish a communication channel that may be used to promptly report security breaches and potential threats, such as leaked or lost cryptographic keys and password.

    • If you utilize hardware keys (e.g., YubiKey or SoloKeys Solo) as a security measure, consider adopting redundancy strategy. Keep two keys: one for daily use and another stored in a secure location. This precaution ensures access even if the primary key is compromised or lost.

    • When security breaches or leaks are reported, react promptly by replacing or disabling affected keys and passwords. This proactive response minimizes the potential risks and damage.

    • Periodically review and update your recovery plan. This ensures that the plan remains relevant and effective as your security landscape evolves.

    WARNING

    Remember that a recovery plan is not just another document. Rather, it's a lifeline that helps navigate unexpected challenges. By anticipating potential scenarios and establishing a clear roadmap for action, you fortify your operational security and enhance your readiness to respond effectively to any security incident.

    + + + + \ No newline at end of file diff --git a/guide/security/password-security.html b/guide/security/password-security.html new file mode 100644 index 000000000..82f3c57ad --- /dev/null +++ b/guide/security/password-security.html @@ -0,0 +1,36 @@ + + + + + + Password Security | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Password Security

    In the realm of blockchain security, protecting passwords is paramount. To ensure your data and everything it represents remain impervious to unauthorized access, let's delve into the nuances of password security.

    Password Strength

    Likely enough, you may have previously encountered recommendations on how to come up with a strong password. These may entail such advice as minimum password length, addition of special characters, etc. Such recommendations aim to increase the strength of your password that hinges on entropy, i.e. randomness of the password.

    So, what defines a strong password? A strong password is a password with high entropy.

    To calculate the entropy of a password, we may follow the Entropy formula:

    Entropy formula

    — Password length; number of symbols in the password.
    — Character set; size of the pool of unique possible symbols.
    — Number of possible combinations.

    The resulting number is the amount of entropy bits in a password. The higher the number, the harder the password is to crack.

    Knowing the entropy value, the amount of attempts required to brute-force a password with said entropy can be derived by using the following formula:

    There is no universal answer as to how high the entropy of a password should be. For financial organizations, it is advised to keep the entropy of their passwords in the range from 64 to 127 bits (128 bits or more is generally considered to be an overkill). However, keep in mind that GPUs keep constantly evolving, and the time required for password cracking keeps decreasing over time.

    Following the entropy formula, let us compare the following two examples:

    1. A 16-character password with the character set utilizing only lowercase letters of the modern English alphabet (26 characters) yields approximately 43 sextillion () possible combinations.
    $$Entropy=log_2(26^{16})=log_2(43,608,742,899,428,874,059,776)=75.20703...$$
    +
    1. A 16-character password with the character set expanded to 96, including uppercase letters and special symbols, inflates the number of possible combinations to a staggering 52 nonillion (), improving entropy significantly.
    $$Entropy=log_2(96^{16})=log_2(52,040,292,466,647,269,602,037,015,248,896)=105.35940... $$
    +

    As can be seen, even by only expanding the character set from 26 to 96 symbols, the number of possible combinations that a malicious party would need to bruteforce has expanded by times.

    Additionally increasing the length of the password, will grow the number of possible combinations even further, therefore enhancing the entropy—strength—of the password.

    However, instead of wrestling with complexities, we advise using a password manager program—like KeePassXC (for more details, see Adding a Password Manager Program and Configuring KeePassXC)—to generate and securely store your passwords.

    TIP

    Certain websites limit the maximum possible entropy of passwords, i.e., either limit the maximum password length or the set of accepted characters, or both.

    Keep this in mind when using such websites and aim to periodically update your passwords.

    Password Vulnerabilities

    Passwords can fall victim to brute-force attacks, typically executed using powerful GPUs in conjunction with dictionaries or exhaustive iteration through all possibilities. To thwart such attempts, craft a unique password devoid of personal information like birthdays, addresses, phone numbers, or social security numbers. Avoid providing attackers with easily guessable clues.

    So, how hard it is to crack a modern password? It really depends on who you ask.

    With a setup like Kevin Mitnick's cluster setup housing 24 NVIDIA® GeForce RTX 4090's and 6 NVIDIA® GeForce RTX 2080's, all of them running Hashtopolis software, he used to crack passwords that supposed to take a year in mere half a month.

    However, let's now compare it to a single RTX 4090, capable of processing through 300 H/s using NTLM and 200 H/s using bcrypt, as outlined in this tweet.

    As an extension of our previous entropy calculations, let's now examine the following projected cracking times:

    1. There are seconds in a regular non-leap year. Assuming the worst-case scenario with NTLM, at the speed of H/s, it would take a single RTX 4090 approximately years to crack a 16-character password with a character set of 26 letters of the modern English alphabet.

    2. If instead of NTLM we use bcrypt, therefore reducing the iteration speed to H/s, while also expanding the character set to 96, including uppercase letters and special symbols, the time to crack soars to about years, far surpassing the age of the universe.

    So, simply picking higher entropy raised the time it takes to crack a password to unfathomable numbers. Yes, the process may be sped up by using multiple GPUs, however this method pales in comparison with the XKCD approach.

    It is important to note that an extensive character set isn't always necessary to reach high entropy. It can be obtained by using multi-word passwords, or lengthy sentences in particular. The classic XKCD comic illustrates this concept eloquently.

    WARNING

    Avoid writing your password down anywhere. Store your password recovery phrase securely. If the phrase is too long, you may write it down, ensuring that you can read it out and type it out later. Store the physical copy of the phrase in a secure location and/or container.

    + + + + \ No newline at end of file diff --git a/guide/security/public-key-cryptography.html b/guide/security/public-key-cryptography.html new file mode 100644 index 000000000..f6c2e9841 --- /dev/null +++ b/guide/security/public-key-cryptography.html @@ -0,0 +1,34 @@ + + + + + + Public Key Cryptography | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Public Key Cryptography

    Public key cryptography provides the means for secure communication and data protection, enabling activities such as secure online transactions, encrypted email communications, etc.

    Public key cryptography employs a pair of cryptographic keys—a public key and a private key—to create a highly secure method of transmitting information over online networks.

    It's easy to make a public key from a private key, but the opposite is rather difficult, if not impossible. This keeps things safe. You can freely share your public key without risking your private key, which remains secure.

    Encryption and Signatures

    Public key cryptography allows individuals to send encrypted messages and data that can only be deciphered by the intended recipient possessing their corresponding private key. In other words, the public key functions as a lock, and the private key serves as an actual unique key that unlocks the encrypted data.

    This encryption process not only ensures the privacy and confidentiality of sensitive information but also establishes the authenticity of the sender. By combining the sender's private key with the public key, a digital signature is created. This signature serves as a digital stamp of approval, verifying the sender's identity and the validity of the transferred data. Anyone with your public key can verify that the person who initiated the transaction used your private key.

    Keys on the Client Side

    Since every transaction must be signed on behalf of a peer, every operation requires a private key that is kept secret (hence the name). Therefore, the client program must handle both the storage and secure signing of transactions.

    WARNING

    All clients are different, but iroha_client_cli is the least secure in this regard, as it stores a peer's private key in the multihash format saved to a plain text file that could be overridden with an environment variable.

    This is currently a reference implementation that will not be a part of the production release.

    One needs to register a user on behalf of another already registered user (just like you need to already have a pair of scissors to cut off the tag from a new one). Suppose that we want to register a user on behalf of mad_hatter@wonderland.

    This entails generating a new private key, and sending its public key to the network so that said network can verify that it's indeed the trustworthy mad_hatter@wonderland, and not some impostor (e.g. mad_hatter@wünderbar). In this case, the client application must prompt you, the user, to provide a key pair and verify the authenticity of the transactions: belonging to mad_hatter@wonderland and having a signature derived from the appropriate public key.

    For public key cryptography to work effectively, avoid re-using keys when you need to specify a new key. While there's nothing stopping you from doing that, the public keys are public, which means that if an attacker sees the same public key being used, they will know that the private keys are also identical.

    Even though private keys operate on slightly different principles than passwords, the advice—to make them as random as possible, never store them unencrypted and never share them with anyone under any circumstances—applies.

    + + + + \ No newline at end of file diff --git a/guide/security/security-principles.html b/guide/security/security-principles.html new file mode 100644 index 000000000..7a6ff7363 --- /dev/null +++ b/guide/security/security-principles.html @@ -0,0 +1,34 @@ + + + + + + Security Principles | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Security Principles

    Organisations and individual users need to work together to ensure secure interactions with Iroha installations. This topic explains the basic principles behind this cooperation.

    General Security Principles

    1. Use a Virtual Private Network (VPN):

      • Whenever accessing sensitive data or resources, especially over public networks, use a VPN to establish a secure connection that safeguards your information.
    2. Use a firewall for network protection:

      • Strengthen home and/or office networks by setting up a firewall that helps to counter unauthorized access and protect the connected devices from viruses and malware.
    3. Secure physical and digital information:

      • Safeguard physical documents containing sensitive information in a secure location, and ensure digital documents are encrypted and stored in password-protected folders.
    4. Keep Regular Data Backups:

      • Always have copies of your important information saved somewhere safe. This way, if you lose your data or something goes wrong, you can quickly get everything back on track. Keep these backups in a different secure place from where you usually keep your data.

    Security Principles for Individual Users

    1. Adopt robust authentication rules:

      • Utilise strong and unique passwords for all accounts.

      • Never reuse passwords.

      • Set up 2FA whenever possible. 2FA improves the overall security by not only requiring a password, but also an additional factor such as an OTP, fingerprint, or a third-party app-based authentication (e.g., Google Authenticator).

      • Avoid using SMS authentication as the second factor. There is no guarantee that malicious software is not monitoring all of your SMS messages. For example, Android applications cannot be limited to only accessing the messages intended specifically for them.

    2. Exercise caution in digital communication:

      • Set up an email client to sign and verify signatures of all the received emails. While it is possible to impersonate the sender's address and even pose as a bank, it is not possible to fake a signature.

      • Disable both HTML messages and loading of external resources from unknown or unverified addresses.

      • Learn about common phishing techniques to recognise and avoid suspicious emails, links, and requests for personal information.

      • Set up an email client to sign and verify signatures of all the received emails. While it is possible to impersonate the sender's address and even pose as a bank, it is not possible to fake a signature.

    3. Safeguard personal information:

      • When communicating with unfamiliar individuals, especially on the phone or online, be careful about sharing private information.

      • Consider independently researching the individuals or organizations you are communicating with to confirm the legitimacy of their identity.

      • Be mindful of the personal information you share on social media platforms, as malicious parties can exploit this information.

    Security Principles for Organisations

    1. Establish clear security policies and procedures:

      • Develop well-defined security policies and protocols for all employees dealing with sensitive data. Thoroughly train employees to adhere to these guidelines, mitigating the risk of negligent actions.

      • Ensure that security policies are accessible to all employees and are regularly reviewed and updated to reflect changing security landscapes.

      • Provide the security policies with examples and scenarios to make them more relatable and actionable for employees.

    2. Cultivate employee awareness:

      • Educate employees about data and operational security measures. Heightened awareness and comprehensive training are pivotal in fortifying organizational security.

      • Encourage employees to report any suspicious activities or security concerns promptly.

    3. Protect physical infrastructure:

      • Restrict physical entry to servers and infrastructure. Set up access controls that only allow authorised personnel to enter restricted areas.

      • Ensure that access control measures are regularly reviewed and updated to align with evolving security needs.

      • Consider implementing biometric access controls for sensitive areas to enhance physical security.

    4. Deploy security monitoring:

      • Enforce a comprehensive security monitoring system that scrutinizes activities and identifies potential security breaches.

      • Implement automated alerts to promptly notify security personnel of any unusual or unauthorized activities.

      • Consider using machine learning algorithms to enhance the system's ability to detect anomalies and potential threats.

      • Employ staff or designate personnel to oversee database security, identify, track and address software vulnerabilities, and conduct regular checks on critical machines for the presence of unauthorized software not included in the approved list.

    5. Conduct recurring security audits:

      • Perform routine security audits to evaluate vulnerabilities and confirm that established security measures align with the commonly-accepted standards and regulations.

      • Consider hiring external security experts for periodic assessments to gain an impartial evaluation of your organization's security condition.

    6. Implement an access control system:

      • Set up a role-based access control system to ensure that employees only have access to the resources and information necessary for their roles.
    7. Embrace Continuous Improvement:

      • Recognize that security is a continuous process. Maintain ongoing assessment of security measures and proactively enhance them to address emerging threats and challenges.

      • Consider establishing a feedback loop that encourages employees to contribute security improvement suggestions, fostering the culture of continuous enhancement.

    + + + + \ No newline at end of file diff --git a/guide/security/storing-cryptographic-keys.html b/guide/security/storing-cryptographic-keys.html new file mode 100644 index 000000000..8c4b6f288 --- /dev/null +++ b/guide/security/storing-cryptographic-keys.html @@ -0,0 +1,34 @@ + + + + + + Storing Cryptographic Keys | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Storing Cryptographic Keys

    Your sensitive data only remains private if you adopt OPSEC practices to protect the cryptographic keys. Social engineering threats, where someone posing as a figure with authority tries to manipulate you into giving them your private cryptographic key, are real. Always be cautious and avoid sharing your private key, treating it as you would your apartment keys—reserved for trusted individuals only.

    For more information on OPSEC and its best practices, see Operational Security.

    Storing Cryptographic Keys Digitally

    When it comes to protecting cryptographic keys digitally, mainly only two approaches—SSH and GPG—are available. These methods provide layers of security to prevent unauthorized access to your cryptographic keys.

    Many of Iroha 2's architectural decisions have been influenced by the principles of the Secure Shell (SSH) protocol, which is why this section primarily focuses on the SSH approach, offering instructions on how to effectively implement the protocol for storing your cryptographic keys within the Iroha 2 ecosystem.

    Using SSH and SSH Agent

    Secure Shell Protocol (SSH) is a cryptographic network protocol that serves as a virtual gateway, enabling secure access to remote machines via potentially not-so-secure networks by using SSH keys—access credentials. It provides an efficient way to remotely interact with systems without the necessity of physical presence. In this context, SSH offers two primary authentication mechanisms: the conventional password-based approach and the more secure public-private key pair method.

    For more information on SSH, see the related SSH Academy topic.

    To streamline the login process and bypass the need for repetitive input, it is possible to pair the SSH keys with the SSH Agent (ssh-agent)—the assistant program that remembers your SSH keys and/or password for the duration of a session. This setup permits the SSH gateway to effortlessly access the keys whenever it connects to other machines.

    The workflow here is as follows: you have your public key stored on a remote system and keep your private key secure. Whenever you want to access a remote system, the ssh-agent steps in to communicate your public key to the accessed system. The remote system then sends back a challenge that only your private key can properly respond to. Your ssh-agent handles this challenge by using your private key and sends the correct response back to the remote system. If the response matches what the system expected, you're granted access.

    The beauty of the ssh-agent is that it holds onto your private key during your session, so there is no need to keep entering your password or private key passphrase every time you connect to a remote system.

    For more information on the ssh-agent, see the related SSH Academy topic.

    Note

    For a detailed overview of the SSH protocol and the ssh-agent tool, see the following SSH Academy topics:

    Adding a Password Manager Program

    It is recommended to enhance the security of your SSH keys by protecting them with a password, which acts as an additional obstacle in the way of malicious parties aiming to obtain your sensitive information.

    A variety of password managers can be used to store user passwords and SSH keys temporarily. For the sake of clarity, KeePass is used as an example password manager, specifically, the KeePassXC port running on Linux-based operating systems.

    For instructions on how to set up KeePassXC see the Configuring KeePassXC section below.

    KeePassXC:  screen UI

    KeePassXC offers enhanced security, flexibility, and control. It not only stores passwords but also the SSH keys. When used for key storage, this password manager provides the ssh-agent with the stored keys, which are then promptly removed from its memory once the KeePassXC window is closed.

    TIP

    Theoretically, any of the KeePass ports listed on the official website can be utilized for the key storage purposes. We recommend any of the following: KeePassX or KeePassXC.

    Configuring KeePassXC

    To configure KeePassXC, perform the following steps:

    1. Launch KeePassXC, then go to Tools > Settings, or select the Gear button from the top UI panel.

    2. In the Application Settings tab that appears, select SSH Agent from the left menu, and then select the Enable SSH Agent integration checkbox.

      Show reference screenshot

      KeePassXC  tab: Enabling SSH Agent

    3. Create a new KeePassXC Database. For instructions, see KeePassXC User Guide > Creating Your First Database.

    4. For every key that you would like to store in the KeePassXC Database you created, perform the following steps:

      • Add a new entry in the database. For instructions, see KeePassXC User Guide > Creating Your First Database.

      • When adding a new entry, attach the file containing the key by doing the following: select Advanced from the left menu, then select Add in the Attachments section, choose the required file in the Select files window that appears.

      • When adding a new entry, select SSH Agent from the left menu, then select the key file you added from the Attachment menu in the Private key section; then select the following checkboxes:

        • Add key to agent when database is opened/unlocked

        • Remove key from agent when database is closed/locked

        • Require user confirmation when this key is used

      • If necessary, make other changes to the entry.

      • When ready, select OK to save the entry.

      Show reference screenshots

      KeePassXC  tab: Adding a private key attachment

      KeePassXC  tab: Adding a private key attachment

    Expected Results
    • Cryptographic and shh keys are stored as entries in a KeePassXC Database that can be accessed while the KeePassXC window is open.

    • Stored cryptographic and ssh keys can be used whenever they are required for authorization.

    • Stored cryptographic and ssh keys are removed from the ssh-agent once the KeePassXC window is closed.

    Note

    Without enabling the Require user confirmation when this key is used option, the ssh-agent may not monitor the process that provided it with a key. In the event that the password manager process is terminated by malware or a system service through a SIGKILL signal, the key is likely to remain in the ssh-agent, as Unix system programs cannot intercept SIGKILL.

    Storing Cryptographic Keys Physically

    For those who seek the highest level of offline security, the option of storing cryptographic keys physically ensures that the keys remain completely disconnected from digital networks, thus minimizing the risk of unauthorized access. Acknowledging the physical option underscores our commitment to catering to diverse security needs.

    Using a Hardware Key

    Our team considers hardware keys to be one of the best safety measures. A hardware key—a compact device that connects via a USB port and has a size of a typical flash drive—only processes security-related events when it is connected to a machine. This allows you to easily disconnect the device in case of a security breach, or simply reconnect it to a different machine whenever it is required.

    However, since there are many brands of hardware keys—each with their unique APIs—it is important to research the market to find the key that best suits your needs.

    So far, our team has enternally tested the YubiKey 5C hardware key that proved to have many positive features, including versatile API functionality.

    However, there's a potential drawback to consider. Implementing the HMAC challenge-response authentication and storing a corresponding private key for this response could create a vulnerability. This setup might inadvertently enable attackers to make educated guesses about the information stored within the YubiKey 5C's memory, thereby compromising the overall security.

    Luckily, this vulnerability can be mitigated by adopting an alternative approach to utilizing the YubiKey 5C. The idea is to use YubiKey 5C to securely access a KeePassXC database storing your cryptographic and SSH keys. This method can even be considered beneficial, since it surpasses the security of most passwords and makes it necessary for the malicious party to be in possession of your hardware key in case the KeePassXC database is leaked.

    INFO

    To read more about the method above, see the answer by one of the KeePassXC developers—Janek Bevendorff—to the following StackExchange question:

    Is it reasonable to use KeePassXC with YubiKey?

    Using a Mnemonic Phrase

    Alternatively, you can memorize a private key as a series of words, known as a mnemonic phrase. This method, used in many wallets, requires remembering around 25 specific words. You can generate these words using the XKCD password generator.

    + + + + \ No newline at end of file diff --git a/guide/support.html b/guide/support.html new file mode 100644 index 000000000..1972c0de6 --- /dev/null +++ b/guide/support.html @@ -0,0 +1,34 @@ + + + + + + Receive support | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Receive support

    From time to time, you may have questions about Iroha that you would like to discuss in detail with others. There are three ways to quickly get in touch with our community: Telegram, Discord, and GitHub.

    A large part of the community currently uses Telegram for communication. The Hyperledger part of the team prefers Discord, with two dedicated channels: iroha and iroha-2-contributors. The Discord and Telegram channels are synchronized, so users of both media see your messages.

    Finally, you can create a GitHub issue, whether it's a request to update documentation, a suggestion for the core team, or a bug you have found.

    + + + + \ No newline at end of file diff --git a/guide/troubleshooting/configuration-issues.html b/guide/troubleshooting/configuration-issues.html new file mode 100644 index 000000000..72aeb648a --- /dev/null +++ b/guide/troubleshooting/configuration-issues.html @@ -0,0 +1,34 @@ + + + + + + Troubleshooting Configuration Issues | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Troubleshooting Configuration Issues

    This section offers troubleshooting tips for issues with Iroha 2 configuration. Make sure you checked the keys first, as it is the most common source of issues in Iroha.

    If the issue you are experiencing is not described here, contact us via Telegram.

    Outdated genesis on a Docker-compose setup

    When you are using the Docker-compose version of Iroha, you might encounter the issue of one of the peer containers failing with the Failed to deserialize raw genesis block error. This happens if there is a mismatch between Iroha versions, meaning an Iroha peer cannot be initialized with the given genesis file.

    If one of the peers is failing and it's been a while since you pulled the Iroha code for the first time, it's safe to assume the outdated genesis file is the cause. Here is how you can make sure Iroha is working incorrectly for exactly this reason:

    1. Use docker ps to check the current containers. Depending on the version, you will see either hyperledger/iroha2:dev or hyperledger/iroha2:lts containers. Check the number of Iroha peer containers in the docker ps output. By default, there are 4 peers configured in docker-compose.yml for Iroha, although you may have changed that value. You will see that the first container that should have been running Iroha just exited with an error, while three other containers remain active.

    2. Check the logs and look for the Failed to deserialize raw genesis block error. If you started your Iroha in daemon mode with docker compose up -d, use docker compose logs command.

    The way to troubleshoot such an issue depends on the use of Iroha.

    If this is a basic demo and you don't need the peer data to be restored, you can simply reset the genesis file to its latest state. To do this, use the git checkout configs/peer/genesis.json command.

    If you need to restore the Iroha instance data, do the following:

    1. Connect the second Iroha peer that will copy the data from the first (failed) peer.
    2. Wait for the new peer to synchronize the data with the first peer.
    3. Leave the new peer active.
    4. Update the genesis file of the first peer.

    INFO

    The features needed to monitor the copying progress between peers and a migration tool to update the genesis file are to be implemented in future releases.

    Multihash Format of Private and Public Keys

    If you look at the client configuration, you will notice that the keys there are given in multi-hash format.

    If you've never worked with multi-hash before, it is natural to assume that the right-hand-side is not a hexadecimal representation of the key bytes (two symbols per byte), but rather the bytes encoded as ASCII (or UTF-8), and call from_hex on the string literal in both the public_key and private_key instantiation.

    It is also natural to assume that calling PrivateKey::try_from_str on the string literal would yield only the correct key. So if you get the number of bits in the key wrong, e.g. 32 bytes vs 64, that it would raise an error message.

    Both of these assumptions are wrong. Unfortunately, the error messages don't help in de-bugging this particular kind of failure.

    How to fix: use hex_literal. This will also turn an ugly string of characters into a nice small table of obviously hexadecimal numbers.

    WARNING

    Even the try_from_str implementation cannot verify if a given string is a valid PrivateKey and warn you if it isn't.

    It will catch some obvious errors, e.g. if the string contains an invalid symbol. However, since we aim to support many key formats, it can't do much else. It cannot tell if the key is the correct private key for the given account either, unless you submit an instruction.

    These sorts of subtle mistakes can be avoided, for example, by deserialising directly from string literals, or by generating a fresh key-pair in places where it makes sense.

    + + + + \ No newline at end of file diff --git a/guide/troubleshooting/deployment-issues.html b/guide/troubleshooting/deployment-issues.html new file mode 100644 index 000000000..a4a0b75c2 --- /dev/null +++ b/guide/troubleshooting/deployment-issues.html @@ -0,0 +1,34 @@ + + + + + + Troubleshooting Deployment Issues | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Troubleshooting Deployment Issues

    This section offers troubleshooting tips for issues with Iroha 2 deployment. If the issue you are experiencing is not described here, contact us via Telegram.

    Docker

    TBD

    Kubernetes

    TBD

    + + + + \ No newline at end of file diff --git a/guide/troubleshooting/installation-issues.html b/guide/troubleshooting/installation-issues.html new file mode 100644 index 000000000..450ba05d9 --- /dev/null +++ b/guide/troubleshooting/installation-issues.html @@ -0,0 +1,50 @@ + + + + + + Troubleshooting Installation Issues | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Troubleshooting Installation Issues

    This section offers troubleshooting tips for issues with Iroha 2 installation. If the issue you are experiencing is not described here, contact us via Telegram.

    Troubleshooting Rust Toolchain

    Sometimes, things don’t go as planned. Especially if you had rust on your system a while ago, but didn’t upgrade. A similar problem can occur in Python: XKCD has a famous example of what that might look like:

    Untitled

    Check Rust version

    In the interest of preserving both your and our sanity, make sure that you have the right version of cargo paired with the right version of rustc (1.57 and 1.57) respectively. To show the versions, do

    bash
    $ cargo -V
    +$ cargo 1.60.0 (d1fd9fe 2022-03-01)
    $ cargo -V
    +$ cargo 1.60.0 (d1fd9fe 2022-03-01)

    and then

    bash
    $ rustc --version
    +$ rustc 1.60.0 (7737e0b5c 2022-04-04)
    $ rustc --version
    +$ rustc 1.60.0 (7737e0b5c 2022-04-04)

    If you have higher versions, you're fine. If you have lower versions, you can run the following command to update it:

    bash
    $ rustup toolchain update stable
    $ rustup toolchain update stable

    Check installation location

    If you get lower version numbers and you updated the toolchain and it didn’t work… let’s just say it’s a common problem, but it doesn’t have a common solution.

    Firstly, you should establish where the version that you want to use is installed:

    bash
    $ rustup which rustc
    +$ rustup which cargo
    $ rustup which rustc
    +$ rustup which cargo

    User installations of the toolchains are usually in ~/.rustup/toolchains/stable-*/bin/. If that is the case, you should be able to run

    bash
    $ rustup toolchain update stable
    $ rustup toolchain update stable

    and that should fix your problems.

    Check the default Rust version

    Another option is that you have the up-to-date stable toolchain, but it is not set as the default. Run:

    bash
    $ rustup default stable
    $ rustup default stable

    This can happen if you installed a nightly version, or set a specific Rust version, but forgot to un-set it.

    Check if there are other Rust versions

    Continuing down the troubleshooting rabbit-hole, we could have shell aliases:

    bash
    $ type rustc
    +$ type cargo
    $ type rustc
    +$ type cargo

    If these point to locations other than the one you saw when running rustup which *, then you have a problem. Note that it’s not enough to just

    bash
    $ alias rustc "~/.rustup/toolchains/stable-*/bin/rustc"
    +$ alias cargo "~/.rustup/toolchains/stable-*/bin/cargo"
    $ alias rustc "~/.rustup/toolchains/stable-*/bin/rustc"
    +$ alias cargo "~/.rustup/toolchains/stable-*/bin/cargo"

    because there is an internal logic that could break, regardless of how you re-arrange your shell aliases.

    The simplest solution would be to remove the versions that you don’t use.

    It’s easier said than done, however, since it entails tracking all the versions of rustup installed and available to you. Usually, there are only two: the system package manager version and the one that got installed into the standard location in your home folder when you ran the command in the beginning of this tutorial. For the former, consult your (Linux) distribution’s manual, (apt remove rust). For the latter, run:

    bash
    $ rustup toolchain list
    $ rustup toolchain list

    And then, for every <toolchain> (without the angle brackets of course):

    bash
    $ rustup remove <toolchain>
    $ rustup remove <toolchain>

    After that, make sure that

    bash
    $ cargo --help
    $ cargo --help

    results in a command-not-found error, i.e. that you have no active Rust toolchain installed. Then, run:

    bash
    $ rustup toolchain install stable
    $ rustup toolchain install stable

    Troubleshooting Python toolchain

    When you install the Python Wheel package using pip on the "client setup" step, you may encounter an error like: "iroha_python-*.whl is not a supported wheel on this platform".

    This error means that pip is outdated, so you need to update it. First of all, it is recommended to check your OS for updates and perform a system upgrade.

    If this doesn't work, you can try updating pip for your user directory.

    python -m pip install --upgrade pip

    Make sure that pip that is installed in your home directory. To do this, run whereis pip and check if /home/username/.local/bin/pip is among the paths. If not, update your shell's PATH variable.

    If the issue persists, please contact us and report the outputs.

    python --version
    +python3 --version
    +pip --version
    +pip3 --version
    python --version
    +python3 --version
    +pip --version
    +pip3 --version
    + + + + \ No newline at end of file diff --git a/guide/troubleshooting/integration-issues.html b/guide/troubleshooting/integration-issues.html new file mode 100644 index 000000000..8fabe8ad2 --- /dev/null +++ b/guide/troubleshooting/integration-issues.html @@ -0,0 +1,34 @@ + + + + + + Troubleshooting Integration Issues | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Troubleshooting Integration Issues

    This section offers troubleshooting tips for issues with Iroha 2 integration. If the issue you are experiencing is not described here, contact us via Telegram.

    TBD

    + + + + \ No newline at end of file diff --git a/guide/troubleshooting/overview.html b/guide/troubleshooting/overview.html new file mode 100644 index 000000000..7c7accf17 --- /dev/null +++ b/guide/troubleshooting/overview.html @@ -0,0 +1,34 @@ + + + + + + Troubleshooting | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Troubleshooting

    This section is intended to help if you encounter issues while working with Iroha. If something goes wrong, please check the keys first. If that doesn't help, check the troubleshooting instructions for each stage:

    If the issue you are experiencing is not described here, contact us via Telegram.

    Check the keys

    Most issues arise as a result of unmatched keys. This is why we recommend to follow this rule: If something goes wrong, check the keys first.

    Here's a quick explanation: It is not possible to differentiate the error messages that arise when peers' keys do not match the keys in the array of trusted peers, because it would expose the peers' public key. As such, if you have Helm charts or K8s deployed with keys defined via the environment variables, you should check for Key definitions.

    If in doubt, generate a new pair of keys.

    + + + + \ No newline at end of file diff --git a/hashmap.json b/hashmap.json new file mode 100644 index 000000000..14f360b64 --- /dev/null +++ b/hashmap.json @@ -0,0 +1 @@ +{"guide_blockchain_events.md":"eb70e642","guide_blockchain_domains.md":"97f6e325","guide_blockchain_consensus.md":"6e226678","guide_blockchain_accounts.md":"62495945","documenting_snippets.md":"12d116dc","guide_blockchain_assets.md":"ae63cc7e","guide_advanced_metrics.md":"66cfb76b","guide_blockchain_expressions.md":"1f016bcd","guide_blockchain_transactions.md":"f5c57974","guide_blockchain_permissions.md":"81c20431","guide_configure_client-configuration.md":"913412a8","guide_blockchain_wasm.md":"e977d18a","guide_get-started_index.md":"0460923f","guide_configure_metadata-and-store-assets.md":"06d6b458","guide_blockchain_instructions.md":"fb8ab4c5","guide_get-started_install.md":"471062b8","guide_configure_keys-for-network-deployment.md":"02deb3dc","guide_blockchain_world.md":"e5785fd9","guide_blockchain_queries.md":"b6c7f793","guide_blockchain_how-iroha-works.md":"0b0aa977","guide_configure_overview.md":"abcd2531","guide_configure_modes.md":"854101fa","guide_blockchain_triggers.md":"f630788f","guide_blockchain_filters.md":"3bc7d228","guide_configure_configuration-types.md":"f5f5b88c","guide_advanced_running-iroha-on-bare-metal.md":"552acb26","guide_configure_sample-configuration.md":"94949a44","guide_blockchain_data-model.md":"563e4b72","guide_advanced_hot-reload.md":"d25b5901","guide_blockchain_metadata.md":"318a1743","guide_configure_peer-configuration.md":"f1ef1883","guide_get-started_build.md":"a6d60f6a","guide_configure_peer-management.md":"e212b1c4","guide_configure_genesis.md":"584065bb","guide_get-started_bash.md":"b307a61c","reference_torii-endpoints.md":"b5467706","guide_get-started_javascript.md":"274c1036","guide_reports_csd-rtgs.md":"9808d30a","guide_introduction.md":"5d831587","guide_iroha-2.md":"9c7ff7ce","guide_security_index.md":"0ab406c3","guide_security_generating-cryptographic-keys.md":"3297fbf3","guide_security_operational-security.md":"e0ab91bf","guide_security_security-principles.md":"7b1fddfa","guide_support.md":"b4280b53","guide_security_storing-cryptographic-keys.md":"213ac358","guide_security_password-security.md":"99153c0f","guide_troubleshooting_configuration-issues.md":"5529b4ac","guide_troubleshooting_integration-issues.md":"4e503ab9","guide_security_public-key-cryptography.md":"04b4a080","guide_troubleshooting_installation-issues.md":"687c65b4","guide_troubleshooting_overview.md":"639dbf2e","index.md":"c38d1cfb","reference_compatibility-matrix.md":"592c45f9","reference_instructions.md":"6acb88a4","reference_glossary.md":"b751955f","reference_ffi.md":"4ec628c3","reference_naming.md":"ce9dbc7c","reference_permissions.md":"73888c63","reference_specification.md":"2a95f9a6","reference_data-model-schema.md":"ea74a419","reference_queries.md":"e4f73f68","guide_troubleshooting_deployment-issues.md":"30145f6f","guide_get-started_quick-start.md":"90fe4bdf","guide_get-started_rust.md":"bea2cc81","guide_get-started_python.md":"694940f1","guide_get-started_tutorials.md":"dee1ad88","guide_get-started_kotlin-java.md":"fd61b4c2"} diff --git a/icon-192.png b/icon-192.png new file mode 100644 index 000000000..914b582c7 Binary files /dev/null and b/icon-192.png differ diff --git a/icon-512.png b/icon-512.png new file mode 100644 index 000000000..9763292b7 Binary files /dev/null and b/icon-512.png differ diff --git a/icon.svg b/icon.svg new file mode 100644 index 000000000..56115da8a --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..c51a3e908 --- /dev/null +++ b/index.html @@ -0,0 +1,34 @@ + + + + + + Hyperledger Iroha 2 Tutorial | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/manifest.webmanifest b/manifest.webmanifest new file mode 100644 index 000000000..985a7aa30 --- /dev/null +++ b/manifest.webmanifest @@ -0,0 +1,9 @@ +{ + "name": "Iroha 2 Documentation", + "start_url": "https://hyperledger.github.io/iroha-2-docs/", + "display": "standalone", + "icons": [ + { "src": "icon-192.png", "type": "image/png", "sizes": "192x192" }, + { "src": "icon-512.png", "type": "image/png", "sizes": "512x512" } + ] +} diff --git a/reference/compatibility-matrix.html b/reference/compatibility-matrix.html new file mode 100644 index 000000000..36c717761 --- /dev/null +++ b/reference/compatibility-matrix.html @@ -0,0 +1,34 @@ + + + + + + Compatibility Matrix | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Compatibility Matrix

    In our continuous efforts to provide clear documentation and to ensure seamless compatibility across multiple SDKs, we present the SDK Compatibility Matrix. This matrix provides an instantaneous overview of how different stories, sourced from TestOps API, fare across varying SDKs.

    The matrix consists of:

    • Stories: Represented in the first column of the matrix, these are directly fetched from the TestOps API.
    • SDKs: Each subsequent column represents an SDK, such as "Java/Kotlin", "JavaScript", "Swift", etc.
    • Status Symbols: The status of each story for an SDK is denoted with:
      • indicating the story passed.
      • indicating the story failed to pass.
      • indicating the data is missing.
    Loading data...

    INFO

    The data for this matrix is retrieved dynamically from our backend service, balancing the latest information with a swift response for documentation readers.

    + + + + \ No newline at end of file diff --git a/reference/data-model-schema.html b/reference/data-model-schema.html new file mode 100644 index 000000000..f52dd09d7 --- /dev/null +++ b/reference/data-model-schema.html @@ -0,0 +1,34 @@ + + + + + + Data Model Schema | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Data Model Schema

    TODO: explain what this page contains

    Account

    Type: Struct

    Declarations:

    Field nameField value
    idAccountId
    assetsSortedMap<AssetId, Asset>
    signatoriesSortedVec<PublicKey>
    signature_check_conditionSignatureCheckCondition
    metadataMetadata
    rolesSortedVec<RoleId>

    AccountEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AssetAssetEvent0
    CreatedAccount1
    DeletedAccountId2
    AuthenticationAddedAccountId3
    AuthenticationRemovedAccountId4
    PermissionAddedAccountPermissionChanged5
    PermissionRemovedAccountPermissionChanged6
    RoleRevokedAccountRoleChanged7
    RoleGrantedAccountRoleChanged8
    MetadataInsertedMetadataChanged<AccountId>9
    MetadataRemovedMetadataChanged<AccountId>10

    AccountEventFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ByCreated0
    ByDeleted1
    ByAuthenticationAdded2
    ByAuthenticationRemoved3
    ByPermissionAdded4
    ByPermissionRemoved5
    ByRoleRevoked6
    ByRoleGranted7
    ByMetadataInserted8
    ByMetadataRemoved9
    ByAssetFilterOpt<AssetFilter>10

    AccountFilter

    Type: Struct

    Declarations:

    Field nameField value
    origin_filterFilterOpt<OriginFilter<AccountEvent>>
    event_filterFilterOpt<AccountEventFilter>

    AccountId

    Type: Struct

    Declarations:

    Field nameField value
    nameName
    domain_idDomainId

    AccountPermissionChanged

    Type: Struct

    Declarations:

    Field nameField value
    account_idAccountId
    permission_idName

    AccountRoleChanged

    Type: Struct

    Declarations:

    Field nameField value
    account_idAccountId
    role_idRoleId

    Action<TriggeringFilterBox, Executable>

    Type: Struct

    Declarations:

    Field nameField value
    executableExecutable
    repeatsRepeats
    authorityAccountId
    filterTriggeringFilterBox
    metadataMetadata

    Action<TriggeringFilterBox, OptimizedExecutable>

    Type: Struct

    Declarations:

    Field nameField value
    executableOptimizedExecutable
    repeatsRepeats
    authorityAccountId
    filterTriggeringFilterBox
    metadataMetadata

    Add

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<NumericValue>
    rightEvaluatesTo<NumericValue>

    Algorithm

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Ed255190
    Secp256k11
    BlsNormal2
    BlsSmall3

    And

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<bool>
    rightEvaluatesTo<bool>

    Array<Interval<u16>, 8>

    Type: Array

    Length: 8

    Value: Interval<u16>

    Array<Interval<u8>, 4>

    Type: Array

    Length: 4

    Value: Interval<u8>

    Array<u16, 8>

    Type: Array

    Length: 8

    Value: u16

    Array<u8, 32>

    Type: Array

    Length: 32

    Value: u8

    Array<u8, 4>

    Type: Array

    Length: 4

    Value: u8

    Asset

    Type: Struct

    Declarations:

    Field nameField value
    idAssetId
    valueAssetValue

    AssetChanged

    Type: Struct

    Declarations:

    Field nameField value
    asset_idAssetId
    amountAssetValue

    AssetDefinition

    Type: Struct

    Declarations:

    Field nameField value
    idAssetDefinitionId
    value_typeAssetValueType
    mintableMintable
    logoOption<IpfsPath>
    metadataMetadata
    owned_byAccountId

    AssetDefinitionEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    CreatedAssetDefinition0
    MintabilityChangedAssetDefinitionId1
    OwnerChangedAssetDefinitionOwnerChanged2
    DeletedAssetDefinitionId3
    MetadataInsertedMetadataChanged<AssetDefinitionId>4
    MetadataRemovedMetadataChanged<AssetDefinitionId>5
    TotalQuantityChangedAssetDefinitionTotalQuantityChanged6

    AssetDefinitionEventFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ByCreated0
    ByMintabilityChanged1
    ByOwnerChanged2
    ByDeleted3
    ByMetadataInserted4
    ByMetadataRemoved5
    ByTotalQuantityChanged6

    AssetDefinitionFilter

    Type: Struct

    Declarations:

    Field nameField value
    origin_filterFilterOpt<OriginFilter<AssetDefinitionEvent>>
    event_filterFilterOpt<AssetDefinitionEventFilter>

    AssetDefinitionId

    Type: Struct

    Declarations:

    Field nameField value
    nameName
    domain_idDomainId

    AssetDefinitionOwnerChanged

    Type: Struct

    Declarations:

    Field nameField value
    asset_definition_idAssetDefinitionId
    new_ownerAccountId

    AssetDefinitionTotalQuantityChanged

    Type: Struct

    Declarations:

    Field nameField value
    asset_definition_idAssetDefinitionId
    total_amountNumericValue

    AssetEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    CreatedAsset0
    DeletedAssetId1
    AddedAssetChanged2
    RemovedAssetChanged3
    MetadataInsertedMetadataChanged<AssetId>4
    MetadataRemovedMetadataChanged<AssetId>5

    AssetEventFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ByCreated0
    ByDeleted1
    ByAdded2
    ByRemoved3
    ByMetadataInserted4
    ByMetadataRemoved5

    AssetFilter

    Type: Struct

    Declarations:

    Field nameField value
    origin_filterFilterOpt<OriginFilter<AssetEvent>>
    event_filterFilterOpt<AssetEventFilter>

    AssetId

    Type: Struct

    Declarations:

    Field nameField value
    definition_idAssetDefinitionId
    account_idAccountId

    AssetValue

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Quantityu320
    BigQuantityu1281
    FixedFixed2
    StoreMetadata3

    AssetValueType

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Quantity0
    BigQuantity1
    Fixed2
    Store3

    AtIndex

    Type: Struct

    Declarations:

    Field nameField value
    indexu32
    predicateValuePredicate

    BatchedResponse<Value>

    Type: Struct

    Declarations:

    Field nameField value
    batchValue
    cursorForwardCursor

    BatchedResponse<Vec<VersionedSignedTransaction>>

    Type: Struct

    Declarations:

    Field nameField value
    batchVec<VersionedSignedTransaction>
    cursorForwardCursor

    BinaryOpIncompatibleNumericValueTypesError

    Type: Struct

    Declarations:

    Field nameField value
    leftNumericValue
    rightNumericValue

    BlockHeader

    Type: Struct

    Declarations:

    Field nameField value
    heightu64
    timestamp_msu64
    previous_block_hashOption<HashOf<VersionedSignedBlock>>
    transactions_hashOption<HashOf<MerkleTree<VersionedSignedTransaction>>>
    commit_topologyVec<PeerId>
    view_change_indexu64
    consensus_estimation_msu64

    BlockMessage

    Type: Alias

    To: VersionedSignedBlock

    BlockPayload

    Type: Struct

    Declarations:

    Field nameField value
    headerBlockHeader
    transactionsVec<TransactionValue>
    event_recommendationsVec<Event>

    BlockRejectionReason

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ConsensusBlockRejection0

    BlockSubscriptionRequest

    Type: Alias

    To: NonZero<u64>

    BurnBox

    Type: Struct

    Declarations:

    Field nameField value
    objectEvaluatesTo<Value>
    destination_idEvaluatesTo<IdBox>

    Conditional

    Type: Struct

    Declarations:

    Field nameField value
    conditionEvaluatesTo<bool>
    thenInstructionBox
    otherwiseOption<InstructionBox>

    ConfigurationEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ChangedParameterId0
    CreatedParameterId1
    DeletedParameterId2

    Container

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AnyValuePredicate0
    AllValuePredicate1
    AtIndexAtIndex2
    ValueOfKeyValueOfKey3
    HasKeyName4

    Contains

    Type: Struct

    Declarations:

    Field nameField value
    collectionEvaluatesTo<Vec<Value>>
    elementEvaluatesTo<Value>

    ContainsAll

    Type: Struct

    Declarations:

    Field nameField value
    collectionEvaluatesTo<Vec<Value>>
    elementsEvaluatesTo<Vec<Value>>

    ContainsAny

    Type: Struct

    Declarations:

    Field nameField value
    collectionEvaluatesTo<Vec<Value>>
    elementsEvaluatesTo<Vec<Value>>

    ContextValue

    Type: Struct

    Declarations:

    Field nameField value
    value_nameName

    DataEntityFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ByPeerFilterOpt<PeerFilter>0
    ByDomainFilterOpt<DomainFilter>1
    ByAccountFilterOpt<AccountFilter>2
    ByAssetDefinitionFilterOpt<AssetDefinitionFilter>3
    ByAssetFilterOpt<AssetFilter>4
    ByTriggerFilterOpt<TriggerFilter>5
    ByRoleFilterOpt<RoleFilter>6

    DataEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    PeerPeerEvent0
    DomainDomainEvent1
    AccountAccountEvent2
    AssetDefinitionAssetDefinitionEvent3
    AssetAssetEvent4
    TriggerTriggerEvent5
    RoleRoleEvent6
    PermissionTokenPermissionTokenSchemaUpdateEvent7
    ConfigurationConfigurationEvent8
    ValidatorValidatorEvent9

    Divide

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<NumericValue>
    rightEvaluatesTo<NumericValue>

    Domain

    Type: Struct

    Declarations:

    Field nameField value
    idDomainId
    accountsSortedMap<AccountId, Account>
    asset_definitionsSortedMap<AssetDefinitionId, AssetDefinition>
    asset_total_quantitiesSortedMap<AssetDefinitionId, NumericValue>
    logoOption<IpfsPath>
    metadataMetadata

    DomainEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AccountAccountEvent0
    AssetDefinitionAssetDefinitionEvent1
    CreatedDomain2
    DeletedDomainId3
    MetadataInsertedMetadataChanged<DomainId>4
    MetadataRemovedMetadataChanged<DomainId>5

    DomainEventFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ByCreated0
    ByDeleted1
    ByMetadataInserted2
    ByMetadataRemoved3
    ByAccountFilterOpt<AccountFilter>4
    ByAssetDefinitionFilterOpt<AssetDefinitionFilter>5

    DomainFilter

    Type: Struct

    Declarations:

    Field nameField value
    origin_filterFilterOpt<OriginFilter<DomainEvent>>
    event_filterFilterOpt<DomainEventFilter>

    DomainId

    Type: Struct

    Declarations:

    Field nameField value
    nameName

    Duration

    Type: Tuple

    Values: (u64, u32)

    Equal

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<Value>
    rightEvaluatesTo<Value>

    EvaluatesTo<AccountId>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<AssetDefinitionId>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<AssetId>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<DomainId>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<HashOf<VersionedSignedBlock>>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<HashOf<VersionedSignedTransaction>>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<IdBox>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<Level>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<Name>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<NumericValue>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<Parameter>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<RegistrableBox>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<RoleId>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<String>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<TriggerId>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<UpgradableBox>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<Value>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<Vec<Value>>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluatesTo<bool>

    Type: Struct

    Declarations:

    Field nameField value
    expressionExpression

    EvaluationError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    MathMathError0
    ValidationValidationFail1
    FindString2
    ConversionString3

    Event

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    PipelinePipelineEvent0
    DataDataEvent1
    TimeTimeEvent2
    ExecuteTriggerExecuteTriggerEvent3
    NotificationNotificationEvent4

    EventMessage

    Type: Alias

    To: Event

    EventSubscriptionRequest

    Type: Alias

    To: FilterBox

    Executable

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    InstructionsVec<InstructionBox>0
    WasmWasmSmartContract1

    ExecuteTriggerBox

    Type: Struct

    Declarations:

    Field nameField value
    trigger_idEvaluatesTo<TriggerId>

    ExecuteTriggerEvent

    Type: Struct

    Declarations:

    Field nameField value
    trigger_idTriggerId
    authorityAccountId

    ExecuteTriggerEventFilter

    Type: Struct

    Declarations:

    Field nameField value
    trigger_idTriggerId
    authorityAccountId

    ExecutionTime

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    PreCommit0
    ScheduleSchedule1

    Expression

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AddAdd0
    SubtractSubtract1
    MultiplyMultiply2
    DivideDivide3
    ModMod4
    RaiseToRaiseTo5
    GreaterGreater6
    LessLess7
    EqualEqual8
    NotNot9
    AndAnd10
    OrOr11
    IfIf12
    RawValue13
    QueryQueryBox14
    ContainsContains15
    ContainsAllContainsAll16
    ContainsAnyContainsAny17
    WhereWhere18
    ContextValueContextValue19

    FailBox

    Type: Struct

    Declarations:

    Field nameField value
    messageString

    FilterBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    PipelinePipelineEventFilter0
    DataFilterOpt<DataEntityFilter>1
    TimeTimeEventFilter2
    ExecuteTriggerExecuteTriggerEventFilter3
    NotificationNotificationEventFilter4

    FilterOpt<AccountEventFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeAccountEventFilter1

    FilterOpt<AccountFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeAccountFilter1

    FilterOpt<AssetDefinitionEventFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeAssetDefinitionEventFilter1

    FilterOpt<AssetDefinitionFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeAssetDefinitionFilter1

    FilterOpt<AssetEventFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeAssetEventFilter1

    FilterOpt<AssetFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeAssetFilter1

    FilterOpt<DataEntityFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeDataEntityFilter1

    FilterOpt<DomainEventFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeDomainEventFilter1

    FilterOpt<DomainFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeDomainFilter1

    FilterOpt<OriginFilter<AccountEvent>>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeOriginFilter<AccountEvent>1

    FilterOpt<OriginFilter<AssetDefinitionEvent>>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeOriginFilter<AssetDefinitionEvent>1

    FilterOpt<OriginFilter<AssetEvent>>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeOriginFilter<AssetEvent>1

    FilterOpt<OriginFilter<DomainEvent>>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeOriginFilter<DomainEvent>1

    FilterOpt<OriginFilter<PeerEvent>>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeOriginFilter<PeerEvent>1

    FilterOpt<OriginFilter<RoleEvent>>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeOriginFilter<RoleEvent>1

    FilterOpt<OriginFilter<TriggerEvent>>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeOriginFilter<TriggerEvent>1

    FilterOpt<PeerEventFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomePeerEventFilter1

    FilterOpt<PeerFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomePeerFilter1

    FilterOpt<RoleEventFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeRoleEventFilter1

    FilterOpt<RoleFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeRoleFilter1

    FilterOpt<TriggerEventFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeTriggerEventFilter1

    FilterOpt<TriggerFilter>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    BySomeTriggerFilter1

    FindAccountById

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AccountId>

    FindAccountKeyValueByIdAndKey

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AccountId>
    keyEvaluatesTo<Name>

    FindAccountsByDomainId

    Type: Struct

    Declarations:

    Field nameField value
    domain_idEvaluatesTo<DomainId>

    FindAccountsByName

    Type: Struct

    Declarations:

    Field nameField value
    nameEvaluatesTo<Name>

    FindAccountsWithAsset

    Type: Struct

    Declarations:

    Field nameField value
    asset_definition_idEvaluatesTo<AssetDefinitionId>

    FindAllAccounts

    Type: Zero-Size Type (unit type, null type)

    FindAllActiveTriggerIds

    Type: Zero-Size Type (unit type, null type)

    FindAllAssets

    Type: Zero-Size Type (unit type, null type)

    FindAllAssetsDefinitions

    Type: Zero-Size Type (unit type, null type)

    FindAllBlockHeaders

    Type: Zero-Size Type (unit type, null type)

    FindAllBlocks

    Type: Zero-Size Type (unit type, null type)

    FindAllDomains

    Type: Zero-Size Type (unit type, null type)

    FindAllParameters

    Type: Zero-Size Type (unit type, null type)

    FindAllPeers

    Type: Zero-Size Type (unit type, null type)

    FindAllRoleIds

    Type: Zero-Size Type (unit type, null type)

    FindAllRoles

    Type: Zero-Size Type (unit type, null type)

    FindAllTransactions

    Type: Zero-Size Type (unit type, null type)

    FindAssetById

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AssetId>

    FindAssetDefinitionById

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AssetDefinitionId>

    FindAssetDefinitionKeyValueByIdAndKey

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AssetDefinitionId>
    keyEvaluatesTo<Name>

    FindAssetKeyValueByIdAndKey

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AssetId>
    keyEvaluatesTo<Name>

    FindAssetQuantityById

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AssetId>

    FindAssetsByAccountId

    Type: Struct

    Declarations:

    Field nameField value
    account_idEvaluatesTo<AccountId>

    FindAssetsByAssetDefinitionId

    Type: Struct

    Declarations:

    Field nameField value
    asset_definition_idEvaluatesTo<AssetDefinitionId>

    FindAssetsByDomainId

    Type: Struct

    Declarations:

    Field nameField value
    domain_idEvaluatesTo<DomainId>

    FindAssetsByDomainIdAndAssetDefinitionId

    Type: Struct

    Declarations:

    Field nameField value
    domain_idEvaluatesTo<DomainId>
    asset_definition_idEvaluatesTo<AssetDefinitionId>

    FindAssetsByName

    Type: Struct

    Declarations:

    Field nameField value
    nameEvaluatesTo<Name>

    FindBlockHeaderByHash

    Type: Struct

    Declarations:

    Field nameField value
    hashEvaluatesTo<HashOf<VersionedSignedBlock>>

    FindDomainById

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<DomainId>

    FindDomainKeyValueByIdAndKey

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<DomainId>
    keyEvaluatesTo<Name>

    FindError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AssetAssetId0
    AssetDefinitionAssetDefinitionId1
    AccountAccountId2
    DomainDomainId3
    MetadataKeyName4
    BlockHashOf<VersionedSignedBlock>5
    TransactionHashOf<VersionedSignedTransaction>6
    PeerPeerId7
    TriggerTriggerId8
    RoleRoleId9
    PermissionTokenName10
    ParameterParameterId11
    PublicKeyPublicKey12

    FindPermissionTokenSchema

    Type: Zero-Size Type (unit type, null type)

    FindPermissionTokensByAccountId

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AccountId>

    FindRoleByRoleId

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<RoleId>

    FindRolesByAccountId

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AccountId>

    FindTotalAssetQuantityByAssetDefinitionId

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<AssetDefinitionId>

    FindTransactionByHash

    Type: Struct

    Declarations:

    Field nameField value
    hashEvaluatesTo<HashOf<VersionedSignedTransaction>>

    FindTransactionsByAccountId

    Type: Struct

    Declarations:

    Field nameField value
    account_idEvaluatesTo<AccountId>

    FindTriggerById

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<TriggerId>

    FindTriggerKeyValueByIdAndKey

    Type: Struct

    Declarations:

    Field nameField value
    idEvaluatesTo<TriggerId>
    keyEvaluatesTo<Name>

    FindTriggersByDomainId

    Type: Struct

    Declarations:

    Field nameField value
    domain_idEvaluatesTo<DomainId>

    Fixed

    Type: Alias

    To: FixedPoint<i64>

    FixedPoint<i64>

    Type: Fixed Point

    Base: i64

    Decimal places: 9

    ForwardCursor

    Type: Struct

    Declarations:

    Field nameField value
    query_idOption<String>
    cursorOption<NonZero<u64>>

    GenericPredicateBox<ValuePredicate>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AndNonTrivial<GenericPredicateBox<ValuePredicate>>0
    OrNonTrivial<GenericPredicateBox<ValuePredicate>>1
    NotGenericPredicateBox<ValuePredicate>2
    RawValuePredicate3

    GrantBox

    Type: Struct

    Declarations:

    Field nameField value
    objectEvaluatesTo<Value>
    destination_idEvaluatesTo<IdBox>

    Greater

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<NumericValue>
    rightEvaluatesTo<NumericValue>

    Hash

    Type: Alias

    To: Array<u8, 32>

    HashOf<MerkleTree<VersionedSignedTransaction>>

    Type: Alias

    To: Hash

    HashOf<VersionedSignedBlock>

    Type: Alias

    To: Hash

    HashOf<VersionedSignedTransaction>

    Type: Alias

    To: Hash

    HashOf<WasmSmartContract>

    Type: Alias

    To: Hash

    HashValue

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    TransactionHashOf<VersionedSignedTransaction>0
    BlockHashOf<VersionedSignedBlock>1

    IdBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    DomainIdDomainId0
    AccountIdAccountId1
    AssetDefinitionIdAssetDefinitionId2
    AssetIdAssetId3
    PeerIdPeerId4
    TriggerIdTriggerId5
    RoleIdRoleId6
    PermissionTokenIdName7
    ParameterIdParameterId8

    IdentifiableBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    NewDomainNewDomain0
    NewAccountNewAccount1
    NewAssetDefinitionNewAssetDefinition2
    NewRoleNewRole3
    PeerPeer4
    DomainDomain5
    AccountAccount6
    AssetDefinitionAssetDefinition7
    AssetAsset8
    TriggerTriggerBox9
    RoleRole10
    ParameterParameter11

    If

    Type: Struct

    Declarations:

    Field nameField value
    conditionEvaluatesTo<bool>
    thenEvaluatesTo<Value>
    otherwiseEvaluatesTo<Value>

    InstructionBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    RegisterRegisterBox0
    UnregisterUnregisterBox1
    MintMintBox2
    BurnBurnBox3
    TransferTransferBox4
    IfConditional5
    PairPair6
    SequenceSequenceBox7
    SetKeyValueSetKeyValueBox8
    RemoveKeyValueRemoveKeyValueBox9
    GrantGrantBox10
    RevokeRevokeBox11
    ExecuteTriggerExecuteTriggerBox12
    SetParameterSetParameterBox13
    NewParameterNewParameterBox14
    UpgradeUpgradeBox15
    LogLogBox16
    FailFailBox17

    InstructionEvaluationError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ExpressionEvaluationError0
    UnsupportedInstructionType1
    PermissionParameterString2
    TypeTypeError3

    InstructionExecutionError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    EvaluateInstructionEvaluationError0
    QueryQueryExecutionFail1
    ConversionString2
    FindFindError3
    RepetitionRepetitionError4
    MintabilityMintabilityError5
    MathMathError6
    MetadataMetadataError7
    FailString8
    InvalidParameterInvalidParameterError9
    InvariantViolationString10

    InstructionExecutionFail

    Type: Struct

    Declarations:

    Field nameField value
    instructionInstructionBox
    reasonString

    InstructionType

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Register0
    Unregister1
    Mint2
    Burn3
    Transfer4
    If5
    Pair6
    Sequence7
    SetKeyValue8
    RemoveKeyValue9
    Grant10
    Revoke11
    ExecuteTrigger12
    SetParameter13
    NewParameter14
    Upgrade15
    Log16
    Fail17

    Interval<u16>

    Type: Struct

    Declarations:

    Field nameField value
    startu16
    limitu16

    Interval<u8>

    Type: Struct

    Declarations:

    Field nameField value
    startu8
    limitu8

    InvalidParameterError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    WasmString0
    NameLength1

    IpfsPath

    Type: Alias

    To: String

    Ipv4Addr

    Type: Alias

    To: Array<u8, 4>

    Ipv4Predicate

    Type: Alias

    To: Array<Interval<u8>, 4>

    Ipv6Addr

    Type: Alias

    To: Array<u16, 8>

    Ipv6Predicate

    Type: Alias

    To: Array<Interval<u16>, 8>

    IsAssetDefinitionOwner

    Type: Struct

    Declarations:

    Field nameField value
    asset_definition_idEvaluatesTo<AssetDefinitionId>
    account_idEvaluatesTo<AccountId>

    LengthLimits

    Type: Struct

    Declarations:

    Field nameField value
    minu32
    maxu32

    Less

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<NumericValue>
    rightEvaluatesTo<NumericValue>

    Level

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    TRACE0
    DEBUG1
    INFO2
    WARN3
    ERROR4

    Limits

    Type: Struct

    Declarations:

    Field nameField value
    max_lenu32
    max_entry_byte_sizeu32

    LogBox

    Type: Struct

    Declarations:

    Field nameField value
    levelEvaluatesTo<Level>
    msgEvaluatesTo<String>

    MathError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Overflow0
    NotEnoughQuantity1
    DivideByZero2
    NegativeValue3
    DomainViolation4
    Unknown5
    BinaryOpIncompatibleNumericValueTypesBinaryOpIncompatibleNumericValueTypesError6
    FixedPointConversionString7

    MerkleTree<VersionedSignedTransaction>

    Type: Vec

    Value: HashOf<VersionedSignedTransaction>

    Metadata

    Type: Struct

    Declarations:

    Field nameField value
    mapSortedMap<Name, Value>

    MetadataChanged<AccountId>

    Type: Struct

    Declarations:

    Field nameField value
    target_idAccountId
    keyName
    valueValue

    MetadataChanged<AssetDefinitionId>

    Type: Struct

    Declarations:

    Field nameField value
    target_idAssetDefinitionId
    keyName
    valueValue

    MetadataChanged<AssetId>

    Type: Struct

    Declarations:

    Field nameField value
    target_idAssetId
    keyName
    valueValue

    MetadataChanged<DomainId>

    Type: Struct

    Declarations:

    Field nameField value
    target_idDomainId
    keyName
    valueValue

    MetadataError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    EntryTooBigSizeError0
    OverallSizeSizeError1
    EmptyPath2
    MissingSegmentName3
    InvalidSegmentName4

    MintBox

    Type: Struct

    Declarations:

    Field nameField value
    objectEvaluatesTo<Value>
    destination_idEvaluatesTo<IdBox>

    MintabilityError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    MintUnmintable0
    ForbidMintOnMintable1

    Mintable

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Infinitely0
    Once1
    Not2

    Mismatch<AssetDefinitionId>

    Type: Struct

    Declarations:

    Field nameField value
    expectedAssetDefinitionId
    actualAssetDefinitionId

    Mismatch<AssetValueType>

    Type: Struct

    Declarations:

    Field nameField value
    expectedAssetValueType
    actualAssetValueType

    Mismatch<Value>

    Type: Struct

    Declarations:

    Field nameField value
    expectedValue
    actualValue

    Mod

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<NumericValue>
    rightEvaluatesTo<NumericValue>

    Multiply

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<NumericValue>
    rightEvaluatesTo<NumericValue>

    Name

    Type: Alias

    To: String

    NewAccount

    Type: Struct

    Declarations:

    Field nameField value
    idAccountId
    signatoriesSortedVec<PublicKey>
    metadataMetadata

    NewAssetDefinition

    Type: Struct

    Declarations:

    Field nameField value
    idAssetDefinitionId
    value_typeAssetValueType
    mintableMintable
    logoOption<IpfsPath>
    metadataMetadata

    NewDomain

    Type: Struct

    Declarations:

    Field nameField value
    idDomainId
    logoOption<IpfsPath>
    metadataMetadata

    NewParameterBox

    Type: Struct

    Declarations:

    Field nameField value
    parameterEvaluatesTo<Parameter>

    NewRole

    Type: Struct

    Declarations:

    Field nameField value
    innerRole

    NonTrivial<GenericPredicateBox<ValuePredicate>>

    Type: Alias

    To: Vec<GenericPredicateBox<ValuePredicate>>

    NonZero<u32>

    Type: Alias

    To: u32

    NonZero<u64>

    Type: Alias

    To: u64

    Not

    Type: Struct

    Declarations:

    Field nameField value
    expressionEvaluatesTo<bool>

    NotificationEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    TriggerCompletedTriggerCompletedEvent0

    NotificationEventFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AcceptAll0
    TriggerCompletedTriggerCompletedEventFilter1

    NumericValue

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    U32u320
    U64u641
    U128u1282
    FixedFixed3

    OptimizedExecutable

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    WasmInternalReprWasmInternalRepr0
    InstructionsVec<InstructionBox>1

    Option<DomainId>

    Type: Option

    Some: DomainId

    Option<Duration>

    Type: Option

    Some: Duration

    Option<Hash>

    Type: Option

    Some: Hash

    Option<HashOf<MerkleTree<VersionedSignedTransaction>>>

    Type: Option

    Some: HashOf<MerkleTree<VersionedSignedTransaction>>

    Option<HashOf<VersionedSignedBlock>>

    Type: Option

    Some: HashOf<VersionedSignedBlock>

    Option<InstructionBox>

    Type: Option

    Some: InstructionBox

    Option<IpfsPath>

    Type: Option

    Some: IpfsPath

    Option<NonZero<u32>>

    Type: Option

    Some: NonZero<u32>

    Option<NonZero<u64>>

    Type: Option

    Some: NonZero<u64>

    Option<PipelineEntityKind>

    Type: Option

    Some: PipelineEntityKind

    Option<PipelineStatusKind>

    Type: Option

    Some: PipelineStatusKind

    Option<String>

    Type: Option

    Some: String

    Option<TimeInterval>

    Type: Option

    Some: TimeInterval

    Option<TransactionRejectionReason>

    Type: Option

    Some: TransactionRejectionReason

    Option<TriggerCompletedOutcomeType>

    Type: Option

    Some: TriggerCompletedOutcomeType

    Option<TriggerId>

    Type: Option

    Some: TriggerId

    Or

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<bool>
    rightEvaluatesTo<bool>

    OriginFilter<AccountEvent>

    Type: Alias

    To: AccountId

    OriginFilter<AssetDefinitionEvent>

    Type: Alias

    To: AssetDefinitionId

    OriginFilter<AssetEvent>

    Type: Alias

    To: AssetId

    OriginFilter<DomainEvent>

    Type: Alias

    To: DomainId

    OriginFilter<PeerEvent>

    Type: Alias

    To: PeerId

    OriginFilter<RoleEvent>

    Type: Alias

    To: RoleId

    OriginFilter<TriggerEvent>

    Type: Alias

    To: TriggerId

    Pair

    Type: Struct

    Declarations:

    Field nameField value
    left_instructionInstructionBox
    right_instructionInstructionBox

    Parameter

    Type: Struct

    Declarations:

    Field nameField value
    idParameterId
    valValue

    ParameterId

    Type: Struct

    Declarations:

    Field nameField value
    nameName

    Peer

    Type: Struct

    Declarations:

    Field nameField value
    idPeerId

    PeerEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AddedPeerId0
    RemovedPeerId1

    PeerEventFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ByAdded0
    ByRemoved1

    PeerFilter

    Type: Struct

    Declarations:

    Field nameField value
    origin_filterFilterOpt<OriginFilter<PeerEvent>>
    event_filterFilterOpt<PeerEventFilter>

    PeerId

    Type: Struct

    Declarations:

    Field nameField value
    addressSocketAddr
    public_keyPublicKey

    PermissionRemoved

    Type: Struct

    Declarations:

    Field nameField value
    role_idRoleId
    permission_token_idName

    PermissionToken

    Type: Struct

    Declarations:

    Field nameField value
    definition_idName
    payloadStringWithJson

    PermissionTokenSchema

    Type: Struct

    Declarations:

    Field nameField value
    token_idsVec<Name>
    schemaString

    PermissionTokenSchemaUpdateEvent

    Type: Struct

    Declarations:

    Field nameField value
    old_schemaPermissionTokenSchema
    new_schemaPermissionTokenSchema

    PipelineEntityKind

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Block0
    Transaction1

    PipelineEvent

    Type: Struct

    Declarations:

    Field nameField value
    entity_kindPipelineEntityKind
    statusPipelineStatus
    hashHash

    PipelineEventFilter

    Type: Struct

    Declarations:

    Field nameField value
    entity_kindOption<PipelineEntityKind>
    status_kindOption<PipelineStatusKind>
    hashOption<Hash>

    PipelineRejectionReason

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    BlockBlockRejectionReason0
    TransactionTransactionRejectionReason1

    PipelineStatus

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Validating0
    RejectedPipelineRejectionReason1
    Committed2

    PipelineStatusKind

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Validating0
    Rejected1
    Committed2

    PublicKey

    Type: Struct

    Declarations:

    Field nameField value
    digest_functionAlgorithm
    payloadVec<u8>

    QueryBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    FindAllAccountsFindAllAccounts0
    FindAccountByIdFindAccountById1
    FindAccountKeyValueByIdAndKeyFindAccountKeyValueByIdAndKey2
    FindAccountsByNameFindAccountsByName3
    FindAccountsByDomainIdFindAccountsByDomainId4
    FindAccountsWithAssetFindAccountsWithAsset5
    FindAllAssetsFindAllAssets6
    FindAllAssetsDefinitionsFindAllAssetsDefinitions7
    FindAssetByIdFindAssetById8
    FindAssetDefinitionByIdFindAssetDefinitionById9
    FindAssetsByNameFindAssetsByName10
    FindAssetsByAccountIdFindAssetsByAccountId11
    FindAssetsByAssetDefinitionIdFindAssetsByAssetDefinitionId12
    FindAssetsByDomainIdFindAssetsByDomainId13
    FindAssetsByDomainIdAndAssetDefinitionIdFindAssetsByDomainIdAndAssetDefinitionId14
    FindAssetQuantityByIdFindAssetQuantityById15
    FindTotalAssetQuantityByAssetDefinitionIdFindTotalAssetQuantityByAssetDefinitionId16
    IsAssetDefinitionOwnerIsAssetDefinitionOwner17
    FindAssetKeyValueByIdAndKeyFindAssetKeyValueByIdAndKey18
    FindAssetDefinitionKeyValueByIdAndKeyFindAssetDefinitionKeyValueByIdAndKey19
    FindAllDomainsFindAllDomains20
    FindDomainByIdFindDomainById21
    FindDomainKeyValueByIdAndKeyFindDomainKeyValueByIdAndKey22
    FindAllPeersFindAllPeers23
    FindAllBlocksFindAllBlocks24
    FindAllBlockHeadersFindAllBlockHeaders25
    FindBlockHeaderByHashFindBlockHeaderByHash26
    FindAllTransactionsFindAllTransactions27
    FindTransactionsByAccountIdFindTransactionsByAccountId28
    FindTransactionByHashFindTransactionByHash29
    FindPermissionTokensByAccountIdFindPermissionTokensByAccountId30
    FindPermissionTokenSchemaFindPermissionTokenSchema31
    FindAllActiveTriggerIdsFindAllActiveTriggerIds32
    FindTriggerByIdFindTriggerById33
    FindTriggerKeyValueByIdAndKeyFindTriggerKeyValueByIdAndKey34
    FindTriggersByDomainIdFindTriggersByDomainId35
    FindAllRolesFindAllRoles36
    FindAllRoleIdsFindAllRoleIds37
    FindRoleByRoleIdFindRoleByRoleId38
    FindRolesByAccountIdFindRolesByAccountId39
    FindAllParametersFindAllParameters40

    QueryExecutionFail

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    SignatureString0
    EvaluateString1
    FindFindError2
    ConversionString3
    Unauthorized4

    QueryPayload

    Type: Struct

    Declarations:

    Field nameField value
    authorityAccountId
    queryQueryBox
    filterGenericPredicateBox<ValuePredicate>

    RaiseTo

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<NumericValue>
    rightEvaluatesTo<NumericValue>

    RawGenesisBlock

    Type: Struct

    Declarations:

    Field nameField value
    transactionsVec<Vec<InstructionBox>>
    validatorValidatorMode

    RegisterBox

    Type: Struct

    Declarations:

    Field nameField value
    objectEvaluatesTo<RegistrableBox>

    RegistrableBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    PeerPeer0
    DomainNewDomain1
    AccountNewAccount2
    AssetDefinitionNewAssetDefinition3
    AssetAsset4
    TriggerTrigger<TriggeringFilterBox, Executable>5
    RoleNewRole6

    RemoveKeyValueBox

    Type: Struct

    Declarations:

    Field nameField value
    object_idEvaluatesTo<IdBox>
    keyEvaluatesTo<Name>

    Repeats

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Indefinitely0
    Exactlyu321

    RepetitionError

    Type: Struct

    Declarations:

    Field nameField value
    instruction_typeInstructionType
    idIdBox

    RevokeBox

    Type: Struct

    Declarations:

    Field nameField value
    objectEvaluatesTo<Value>
    destination_idEvaluatesTo<IdBox>

    Role

    Type: Struct

    Declarations:

    Field nameField value
    idRoleId
    permissionsSortedVec<PermissionToken>

    RoleEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    CreatedRole0
    DeletedRoleId1
    PermissionRemovedPermissionRemoved2

    RoleEventFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ByCreated0
    ByDeleted1
    ByPermissionRemoved2

    RoleFilter

    Type: Struct

    Declarations:

    Field nameField value
    origin_filterFilterOpt<OriginFilter<RoleEvent>>
    event_filterFilterOpt<RoleEventFilter>

    RoleId

    Type: Struct

    Declarations:

    Field nameField value
    nameName

    Schedule

    Type: Struct

    Declarations:

    Field nameField value
    startDuration
    periodOption<Duration>

    SemiInterval<Fixed>

    Type: Struct

    Declarations:

    Field nameField value
    startFixed
    limitFixed

    SemiInterval<u128>

    Type: Struct

    Declarations:

    Field nameField value
    startu128
    limitu128

    SemiInterval<u32>

    Type: Struct

    Declarations:

    Field nameField value
    startu32
    limitu32

    SemiRange

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    U32SemiInterval<u32>0
    U128SemiInterval<u128>1
    FixedSemiInterval<Fixed>2

    SequenceBox

    Type: Struct

    Declarations:

    Field nameField value
    instructionsVec<InstructionBox>

    SetKeyValueBox

    Type: Struct

    Declarations:

    Field nameField value
    object_idEvaluatesTo<IdBox>
    keyEvaluatesTo<Name>
    valueEvaluatesTo<Value>

    SetParameterBox

    Type: Struct

    Declarations:

    Field nameField value
    parameterEvaluatesTo<Parameter>

    Signature

    Type: Struct

    Declarations:

    Field nameField value
    public_keyPublicKey
    payloadVec<u8>

    SignatureCheckCondition

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AnyAccountSignatureOrVec<PublicKey>0
    AllAccountSignaturesAndVec<PublicKey>1

    SignatureOf<BlockPayload>

    Type: Alias

    To: Signature

    SignatureOf<QueryPayload>

    Type: Alias

    To: Signature

    SignatureOf<TransactionPayload>

    Type: Alias

    To: Signature

    SignaturesOf<BlockPayload>

    Type: Struct

    Declarations:

    Field nameField value
    signaturesSortedVec<SignatureOf<BlockPayload>>

    SignaturesOf<TransactionPayload>

    Type: Struct

    Declarations:

    Field nameField value
    signaturesSortedVec<SignatureOf<TransactionPayload>>

    SignedBlock

    Type: Struct

    Declarations:

    Field nameField value
    signaturesSignaturesOf<BlockPayload>
    payloadBlockPayload

    SignedQuery

    Type: Struct

    Declarations:

    Field nameField value
    signatureSignatureOf<QueryPayload>
    payloadQueryPayload

    SignedTransaction

    Type: Struct

    Declarations:

    Field nameField value
    signaturesSignaturesOf<TransactionPayload>
    payloadTransactionPayload

    SizeError

    Type: Struct

    Declarations:

    Field nameField value
    limitsLimits
    actualu64

    SocketAddr

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Ipv4SocketAddrV40
    Ipv6SocketAddrV61
    HostSocketAddrHost2

    SocketAddrHost

    Type: Struct

    Declarations:

    Field nameField value
    hostString
    portu16

    SocketAddrV4

    Type: Struct

    Declarations:

    Field nameField value
    ipIpv4Addr
    portu16

    SocketAddrV6

    Type: Struct

    Declarations:

    Field nameField value
    ipIpv6Addr
    portu16

    SortedMap<AccountId, Account>

    Type: Map

    Key: AccountId

    Value: Account

    SortedMap<AssetDefinitionId, AssetDefinition>

    Type: Map

    Key: AssetDefinitionId

    Value: AssetDefinition

    SortedMap<AssetDefinitionId, NumericValue>

    Type: Map

    Key: AssetDefinitionId

    Value: NumericValue

    SortedMap<AssetId, Asset>

    Type: Map

    Key: AssetId

    Value: Asset

    SortedMap<Name, EvaluatesTo<Value>>

    Type: Map

    Key: Name

    Value: EvaluatesTo<Value>

    SortedMap<Name, Value>

    Type: Map

    Key: Name

    Value: Value

    SortedVec<PermissionToken>

    Type: Vec

    Value: PermissionToken

    SortedVec<PublicKey>

    Type: Vec

    Value: PublicKey

    SortedVec<RoleId>

    Type: Vec

    Value: RoleId

    SortedVec<SignatureOf<BlockPayload>>

    Type: Vec

    Value: SignatureOf<BlockPayload>

    SortedVec<SignatureOf<TransactionPayload>>

    Type: Vec

    Value: SignatureOf<TransactionPayload>

    String

    Type: Alias

    To: String

    StringPredicate

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ContainsString0
    StartsWithString1
    EndsWithString2
    IsString3

    StringWithJson

    Type: Alias

    To: String

    Subtract

    Type: Struct

    Declarations:

    Field nameField value
    leftEvaluatesTo<NumericValue>
    rightEvaluatesTo<NumericValue>

    TimeEvent

    Type: Struct

    Declarations:

    Field nameField value
    prev_intervalOption<TimeInterval>
    intervalTimeInterval

    TimeEventFilter

    Type: Alias

    To: ExecutionTime

    TimeInterval

    Type: Struct

    Declarations:

    Field nameField value
    sinceDuration
    lengthDuration

    TransactionLimitError

    Type: Struct

    Declarations:

    Field nameField value
    reasonString

    TransactionLimits

    Type: Struct

    Declarations:

    Field nameField value
    max_instruction_numberu64
    max_wasm_size_bytesu64

    TransactionPayload

    Type: Struct

    Declarations:

    Field nameField value
    creation_time_msu64
    authorityAccountId
    instructionsExecutable
    time_to_live_msOption<NonZero<u64>>
    nonceOption<NonZero<u32>>
    metadataSortedMap<Name, Value>

    TransactionQueryOutput

    Type: Struct

    Declarations:

    Field nameField value
    transactionTransactionValue
    block_hashHashOf<VersionedSignedBlock>

    TransactionRejectionReason

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AccountDoesNotExistFindError0
    LimitCheckTransactionLimitError1
    ValidationValidationFail2
    InstructionExecutionInstructionExecutionFail3
    WasmExecutionWasmExecutionFail4
    Expired5

    TransactionValue

    Type: Struct

    Declarations:

    Field nameField value
    valueVersionedSignedTransaction
    errorOption<TransactionRejectionReason>

    TransferBox

    Type: Struct

    Declarations:

    Field nameField value
    source_idEvaluatesTo<IdBox>
    objectEvaluatesTo<Value>
    destination_idEvaluatesTo<IdBox>

    Trigger<TriggeringFilterBox, Executable>

    Type: Struct

    Declarations:

    Field nameField value
    idTriggerId
    actionAction<TriggeringFilterBox, Executable>

    Trigger<TriggeringFilterBox, OptimizedExecutable>

    Type: Struct

    Declarations:

    Field nameField value
    idTriggerId
    actionAction<TriggeringFilterBox, OptimizedExecutable>

    TriggerBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    RawTrigger<TriggeringFilterBox, Executable>0
    OptimizedTrigger<TriggeringFilterBox, OptimizedExecutable>1

    TriggerCompletedEvent

    Type: Struct

    Declarations:

    Field nameField value
    trigger_idTriggerId
    outcomeTriggerCompletedOutcome

    TriggerCompletedEventFilter

    Type: Struct

    Declarations:

    Field nameField value
    trigger_idOption<TriggerId>
    outcome_typeOption<TriggerCompletedOutcomeType>

    TriggerCompletedOutcome

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Success0
    FailureString1

    TriggerCompletedOutcomeType

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Success0
    Failure1

    TriggerEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    CreatedTriggerId0
    DeletedTriggerId1
    ExtendedTriggerNumberOfExecutionsChanged2
    ShortenedTriggerNumberOfExecutionsChanged3

    TriggerEventFilter

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ByCreated0
    ByDeleted1
    ByExtended2
    ByShortened3

    TriggerFilter

    Type: Struct

    Declarations:

    Field nameField value
    origin_filterFilterOpt<OriginFilter<TriggerEvent>>
    event_filterFilterOpt<TriggerEventFilter>

    TriggerId

    Type: Struct

    Declarations:

    Field nameField value
    nameName
    domain_idOption<DomainId>

    TriggerNumberOfExecutionsChanged

    Type: Struct

    Declarations:

    Field nameField value
    trigger_idTriggerId
    byu32

    TriggeringFilterBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    PipelinePipelineEventFilter0
    DataFilterOpt<DataEntityFilter>1
    TimeTimeEventFilter2
    ExecuteTriggerExecuteTriggerEventFilter3

    TypeError

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    AssetValueTypeMismatch<AssetValueType>0
    ParameterValueTypeMismatch<Value>1
    AssetDefinitionIdMismatch<AssetDefinitionId>2

    UnregisterBox

    Type: Struct

    Declarations:

    Field nameField value
    object_idEvaluatesTo<IdBox>

    UpgradableBox

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    ValidatorValidator0

    UpgradeBox

    Type: Struct

    Declarations:

    Field nameField value
    objectEvaluatesTo<UpgradableBox>

    ValidationFail

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    NotPermittedString0
    InstructionFailedInstructionExecutionError1
    QueryFailedQueryExecutionFail2
    TooComplex3
    InternalError4

    Validator

    Type: Struct

    Declarations:

    Field nameField value
    wasmWasmSmartContract

    ValidatorEvent

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Upgraded0

    ValidatorMode

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    PathString0
    InlineValidator1

    Value

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    Boolbool0
    StringString1
    NameName2
    VecVec<Value>3
    LimitedMetadataMetadata4
    MetadataLimitsLimits5
    TransactionLimitsTransactionLimits6
    LengthLimitsLengthLimits7
    IdIdBox8
    IdentifiableIdentifiableBox9
    PublicKeyPublicKey10
    SignatureCheckConditionSignatureCheckCondition11
    TransactionQueryOutputTransactionQueryOutput12
    PermissionTokenPermissionToken13
    PermissionTokenSchemaPermissionTokenSchema14
    HashHashValue15
    BlockVersionedSignedBlock16
    BlockHeaderBlockHeader17
    Ipv4AddrIpv4Addr18
    Ipv6AddrIpv6Addr19
    NumericNumericValue20
    ValidatorValidator21
    LogLevelLevel22

    ValueOfKey

    Type: Struct

    Declarations:

    Field nameField value
    keyName
    predicateValuePredicate

    ValuePredicate

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    IdentifiableStringPredicate0
    ContainerContainer1
    DisplayStringPredicate2
    NumericalSemiRange3
    TimeStampSemiInterval<u128>4
    Ipv4AddrIpv4Predicate5
    Ipv6AddrIpv6Predicate6
    Pass7

    Vec<Event>

    Type: Vec

    Value: Event

    Vec<GenericPredicateBox<ValuePredicate>>

    Type: Vec

    Value: GenericPredicateBox<ValuePredicate>

    Vec<InstructionBox>

    Type: Vec

    Value: InstructionBox

    Vec<Name>

    Type: Vec

    Value: Name

    Vec<PeerId>

    Type: Vec

    Value: PeerId

    Vec<PublicKey>

    Type: Vec

    Value: PublicKey

    Vec<TransactionValue>

    Type: Vec

    Value: TransactionValue

    Vec<Value>

    Type: Vec

    Value: Value

    Vec<Vec<InstructionBox>>

    Type: Vec

    Value: Vec<InstructionBox>

    Vec<VersionedSignedTransaction>

    Type: Vec

    Value: VersionedSignedTransaction

    Vec<u8>

    Type: Vec

    Value: u8

    VersionedBatchedResponse<Value>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    V1BatchedResponse<Value>1

    VersionedBatchedResponse<Vec<VersionedSignedTransaction>>

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    V1BatchedResponse<Vec<VersionedSignedTransaction>>1

    VersionedSignedBlock

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    V1SignedBlock1

    VersionedSignedQuery

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    V1SignedQuery1

    VersionedSignedTransaction

    Type: Enum

    Variants:

    Variant nameVariant valueDiscriminant
    V1SignedTransaction1

    WasmExecutionFail

    Type: Struct

    Declarations:

    Field nameField value
    reasonString

    WasmInternalRepr

    Type: Struct

    Declarations:

    Field nameField value
    serializedVec<u8>
    blob_hashHashOf<WasmSmartContract>

    WasmSmartContract

    Type: Alias

    To: Vec<u8>

    Where

    Type: Struct

    Declarations:

    Field nameField value
    expressionEvaluatesTo<Value>
    valuesSortedMap<Name, EvaluatesTo<Value>>

    bool

    Type: Alias

    To: bool

    i64

    Type: Int

    Kind: FixedWidth

    u128

    Type: Int

    Kind: FixedWidth

    u16

    Type: Int

    Kind: FixedWidth

    u32

    Type: Int

    Kind: FixedWidth

    u64

    Type: Int

    Kind: FixedWidth

    u8

    Type: Int

    Kind: FixedWidth

    + + + + \ No newline at end of file diff --git a/reference/ffi.html b/reference/ffi.html new file mode 100644 index 000000000..d8d7b8a30 --- /dev/null +++ b/reference/ffi.html @@ -0,0 +1,64 @@ + + + + + + Foreign Function Interfaces (FFI) | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Foreign Function Interfaces (FFI)

    To reduce the sizes of smartcontracts, we provide a dynamic library in the execution environment. We shall detail how to link against that library and use the functions at a later date, but for now, let's explore how to include functions and trait implementations into that library.

    Why FFI

    A function is a rather abstract entity, and while most languages agree on what a function should do, the way in which said functions are represented is very different. Moreover, in some languages (like Rust), the consequences of calling a function, and the things that it is allowed to do are different. Because one can use any language to create a WASM smartcontract, we need to level the playing field. This is where the concept of foreign function interface (FFI) comes in.

    The main standard used today is the C application binary interface. It's simple, it's guaranteed to be available even in languages which can't compile to WASM, and it's stable. In principle, you could do everything manually, but Iroha provides you with a crate iroha_ffi which contains all you need to generate FFI-compliant functions out of your existing Rust API.

    You can, of course, do this your way. The iroha_ffi crate merely generates the code that you would need to generate anyway. Writing the necessary boilerplate requires quite a bit of diligence and discipline. Every function call over the FFI boundary is unsafe with a potential to cause undefined behaviour. The method by which we managed to solve it, revolves around using robust repr(C) types.

    INFO

    The only exception are pointers. The null check and the validity cannot be enforced globally, so raw pointers (as always) are only used in exceptional cases. Given that we provide wrappers around almost every instance of an object in the Iroha data model, you shouldn't have to use raw pointers at all.

    Example

    Here is an example of generating a binding:

    rust
    #[derive(FfiType)]
    +struct DaysSinceEquinox(u32);
    +
    +#[ffi_export]
    +impl DaysSinceEquinox {
    +    pub fn update_value(&mut self, a: &u8) {
    +        self.0 = *a as u32;
    +    }
    +}
    #[derive(FfiType)]
    +struct DaysSinceEquinox(u32);
    +
    +#[ffi_export]
    +impl DaysSinceEquinox {
    +    pub fn update_value(&mut self, a: &u8) {
    +        self.0 = *a as u32;
    +    }
    +}

    The example above will generate the following binding with DaysSinceEquinox represented as an opaque pointer:

    rust
    pub extern fn DaysSinceEquinox__update_value(handle: *mut DaysSinceEquinox, a: *const u8) -> FfiReturn {
    +    // function implementation
    +}
    pub extern fn DaysSinceEquinox__update_value(handle: *mut DaysSinceEquinox, a: *const u8) -> FfiReturn {
    +    // function implementation
    +}

    FFI Binding Generation

    The iroha_ffi crate is used to generate functions that are callable via FFI. Given Rust structs and methods, they generate the unsafe code that you would need in order to cross the linking boundary.

    A Rust type is converted into a robust repr(C) type that can cross the FFI boundary with FfiType::into_ffi. This goes the other way around as well: FFI ReprC type is converted into a Rust type via FfiType::try_from_ffi.

    WARNING

    Note that the opposite conversion is fallible and can cause undefined behaviour. While we can make the best effort to avoid the most obvious mistakes, you must ensure the program's correctness on your end.

    The diagram below uses the creation of a new domain as an example to show the conversion process (more on the name mangling semantics in a separate section).

    Untitled

    The main traits that enable binding generation are ReprC, FfiType and FfiConvert

    TraitDescription
    ReprCThis trait represents a robust type that conforms to C ABI. The type can be safely shared across FFI boundaries.
    FfiTypeThis trait defines a corresponding ReprC type for a given Rust type. The defined ReprC type is used in place of the Rust type in the API of the generated FFI function.
    FfiConvertThis trait defines two methods into_ffi and try_from_ffi that are used to perform the conversion of the Rust type to or from ReprC type.

    Note that there is no ownership transfer over FFI except for opaque pointer types. All other types that carry ownership, such as Vec<T>, are cloned.

    Name Mangling

    Note the use of double underscores in generated names of FFI objects:

    • For the inherent_fn method defined on the StructName struct, the FFI name would be StructName__inherent_fn.

    • For the MethodName method from the TraitName trait in the StructName struct, the FFI name would be StructName__TraitName__MethodName.

    • To set the field_name field in the StructName struct, the FFI function name would be StructName__set_field_name.

    • To get the field_name field in the StructName struct, the FFI function name would be StructName__field_name.

    • To get the mutable field_name field in the StructName struct, the FFI function name would be StrucuName__field_name_mut.

    • For the freestanding module_name::fn_name, the FFI name would be module_name::__fn_name.

    • For the traits that are not generic and allow sharing their implementation in the FFI (see Clone below), the FFI name would be module_name::__clone.

      rust
      impl Clone for Type1 {
      +    fn clone(&self) -> Self;
      +}
      +impl Clone for Type2 {
      +    fn clone(&self) -> Self;
      +}
      impl Clone for Type1 {
      +    fn clone(&self) -> Self;
      +}
      +impl Clone for Type2 {
      +    fn clone(&self) -> Self;
      +}
    + + + + \ No newline at end of file diff --git a/reference/glossary.html b/reference/glossary.html new file mode 100644 index 000000000..035cc4bb9 --- /dev/null +++ b/reference/glossary.html @@ -0,0 +1,34 @@ + + + + + + Glossary | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Glossary

    Here you can find definitions of all Iroha-related entities.

    Blockchain ledgers

    Blockchain ledgers are digital record-keeping systems that use blockchain technology to keep financial records. These are named after old-fashioned books that were used for financial records such as prices, news, and transaction information.

    During medieval times, ledger books were open for public viewing and accuracy verification. This idea is reflected in the blockchain-based systems that can check the stored data for validity.

    Peer

    A peer in Iroha means an Iroha process instance to which other Iroha processes and client applications can connect. A single machine can host several Iroha peers. Peers are equal regarding their resources and capabilities, with an important exception: only one of the peers runs the genesis block at the bootstrapping stage of the Iroha network.

    Other blockchains may refer to the same concept as a node or a validator.

    A peer can be a process on its host system. It also can be contained in a Docker container and a Kubernetes pod.

    Asset

    In the context of blockchains, an asset is the representation of a valuable object on the blockchain.

    Additional information on assets is available here.

    Fungible assets

    Such assets can be easily swapped for other assets of the same type because they are interchangeable.

    As an example, all units of the same currency are equal in their value and can be used to purchase goods. Typically, fungible assets are identical in appearance, aside from the wear of banknotes and coins.

    Non-fungible assets

    Non-fungible assets are unique and valuable due to their specific characteristics and rarity; their value cannot be compared to other assets.

    • A painting's value can vary based on the artist, the time period it was painted, and the public's interest in it.
    • Two houses on the same street may have differing levels of maintenance.
    • Jewellery manufacturers typically offer a range of different designs.

    Mintable assets

    An asset is mintable if more of the same type can be issued.

    Non-mintable assets

    If the initial amount of an asset is specified once and doesn't change, it is considered non-mintable.

    The Genesis block sets this information for the Iroha configuration.

    Byzantine fault-tolerance (BFT)

    The property of being able to properly function with a network containing a certain percentage of malicious actors. Iroha is capable of functioning with up to 33% malicious actors in its peer-to-peer network.

    Iroha Components

    Rust modules containing Iroha functionality.

    Sumeragi (Emperor)

    The Iroha module responsible for consensus.

    Torii (Gate)

    Module with the incoming request handling logic for the peer. It is used to receive, accept and route incoming instructions, and HTTP queries, as well as run-time configuration updates.

    Kura (Warehouse)

    Persistence-related logic. It handles storing the blocks, log rotation, block storage folder rotation, etc.

    Kagami(Teacher and Exemplar and/or looking glass)

    Generator for commonly used data. It can generate cryptographic key pairs, genesis blocks, documentation, etc.

    Merkle tree (hash tree)

    A data structure used to validate and verify the state at each block height. Iroha's current implementation is a binary tree. See Wikipedia for more details.

    Smart contracts

    Smart contracts are blockchain-based programs that run when a specific set of conditions is met. In Iroha smart contracts are implemented using core Iroha special instructions.

    Triggers

    An event type that allows invoking an Iroha special instruction at specific block commit, time (with some caveats), etc. More on triggers here.

    Versioning

    Each request is labelled with the API version to which it belongs. It allows a combination of different binary versions of Iroha client/peer software to interoperate, which in turn allows software upgrades in the Iroha network.

    Hijiri (peer reputation system)

    Iroha's reputation system. It allows prioritising communication with peers that have a good track-record, and reducing the harm that can be caused by malicious peers.

    Iroha Modules

    Third party extensions to Iroha that provide custom functionality.

    Iroha Special Instructions (ISI)

    A library of smart contracts provided with Iroha. These can be invoked via either transactions or registered event listeners. More on ISI here.

    Utility Iroha Special Instructions

    This set of isi contains logical instructions like If, I/O related like Notify and compositions like Sequence. They are mostly used as custom instructions.

    Core Iroha Special Instructions

    Special instructions provided with every Iroha deployment. These include some domain-specific as well as utility instructions.

    Domain-specific Iroha Special Instructions

    Instructions related to domain-specific activities: assets, accounts, domains, peer management). These provide the tools necessary to make changes to the World State View in a secure and safe manner.

    Custom Iroha Special Instruction

    Instructions provided in Iroha Modules, by clients or 3rd parties. These can only be built using the Core Instructions. Forking and modifying the Iroha source code is not recommended, as special instructions not agreed-upon by peers in an Iroha deployment will be treated as faults, thus peers running a modified instance will have their access revoked.

    Iroha Query

    A request to read the World State View without modifying said view. More on queries here.

    View change

    A process that takes place in case of a failed attempt at consensus. Usually this entails the election of a new Leader.

    World state view (WSV)

    In-memory representation of the current blockchain state. This includes all currently loaded blocks, with all of their contents, as well as peers elected for the current epoch.

    Leader

    In an iroha network, a peer is selected randomly and granted the special privilege of forming the next block. This privilege can be revoked in networks that achieve Byzantine fault-torelance via view change.

    + + + + \ No newline at end of file diff --git a/reference/instructions.html b/reference/instructions.html new file mode 100644 index 000000000..06d1aec6a --- /dev/null +++ b/reference/instructions.html @@ -0,0 +1,34 @@ + + + + + + Iroha Special Instructions | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Iroha Special Instructions

    The following instructions are supported in Iroha 2:

    InstructionDescriptions
    Register/UnregisterGive an ID to a new entity on the blockchain.
    Mint/BurnMint/burn assets, triggers, or permission tokens.
    SetKeyValue/RemoveKeyValueUpdate blockchain object metadata.
    NewParameter/SetParameterCreate/set a chain-wide config parameter.
    Grant/RevokeGive or remove certain permissions from accounts.
    TransferTransfer assets between accounts.
    ExecuteTriggerExecute triggers.
    If, Pair, SequenceUse to create composite instructions.
    Diagram: Iroha Special Instructions
    + + + + \ No newline at end of file diff --git a/reference/naming.html b/reference/naming.html new file mode 100644 index 000000000..71d8b3041 --- /dev/null +++ b/reference/naming.html @@ -0,0 +1,34 @@ + + + + + + Naming Conventions | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Naming Conventions

    When you are naming accounts, domains, or assets, you have to keep in mind the following conventions used in Iroha 2:

    1. There is a number of reserved characters that are used for specific types of constructs:

      • @ is reserved for account@domain constructs
      • # is reserved for asset#domain constructs
      • $ is reserved for trigger$domain constructs
      • % is reserved for validator%account constructs
    2. The maximum number of characters (including UTF-8 characters) a name can have is limited by two factors: [0, u32::MAX] and the currently allocated stack space.

    + + + + \ No newline at end of file diff --git a/reference/permissions.html b/reference/permissions.html new file mode 100644 index 000000000..9079bcd18 --- /dev/null +++ b/reference/permissions.html @@ -0,0 +1,384 @@ + + + + + + Permissions | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Permissions

    This section provides details about pre-configured permission tokens in Iroha 2. For more general information on permission tokens and permission groups (roles), refer to the Permissions chapter in Guide.

    Permission Tokens

    The following permission tokens are pre-configured in Iroha 2:

    Permission TokenCategoryOperation
    CanSetKeyValueInUserMetadataAccountSet key value
    CanRemoveKeyValueInUserMetadataAccountRemove key value
    CanBurnUserAssetsAssetBurn
    CanSetKeyValueInUserAssetsAssetSet key value
    CanRemoveKeyValueInUserAssetsAssetRemove key value
    CanTransferUserAssetsAssetTransfer
    CanTransferOnlyFixedNumberOfTimesPerPeriodAssetTransfer
    CanMintUserAssetDefinitionsAsset DefinitionMint
    CanBurnAssetWithDefinitionAsset DefinitionBurn
    CanUnregisterAssetWithDefinitionAsset DefinitionUnregister
    CanSetKeyValueInAssetDefinitionAsset DefinitionSet key value
    CanRemoveKeyValueInAssetDefinitionAsset DefinitionRemove key value
    CanRegisterDomainsDomainRegister

    INFO

    The way permission work in Iroha 2 is subject to change. Note that there won't be pre-configured permissions in the future.

    CanMintUserAssetDefinitions

    With CanMintUserAssetDefinitions, a user can register and mint assets with the corresponding asset definition.

    rust
    let mut genesis = RawGenesisBlock::new(
    +    "alice".parse(),
    +    "wonderland".parse(),
    +    get_key_pair().public_key().clone(),
    +);
    +let rose_definition_id =
    +    AssetDefinitionId::from_str("rose#wonderland")?;
    +let alice_id =
    +    AccountId::from_str("alice@wonderland")?;
    +
    +// Create a new `CanMintUserAssetDefinitions` permission token
    +// to mint rose assets (`rose_definition_id`)
    +let mint_rose_permission: PermissionToken =
    +    CanMintUserAssetDefinitions::new(rose_definition_id).into();
    +
    +// Grant Alice permission to mint rose assets
    +genesis.transactions[0]
    +    .isi
    +    .push(GrantBox::new(mint_rose_permission, alice_id).into());
    let mut genesis = RawGenesisBlock::new(
    +    "alice".parse(),
    +    "wonderland".parse(),
    +    get_key_pair().public_key().clone(),
    +);
    +let rose_definition_id =
    +    AssetDefinitionId::from_str("rose#wonderland")?;
    +let alice_id =
    +    AccountId::from_str("alice@wonderland")?;
    +
    +// Create a new `CanMintUserAssetDefinitions` permission token
    +// to mint rose assets (`rose_definition_id`)
    +let mint_rose_permission: PermissionToken =
    +    CanMintUserAssetDefinitions::new(rose_definition_id).into();
    +
    +// Grant Alice permission to mint rose assets
    +genesis.transactions[0]
    +    .isi
    +    .push(GrantBox::new(mint_rose_permission, alice_id).into());

    CanBurnAssetWithDefinition

    With CanBurnAssetWithDefinition permission token, a user can burn and unregister assets with the corresponding asset definition.

    rust
    let mut genesis = RawGenesisBlock::new(
    +    "alice".parse(),
    +    "wonderland".parse(),
    +    get_key_pair().public_key().clone(),
    +);
    +let rose_definition_id =
    +    AssetDefinitionId::from_str("rose#wonderland")?;
    +let alice_id =
    +    AccountId::from_str("alice@wonderland")?;
    +
    +// Create a new `CanBurnAssetWithDefinition` permission token
    +// to burn rose assets (`rose_definition_id`)
    +let burn_rose_permission: PermissionToken =
    +    CanBurnAssetWithDefinition::new(rose_definition_id).into();
    +
    +// Grant Alice permission to burn rose assets
    +genesis.transactions[0]
    +    .isi
    +    .push(GrantBox::new(burn_rose_permission, alice_id).into());
    let mut genesis = RawGenesisBlock::new(
    +    "alice".parse(),
    +    "wonderland".parse(),
    +    get_key_pair().public_key().clone(),
    +);
    +let rose_definition_id =
    +    AssetDefinitionId::from_str("rose#wonderland")?;
    +let alice_id =
    +    AccountId::from_str("alice@wonderland")?;
    +
    +// Create a new `CanBurnAssetWithDefinition` permission token
    +// to burn rose assets (`rose_definition_id`)
    +let burn_rose_permission: PermissionToken =
    +    CanBurnAssetWithDefinition::new(rose_definition_id).into();
    +
    +// Grant Alice permission to burn rose assets
    +genesis.transactions[0]
    +    .isi
    +    .push(GrantBox::new(burn_rose_permission, alice_id).into());

    CanBurnUserAssets

    With CanBurnUserAssets permission token, a user can burn the specified asset.

    rust
    let alice_id = AccountId::from_str("alice@test")?;
    +let bob_id = AccountId::from_str("bob@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanBurnUserAssets` permission token
    +// that allows burning `alice_xor_id` asset
    +let permission_token_to_alice: PermissionToken =
    +    burn::CanBurnUserAssets::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission to burn `alice_xor_id` asset
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_token_to_alice,
    +    IdBox::AccountId(bob_id),
    +));
    let alice_id = AccountId::from_str("alice@test")?;
    +let bob_id = AccountId::from_str("bob@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanBurnUserAssets` permission token
    +// that allows burning `alice_xor_id` asset
    +let permission_token_to_alice: PermissionToken =
    +    burn::CanBurnUserAssets::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission to burn `alice_xor_id` asset
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_token_to_alice,
    +    IdBox::AccountId(bob_id),
    +));

    CanUnregisterAssetWithDefinition

    With CanUnregisterAssetWithDefinition permission token, a user can unregister assets with the corresponding asset definition.

    rust
    let alice_id = AccountId::from_str("alice@test")?;
    +let bob_id = AccountId::from_str("bob@test")?;
    +let xor_id = AssetDefinitionId::from_str("xor#test")?;
    +
    +// Create a new `CanUnregisterAssetWithDefinition` permission token
    +// that allows unregistering `xor_id` asset
    +let permission_token_to_alice: PermissionToken =
    +    unregister::CanUnregisterAssetWithDefinition::new(xor_id).into();
    +
    +// Create an instruction that grants Bob permission to unregister `xor_id` asset
    +let grant = Instruction::Grant(GrantBox {
    +    permission_token_to_alice.into(),
    +    IdBox::AccountId(bob_id).into(),
    +});
    let alice_id = AccountId::from_str("alice@test")?;
    +let bob_id = AccountId::from_str("bob@test")?;
    +let xor_id = AssetDefinitionId::from_str("xor#test")?;
    +
    +// Create a new `CanUnregisterAssetWithDefinition` permission token
    +// that allows unregistering `xor_id` asset
    +let permission_token_to_alice: PermissionToken =
    +    unregister::CanUnregisterAssetWithDefinition::new(xor_id).into();
    +
    +// Create an instruction that grants Bob permission to unregister `xor_id` asset
    +let grant = Instruction::Grant(GrantBox {
    +    permission_token_to_alice.into(),
    +    IdBox::AccountId(bob_id).into(),
    +});

    CanTransferUserAssets

    With CanTransferUserAssets permission token, a user can transfer the specified asset.

    rust
    let alice_id = AccountId::from_str("alice@test")?;
    +let bob_id = AccountId::from_str("bob@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanTransferUserAssets` permission token
    +// that allows to transfer `alice_xor_id` asset
    +let permission_token_to_alice: PermissionToken =
    +    transfer::CanTransferUserAssets::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission to transfer `alice_xor_id` asset
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_token_to_alice,
    +    IdBox::AccountId(bob_id),
    +));
    let alice_id = AccountId::from_str("alice@test")?;
    +let bob_id = AccountId::from_str("bob@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanTransferUserAssets` permission token
    +// that allows to transfer `alice_xor_id` asset
    +let permission_token_to_alice: PermissionToken =
    +    transfer::CanTransferUserAssets::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission to transfer `alice_xor_id` asset
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_token_to_alice,
    +    IdBox::AccountId(bob_id),
    +));

    CanTransferOnlyFixedNumberOfTimesPerPeriod

    With CanTransferOnlyFixedNumberOfTimesPerPeriod permission token, a user can transfer assets only a fixed number of times per some time period.

    rust
    const PERIOD_MS: u64 = 5000;
    +const COUNT: u32 = 2;
    +
    +// Create a new `CanTransferOnlyFixedNumberOfTimesPerPeriod` permission token
    +// that limits the number of transfer to 2 per 5000 ms.
    +let permission_token =
    +    transfer::CanTransferOnlyFixedNumberOfTimesPerPeriod::new(PERIOD_MS.into(), COUNT);
    const PERIOD_MS: u64 = 5000;
    +const COUNT: u32 = 2;
    +
    +// Create a new `CanTransferOnlyFixedNumberOfTimesPerPeriod` permission token
    +// that limits the number of transfer to 2 per 5000 ms.
    +let permission_token =
    +    transfer::CanTransferOnlyFixedNumberOfTimesPerPeriod::new(PERIOD_MS.into(), COUNT);

    CanSetKeyValueInUserAssets

    With CanSetKeyValueInUserAssets permission token, a user can set key value in the specified asset.

    rust
    let bob_id = AccountId::from_str("bob@test")?;
    +let alice_id = AccountId::from_str("alice@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanSetKeyValueInUserAssets` permission token
    +// that allows to set key value in `alice_xor_id` asset
    +let permission_to_set_key_value_in_xor: PermissionToken =
    +    key_value::CanSetKeyValueInUserAssets::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission
    +// to set key value in `alice_xor_id` asset
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_to_set_key_value_in_xor,
    +    IdBox::AccountId(bob_id),
    +));
    let bob_id = AccountId::from_str("bob@test")?;
    +let alice_id = AccountId::from_str("alice@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanSetKeyValueInUserAssets` permission token
    +// that allows to set key value in `alice_xor_id` asset
    +let permission_to_set_key_value_in_xor: PermissionToken =
    +    key_value::CanSetKeyValueInUserAssets::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission
    +// to set key value in `alice_xor_id` asset
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_to_set_key_value_in_xor,
    +    IdBox::AccountId(bob_id),
    +));

    CanRemoveKeyValueInUserAssets

    With CanRemoveKeyValueInUserAssets permission token, a user can remove key value in the specified asset.

    rust
    let bob_id = AccountId::from_str("bob@test")?;
    +let alice_id = AccountId::from_str("alice@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanRemoveKeyValueInUserAssets` permission token
    +// that allows to remove key value from `alice_xor_id` asset
    +let permission_to_set_key_value_in_xor: PermissionToken =
    +    key_value::CanRemoveKeyValueInUserAssets::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission
    +// to remove key value from `alice_xor_id` asset
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_to_set_key_value_in_xor,
    +    IdBox::AccountId(bob_id),
    let bob_id = AccountId::from_str("bob@test")?;
    +let alice_id = AccountId::from_str("alice@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanRemoveKeyValueInUserAssets` permission token
    +// that allows to remove key value from `alice_xor_id` asset
    +let permission_to_set_key_value_in_xor: PermissionToken =
    +    key_value::CanRemoveKeyValueInUserAssets::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission
    +// to remove key value from `alice_xor_id` asset
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_to_set_key_value_in_xor,
    +    IdBox::AccountId(bob_id),

    CanSetKeyValueInUserMetadata

    With CanSetKeyValueInUserMetadata permission token, a user can set a key value pair in the metadata for the specified account.

    rust
    let mouse_id = AccountId::from_str("mouse@wonderland")?;
    +
    +// Create a new `CanSetKeyValueInUserMetadata` token that, when granted to another account,
    +// allows it to set key value to the metadata in Mouse's account
    +let permission_to_set_key_value_in_mouse_metadata: PermissionToken =
    +    key_value::CanSetKeyValueInUserMetadata::new(mouse_id).into();
    let mouse_id = AccountId::from_str("mouse@wonderland")?;
    +
    +// Create a new `CanSetKeyValueInUserMetadata` token that, when granted to another account,
    +// allows it to set key value to the metadata in Mouse's account
    +let permission_to_set_key_value_in_mouse_metadata: PermissionToken =
    +    key_value::CanSetKeyValueInUserMetadata::new(mouse_id).into();

    CanRemoveKeyValueInUserMetadata

    With CanRemoveKeyValueInUserMetadata permission token, a user can remove a key value pair in the metadata for the specified account.

    rust
    let mouse_id = AccountId::from_str("mouse@wonderland")?;
    +
    +// Create a new `CanRemoveKeyValueInUserMetadata` token that, when granted to another account,
    +// allows it to remove key value from the metadata in Mouse's account
    +let permission_to_remove_key_value_in_mouse_metadata: PermissionToken =
    +    key_value::CanRemoveKeyValueInUserMetadata::new(mouse_id).into();
    let mouse_id = AccountId::from_str("mouse@wonderland")?;
    +
    +// Create a new `CanRemoveKeyValueInUserMetadata` token that, when granted to another account,
    +// allows it to remove key value from the metadata in Mouse's account
    +let permission_to_remove_key_value_in_mouse_metadata: PermissionToken =
    +    key_value::CanRemoveKeyValueInUserMetadata::new(mouse_id).into();

    CanSetKeyValueInAssetDefinition

    With CanSetKeyValueInAssetDefinition permission token, a user can set key value in the corresponding asset definition.

    rust
    let bob_id = AccountId::from_str("bob@test")?;
    +let alice_id = AccountId::from_str("alice@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanSetKeyValueInAssetDefinition` permission token
    +// that allows to set key value in `alice_xor_id` asset definition
    +let permission_to_set_key_value_in_xor: PermissionToken =
    +    key_value::CanSetKeyValueInAssetDefinition::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission
    +// to set key value in `alice_xor_id` asset definition
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_to_set_key_value_in_xor,
    +    IdBox::AccountId(bob_id),
    +));
    let bob_id = AccountId::from_str("bob@test")?;
    +let alice_id = AccountId::from_str("alice@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanSetKeyValueInAssetDefinition` permission token
    +// that allows to set key value in `alice_xor_id` asset definition
    +let permission_to_set_key_value_in_xor: PermissionToken =
    +    key_value::CanSetKeyValueInAssetDefinition::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission
    +// to set key value in `alice_xor_id` asset definition
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_to_set_key_value_in_xor,
    +    IdBox::AccountId(bob_id),
    +));

    CanRemoveKeyValueInAssetDefinition

    With CanRemoveKeyValueInAssetDefinition permission token, a user can remove key value in the corresponding asset definition.

    rust
    let bob_id = AccountId::from_str("bob@test")?;
    +let alice_id = AccountId::from_str("alice@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanRemoveKeyValueInAssetDefinition` permission token
    +// that allows to remove key value from `alice_xor_id` asset definition
    +let permission_to_remove_key_value_from_xor: PermissionToken =
    +    key_value::CanRemoveKeyValueInAssetDefinition::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission
    +// to remove key value from `alice_xor_id` asset definition
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_to_remove_key_value_from_xor,
    +    IdBox::AccountId(bob_id),
    +));
    let bob_id = AccountId::from_str("bob@test")?;
    +let alice_id = AccountId::from_str("alice@test")?;
    +let alice_xor_id = AssetId::new(
    +    AssetDefinitionId::from_str("xor#test")?,
    +    AccountId::from_str("alice@test")?,
    +);
    +
    +// Create a new `CanRemoveKeyValueInAssetDefinition` permission token
    +// that allows to remove key value from `alice_xor_id` asset definition
    +let permission_to_remove_key_value_from_xor: PermissionToken =
    +    key_value::CanRemoveKeyValueInAssetDefinition::new(alice_xor_id).into();
    +
    +// Create an instruction that grants Bob permission
    +// to remove key value from `alice_xor_id` asset definition
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_to_remove_key_value_from_xor,
    +    IdBox::AccountId(bob_id),
    +));

    CanRegisterDomains

    With CanRegisterDomains permission token, a user can register domains.

    rust
    let alice_id = AccountId::from_str("alice@test0")?;
    +let mut alice = Account::new(alice_id, []).build();
    +
    +// Create a new `CanRegisterDomains` permission token
    +// that allows registering new domains
    +let permission_token_to_register_domains: PermissionToken =
    +    register::CanRegisterDomains::new().into();
    +
    +// Create an instruction that grants Alice permission to register new domains
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_token_to_register_domains,
    +    IdBox::AccountId(alice_id),
    let alice_id = AccountId::from_str("alice@test0")?;
    +let mut alice = Account::new(alice_id, []).build();
    +
    +// Create a new `CanRegisterDomains` permission token
    +// that allows registering new domains
    +let permission_token_to_register_domains: PermissionToken =
    +    register::CanRegisterDomains::new().into();
    +
    +// Create an instruction that grants Alice permission to register new domains
    +let grant = Instruction::Grant(GrantBox::new(
    +    permission_token_to_register_domains,
    +    IdBox::AccountId(alice_id),
    + + + + \ No newline at end of file diff --git a/reference/queries.html b/reference/queries.html new file mode 100644 index 000000000..1f33c0523 --- /dev/null +++ b/reference/queries.html @@ -0,0 +1,62 @@ + + + + + + Queries | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Queries

    In the following section we mirror the module structure of the queries and present to you what they do. You can learn

    INFO

    The results of queries can be sorted, paginated and filtered peer-side all at once.

    Conventions

    Expand to learn about the conventions used in the descriptions below
    1. In the Details section of each query, we use gets, returns, searches with the following precise meanings:

      NotationMeaning
      getsThe query already has the data readily available and the data is trivial. Use these queries at will.
      returns or findsThe query has the data readily available, just as with gets, but the data is not trivial. You can still use these queries, but be mindful of the performance impact.
      searchesFor this query, the data must be actively collected and neither the return type nor the collection process is cheap. Use with great care.
    2. The queries are provided with just one data type as input, and parameterised by the type of the output.

    3. For the FindZByXAndY queries, their descriptions have a Parameters and a Returns section. The parameters can either be single or multiple types, while the output is almost always either one type, or a Vec<Type> kind of construction:

      NotationMeaning
      Parameters: (X, Y)In Rust source code, you need to construct the query as let query = FindZByXAndY::new(x: X, y: Y);, where x and y are variables of type X and Y respectively. In the reference below we provide you with information about each type.
      Returns: Vec<Z>The return value is a collection of more than one element of type Z. Depending on the SDK implementation this can be a type native to the language (e.g. JavaScript) or a thin wrapper around the Rust Vec structure.

    Role

    An optional feature. By default, it is present on all Iroha 2 deployments when they're compiled in the private blockchain configuration.

    You can learn more about roles in a dedicated section.

    FindAllRoles

    • Returns: Vec<Roles>

    • Details: Returns all roles registered as global (as opposed to domain-scoped) in the blockchain.

    FindAllRoleIds

    • Returns: Vec<Roles>

    • Details: Returns IDs of all the roles registered as global (as opposed to domain-scoped) in the blockchain.

      Note that it does not return its values, which contain permission tokens, only IDs.

    FindRoleByRoleId

    • Parameters: RoleId

    • Returns: Vec<Roles>

    • Details: Returns the role that has the provided role ID.

      For example, given the name of the role admin, it will return all of the admin-level permission tokens.

    FindRolesByAccountId

    • Parameters: AccountId

    • Returns: Vec<RoleId>

    • Details: Returns all of the role IDs that are attached to the given account.

      Note that unlike FindAllRoles, it does not return the roles themselves.

    Permission

    A semi-optional feature. You have permissions in both public and private blockchains but the use cases are different:

    • In a public blockchain, most accounts have the same common-sense permissions.
    • In a private blockchain, most accounts are assumed not to be able to do anything outside of their own account or domain unless explicitly granted said permission.

    We talk about permissions in more detail in a dedicated chapter.

    FindAllPermissionTokenDefinitions

    • Returns: Vec<PermissionTokenDefinition>

    • Details: Finds all registered permission token definitions.

    FindPermissionTokensByAccountId

    • Parameters: AccountId

    • Returns: Vec<PermissionToken>

    • Details: Returns all of the permission tokens granted to the specified account.

    Account

    Most queries in Iroha pertain to accounts. At the moment this is the most diverse set of queries.

    FindAllAccounts

    • Returns: Vec<Account>

    • Details: Finds all accounts registered globally in the blockchain.

    FindAccountById

    • Parameters: AccountId

    • Returns: Account

    • Details: Returns the full account information corresponding to the given AccountId.

    FindAccountKeyValueByIdAndKey

    • Parameters: (AccountId, Name)

    • Returns: Value

    • Details: Returns the value keyed by the provided Name for the given account.

      This is done by querying the metadata attached to the given account.

    FindAccountsByName

    • Parameters: Name

    • Returns: Vec<Account>

    • Details: Returns all of the accounts that have the given Name.

      This is particularly useful if you remember the name of the account, but do not, for example, recall the domain name in which it was registered.

    FindAccountsByDomainId

    • Parameters: DomainId

    • Returns: Vec<Account>

    • Details: Returns all accounts that belong to a specific domain.

      Note that this returns the full accounts and not the AccountId collection.

    FindAccountsWithAsset

    • Parameters: AccountId

    • Returns: Vec<Account>

    • Details: Returns all accounts that have the given asset.

    Asset

    Assets include simple numbers, but also a special type of key-to-value map that is used as a secure data storage for privileged information.

    FindAllAssets

    • Returns: Vec<Asset>

    • Details: Returns all known assets by value.

      INFO

      You should note that this is not the same as AssetDefinition. If you have one asset called e.g. tea#wonderland that belongs to every account on the blockchain, you will receive the aggregated value across all accounts, but not the information such as the type of the asset.

    FindAllAssetDefinitions

    • Returns: Vec<AssetDefinition>

    • Details: Returns all known asset definitions by value.

      TIP

      To reduce the load on the network, we store the definition of an asset separate from its instances. So if you want to know if an asset is mintable or what type is stored in it, you need to query the asset definition, rather than the asset itself.

    FindAssetById

    • Parameters: AssetId

    • Returns: Asset

    • Details: Returns the aggregated data about the asset usage across the network.

    FindAssetsByName

    • Parameters: Name

    • Returns: Vec<Asset>

    • Details: Searches the network for all assets that match the given name.

    FindAssetsByAccountId

    • Parameters: AccountId

    • Returns: Vec<Asset>

    • Details: Returns all of the assets that belong to a given account.

    FindAssetsByAssetDefinitionId

    • Parameters: AssetDefinitionId

    • Returns: Vec<Asset>

    • Details: Searches for all of the assets that have the given definition ID.

    FindAssetsByDomainId

    • Parameters: DomainId

    • Returns: Vec<Asset>

    • Details: Returns all assets that are registered in the given domain.

    FindAssetsByDomainIdAndAssetDefinitionId

    • Parameters: (DomainId, AssetDefinitionId)

    • Returns: Vec<Asset>

    • Details: Searches the domain for assets that have the given definition ID.

    FindAssetQuantityById

    • Parameters: AssetId

    • Returns: NumericValue

    • Details: Returns the asset quantity.

      Note that this query assumes that the asset given by the identifier is of type AssetValue::Quantity.

      WARNING

      This query can fail.

    FindAssetKeyValueByIdAndKey

    • Parameters: (AssetId, Name)

    • Returns: Value

    • Details: Gets the value keyed by the given name in the metadata of the asset corresponding to the given identifier.

    FindAssetDefinitionKeyValueByIdAndKey

    • Parameters: (AssetDefinitionId, Name)

    • Returns: Value

    • Details: Gets the value keyed by the given name in the metadata of the asset definition corresponding to the given identifier.

    FindTotalAssetQuantityByAssetDefinitionId

    • Parameters: AssetDefinitionId

    • Returns: NumericValue

    • Details: Finds the total asset quantity for the given asset definition. For the Store asset value, finds the sum of asset quantities through all accounts that hold the specified asset.

    Block

    FindAllBlocks

    • Returns: Vec<VersionedCommittedBlock>
    • Details: Returns all blocks in the blockchain.

    FindAllBlockHeaders

    • Returns: Vec<BlockHeader>
    • Details: Returns all block headers for blocks in the blockchain.

    FindBlockHeaderByHash

    • Parameters: Hash
    • Returns: BlockHeader
    • Details: Gets the block header that matches the hash that was provided.

    Domain

    The domain is the basic unit of organisation in an Iroha blockchain. Accounts and assets must be registered inside a domain, triggers are usually scoped by domain, and most queries have the domain as a possible input.

    FindAllDomains

    • Returns: Vec<Domain>

    • Details: Returns all of the known registered domains.

      WARNING

      This query returns the full contents of the world state view as of execution. This query should be used sparingly and for debugging purposes only.

    FindDomainById

    • Parameters: DomainId

    • Returns: Domain

    • Details: Gets the domain corresponding to the given identifier.

    FindDomainKeyValueByIdAndKey

    • Parameters: (DomainId, Name)

    • Returns: Value

    • Details: Returns the value keyed by the given name in the domain corresponding to the given identifier.

    Peer

    A peer is the basic unit of storage and validation. In common parlance we may conflate the node and the peer binary running on the node, but in this case we specifically mean the peer binary as a server with its specific configuration.

    FindAllPeers

    • Returns: Vec<Peer>

    • Details: Returns all known peers identified by their key and accompanied by the address of the API endpoint of each.

    FindAllParameters

    • Returns: Vec<Parameter>

      rust
      pub enum Parameter {
      +    /// Maximum amount of Faulty Peers in the system.
      +    MaximumFaultyPeersAmount(u32),
      +    /// Maximum time for a leader to create a block.
      +    BlockTime(u128),
      +    /// Maximum time for a proxy tail to send commit message.
      +    CommitTime(u128),
      +    /// Time to wait for a transaction Receipt.
      +    TransactionReceiptTime(u128),
      +}
      pub enum Parameter {
      +    /// Maximum amount of Faulty Peers in the system.
      +    MaximumFaultyPeersAmount(u32),
      +    /// Maximum time for a leader to create a block.
      +    BlockTime(u128),
      +    /// Maximum time for a proxy tail to send commit message.
      +    CommitTime(u128),
      +    /// Time to wait for a transaction Receipt.
      +    TransactionReceiptTime(u128),
      +}
    • Details: Returns the parameters used by all peers in the network.

      This is useful for debugging if any of the peers are incorrectly configured and causing view changes.

    Transaction

    It is often necessary to query the state of specific transactions, especially for use in blockchain explorers and for user-facing applications.

    FindTransactionsByAccountId

    • Parameters: AccountId

    • Returns: Vec<TransactionValue>

      rust
      pub enum TransactionValue {
      +    /// Committed transaction
      +    Transaction(Box<VersionedSignedTransaction>),
      +    /// Rejected transaction with reason of rejection
      +    RejectedTransaction(Box<VersionedRejectedTransaction>),
      +}
      pub enum TransactionValue {
      +    /// Committed transaction
      +    Transaction(Box<VersionedSignedTransaction>),
      +    /// Rejected transaction with reason of rejection
      +    RejectedTransaction(Box<VersionedRejectedTransaction>),
      +}
    • Details: Returns the full set of transactions that an account has submitted throughout the existence of the blockchain.

    FindTransactionByHash

    • Parameters: Hash

    • Returns: TransactionValue

    • Details: Returns the transaction by hash.

    Trigger

    Iroha is an event-driven architecture. Every modification of the world state emits a corresponding event that can be captured by appropriate event listeners called filters.

    INFO

    Note that Iroha shut downs all listeners on panic.

    FindAllActiveTriggerIds

    • Returns: Vec<TriggerId>

    • Details: Finds all currently active triggers, that is, triggers that have not expired at the time of the query.

    FindTriggerById

    • Parameters: TriggerId

    • Returns: Trigger

    • Details: Finds the trigger with the given ID.

    FindTriggerKeyValueByIdAndKey

    • Parameters: (TriggerId, Name)

    • Returns: Trigger

    • Details: Finds the value corresponding to the key in the metadata of the trigger with the given ID.

    FindTriggersByDomainId

    • Parameters: DomainId

    • Returns: Vec<Trigger>

    • Details: Finds all domain triggers for the given domain ID.

    + + + + \ No newline at end of file diff --git a/reference/specification.html b/reference/specification.html new file mode 100644 index 000000000..211a08b28 --- /dev/null +++ b/reference/specification.html @@ -0,0 +1,34 @@ + + + + + + API Specification | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    API Specification

    INFO

    This page contains a copy of api_spec.md from hyperledger/iroha#iroha2-dev. You can read the most up-to-date version of it on GitHub.

    Please note this page was last updated on 11/21/2023, 10:47:05 AM.

    + + + + \ No newline at end of file diff --git a/reference/torii-endpoints.html b/reference/torii-endpoints.html new file mode 100644 index 000000000..79068d5c3 --- /dev/null +++ b/reference/torii-endpoints.html @@ -0,0 +1,180 @@ + + + + + + Torii Endpoints | Hyperledger Iroha 2 Tutorial + + + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    Torii Endpoints

    API Version

    • Protocol: HTTP
    • Method: GET
    • Endpoint: /api_version

    Responses:

    200 OK: The current version of API used by Iroha returned as a JSON string. Grabbed from the genesis block's version, so at least a minimal subnet of 4 peers should be running and the genesis be submitted at the time of request.

    json
    "1"
    "1"

    Blocks Stream

    • Protocol: HTTP
    • Protocol Upgrade:`WebSocket
    • Endpoint: /block/stream

    The client should send a BlockSubscriptionRequest to initiate communication after the WebSocket handshake. Then the server sends a BlockMessage. Messages are SCALE-encoded[1].

    Via this endpoint, the client first provides the starting block number (i.e. height) in the subscription request. After sending the confirmation message, the server starts streaming all the blocks from the given block number up to the current block and continues to stream blocks as they are added to the blockchain.

    Events

    • Protocol: HTTP
    • Protocol Upgrade: WebSocket
    • Endpoint: /events

    After a handshake, the client should send an EventSubscriptionRequest. Then the server sends an EventMessage. Messages are SCALE-encoded[1:1].

    Transaction Events

    Transaction event statuses can be either Validating, Committed or Rejected.

    Transaction statuses proceed from Validating to either Committed or Rejected. However, due to the distributed nature of the network, some peers might receive events out of order (e.g. Committed before Validating).

    Some peers in the network may be offline for the validation round. If the client connects to them while they are offline, the peers might not respond with the Validating status. But when the offline peers come back online they will synchronize the blocks. They are then guaranteed to respond with the Committed (or Rejected) status depending on the information found in the block.

    Health

    • Protocol: HTTP
    • Method: GET
    • Endpoint: /health

    Responses with 200 OK and a current status of peer as a JSON string:

    json
    "Healthy"
    "Healthy"

    Metrics

    • Protocol: HTTP
    • Method: GET
    • Endpoint: /metrics

    Responses:

    200 OK reports 8 of 10 metrics:

    Sample Prometheus metrics
    bash
    # HELP accounts User accounts registered at this time
    +# TYPE accounts gauge
    +accounts{domain="genesis"} 1
    +accounts{domain="wonderland"} 1
    +# HELP block_height Current block height
    +# TYPE block_height counter
    +block_height 1
    +# HELP connected_peers Total number of currently connected peers
    +# TYPE connected_peers gauge
    +connected_peers 0
    +# HELP domains Total number of domains
    +# TYPE domains gauge
    +domains 2
    +# HELP tx_amount average amount involved in a transaction on this peer
    +# TYPE tx_amount histogram
    +tx_amount_bucket{le="0.005"} 0
    +tx_amount_bucket{le="0.01"} 0
    +tx_amount_bucket{le="0.025"} 0
    +tx_amount_bucket{le="0.05"} 0
    +tx_amount_bucket{le="0.1"} 0
    +tx_amount_bucket{le="0.25"} 0
    +tx_amount_bucket{le="0.5"} 0
    +tx_amount_bucket{le="1"} 0
    +tx_amount_bucket{le="2.5"} 0
    +tx_amount_bucket{le="5"} 0
    +tx_amount_bucket{le="10"} 0
    +tx_amount_bucket{le="+Inf"} 2
    +tx_amount_sum 26
    +tx_amount_count 2
    +# HELP txs Transactions committed
    +# TYPE txs counter
    +txs{type="accepted"} 1
    +txs{type="rejected"} 0
    +txs{type="total"} 1
    +# HELP uptime_since_genesis_ms Network up-time, from creation of the genesis block
    +# TYPE uptime_since_genesis_ms gauge
    +uptime_since_genesis_ms 54572974
    +# HELP view_changes Number of view_changes in the current round
    +# TYPE view_changes gauge
    +view_changes 0
    # HELP accounts User accounts registered at this time
    +# TYPE accounts gauge
    +accounts{domain="genesis"} 1
    +accounts{domain="wonderland"} 1
    +# HELP block_height Current block height
    +# TYPE block_height counter
    +block_height 1
    +# HELP connected_peers Total number of currently connected peers
    +# TYPE connected_peers gauge
    +connected_peers 0
    +# HELP domains Total number of domains
    +# TYPE domains gauge
    +domains 2
    +# HELP tx_amount average amount involved in a transaction on this peer
    +# TYPE tx_amount histogram
    +tx_amount_bucket{le="0.005"} 0
    +tx_amount_bucket{le="0.01"} 0
    +tx_amount_bucket{le="0.025"} 0
    +tx_amount_bucket{le="0.05"} 0
    +tx_amount_bucket{le="0.1"} 0
    +tx_amount_bucket{le="0.25"} 0
    +tx_amount_bucket{le="0.5"} 0
    +tx_amount_bucket{le="1"} 0
    +tx_amount_bucket{le="2.5"} 0
    +tx_amount_bucket{le="5"} 0
    +tx_amount_bucket{le="10"} 0
    +tx_amount_bucket{le="+Inf"} 2
    +tx_amount_sum 26
    +tx_amount_count 2
    +# HELP txs Transactions committed
    +# TYPE txs counter
    +txs{type="accepted"} 1
    +txs{type="rejected"} 0
    +txs{type="total"} 1
    +# HELP uptime_since_genesis_ms Network up-time, from creation of the genesis block
    +# TYPE uptime_since_genesis_ms gauge
    +uptime_since_genesis_ms 54572974
    +# HELP view_changes Number of view_changes in the current round
    +# TYPE view_changes gauge
    +view_changes 0

    Learn how to use metrics.

    Query

    • Protocol: HTTP
    • Method: POST
    • Endpoint: /query
    • Expects:
      • Body: SCALE-encoded[1:2] VersionedSignedQuery
      • Query parameters:
        • start: An optional parameter in queries where results can be indexed. Use to return results from a specified point. Results are ordered by id, which uses Rust's PartialOrd and Ord traits.
        • limit: An optional parameter in queries where results can be indexed. Use to return a specific number of results.
        • sort_by_metadata_key: An optional parameter in queries. Use to sort results containing metadata with a given key.
        • fetch_size: An optional parameter in queries. Use to specify the exact number of results returned by a query.

    Responses:

    ResponseStatusBody
    Success200VersionedBatchedResponse<Value>
    Conversion Error400QueryExecutionFail::Conversion(String)
    Evaluate Error400QueryExecutionFail::Evaluate(String)
    Signature Error401QueryExecutionFail::Signature(String)
    Permission Error403QueryExecutionFail::Permission(String)
    Find Error404QueryExecutionFail::Find(FindError)

    Account Not Found 404

    Whether each prerequisite object was found and FindError:

    DomainAccountFindError
    N-FindError::Domain(DomainId)
    YNFindError::Account(AccountId)

    Asset Not Found 404

    Whether each prerequisite object was found and FindError:

    DomainAccountAsset DefinitionAssetFindError
    N---FindError::Domain(DomainId)
    YN--FindError::Account(AccountId)
    Y-N-FindError::AssetDefinition(AssetDefinitionId)
    YYYNFindError::Asset(AssetId)

    Status

    • Protocol: HTTP
    • Method: GET
    • Endpoint: /status
    • Expects: an optional Accept: application/x-parity-scale request header to encode response with SCALE[1:3]. Otherwise, will fall back to application/json.
    • Responses: with Content-Type set either to application/json or application/x-parity-scale, and an accordingly encoded response body.

    Response body is of the following Status structure:

    rust
    struct Status {
    +    /// Number of connected peers, except for the reporting peer itself
    +    peers: u64,
    +    /// Number of committed blocks
    +    blocks: u64,
    +    /// Number of accepted transactions
    +    txs_accepted: u64,
    +    /// Number of rejected transactions
    +    txs_rejected: u64,
    +    /// Uptime since genesis block creation
    +    uptime: Uptime,
    +    /// Number of view changes in the current round
    +    view_changes: u64,
    +    /// Number of the transactions in the queue
    +    queue_size: u64,
    +}
    +
    +struct Uptime {
    +    secs: u64,
    +    nanos: u32
    +}
    struct Status {
    +    /// Number of connected peers, except for the reporting peer itself
    +    peers: u64,
    +    /// Number of committed blocks
    +    blocks: u64,
    +    /// Number of accepted transactions
    +    txs_accepted: u64,
    +    /// Number of rejected transactions
    +    txs_rejected: u64,
    +    /// Uptime since genesis block creation
    +    uptime: Uptime,
    +    /// Number of view changes in the current round
    +    view_changes: u64,
    +    /// Number of the transactions in the queue
    +    queue_size: u64,
    +}
    +
    +struct Uptime {
    +    secs: u64,
    +    nanos: u32
    +}

    JSON Precision Lost

    Almost all fields in the Status structure are 64-bit integers, and they are encoded in JSON as-is. Since native JSON's number type according to the specification effectively is f64, the precision might be lost on deserialization, for example, in JavaScript's JSON.parse. For more details, see related issue.

    Compact Form in SCALE

    Fields with type u64 serialized in the Compact form.

    Sample responses

    These samples represent the same data:

    json
    {
    +  "peers": 4,
    +  "blocks": 5,
    +  "txs_accepted": 31,
    +  "txs_rejected": 3,
    +  "uptime": {
    +    "secs": 5,
    +    "nanos": 937000000
    +  },
    +  "view_changes": 2,
    +  "queue_size": 18
    +}
    {
    +  "peers": 4,
    +  "blocks": 5,
    +  "txs_accepted": 31,
    +  "txs_rejected": 3,
    +  "uptime": {
    +    "secs": 5,
    +    "nanos": 937000000
    +  },
    +  "view_changes": 2,
    +  "queue_size": 18
    +}
    10 14 7C 0C 14 40 7C D9 37 08 48
    10 14 7C 0C 14 40 7C D9 37 08 48

    Sub-routing

    To obtain the value of a specific field, one can append the name of the field to the path, e.g. /status/peers. This returns the corresponding JSON value.

    json
    4
    4
    json
    {
    +  "secs": 5,
    +  "nanos": 937000000
    +}
    {
    +  "secs": 5,
    +  "nanos": 937000000
    +}
    json
    5
    5

    Transaction

    Responses:

    StatusDescription
    200Transaction Accepted (But not guaranteed to have passed consensus yet)
    400Transaction Rejected (Malformed)
    401Transaction Rejected (Improperly signed)

    1. For more information on Parity SCALE Codec check Substrate Dev Hub and codec's GitHub repository.

      ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
    + + + + \ No newline at end of file