Merge branch 'ido' into 'master'
ido See merge request riftenlabs/riftenlabs-indexer!81
This commit is contained in:
commit
d5fa393870
14 changed files with 4329 additions and 32 deletions
189
Cargo.lock
generated
189
Cargo.lock
generated
|
|
@ -408,6 +408,7 @@ dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-link 0.1.1",
|
"windows-link 0.1.1",
|
||||||
]
|
]
|
||||||
|
|
@ -549,6 +550,40 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
|
||||||
|
dependencies = [
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-encoding"
|
name = "data-encoding"
|
||||||
version = "2.10.0"
|
version = "2.10.0"
|
||||||
|
|
@ -568,11 +603,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.4.0"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -643,6 +679,12 @@ version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
|
|
@ -965,7 +1007,7 @@ dependencies = [
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|
@ -1024,6 +1066,9 @@ name = "hex"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex-conservative"
|
name = "hex-conservative"
|
||||||
|
|
@ -1330,6 +1375,12 @@ dependencies = [
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
|
@ -1351,6 +1402,17 @@ dependencies = [
|
||||||
"icu_properties",
|
"icu_properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
|
|
@ -1663,9 +1725,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
|
|
@ -2244,6 +2306,7 @@ dependencies = [
|
||||||
"rust_decimal_macros",
|
"rust_decimal_macros",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_with",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"stderrlog",
|
"stderrlog",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
@ -2306,7 +2369,7 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"figment",
|
"figment",
|
||||||
"futures",
|
"futures",
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"multer",
|
"multer",
|
||||||
|
|
@ -2338,7 +2401,7 @@ checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"devise",
|
"devise",
|
||||||
"glob",
|
"glob",
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rocket_http",
|
"rocket_http",
|
||||||
|
|
@ -2375,7 +2438,7 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"hyper 0.14.32",
|
"hyper 0.14.32",
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pear",
|
"pear",
|
||||||
|
|
@ -2527,6 +2590,30 @@ dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schemars"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
|
||||||
|
dependencies = [
|
||||||
|
"dyn-clone",
|
||||||
|
"ref-cast",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schemars"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc"
|
||||||
|
dependencies = [
|
||||||
|
"dyn-clone",
|
||||||
|
"ref-cast",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
@ -2567,18 +2654,28 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.228"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.219"
|
version = "1.0.228"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -2587,14 +2684,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.140"
|
version = "1.0.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ryu",
|
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2618,6 +2716,37 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "3.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"chrono",
|
||||||
|
"hex",
|
||||||
|
"indexmap 1.9.3",
|
||||||
|
"indexmap 2.9.0",
|
||||||
|
"schemars 0.9.0",
|
||||||
|
"schemars 1.2.1",
|
||||||
|
"serde_core",
|
||||||
|
"serde_json",
|
||||||
|
"serde_with_macros",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "3.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
|
@ -2758,7 +2887,7 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.15.3",
|
"hashbrown 0.15.3",
|
||||||
"hashlink",
|
"hashlink",
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
|
@ -2963,6 +3092,12 @@ dependencies = [
|
||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
|
|
@ -3071,30 +3206,30 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.41"
|
version = "0.3.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa",
|
"itoa",
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
"serde",
|
"serde_core",
|
||||||
"time-core",
|
"time-core",
|
||||||
"time-macros",
|
"time-macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-core"
|
name = "time-core"
|
||||||
version = "0.1.4"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.22"
|
version = "0.2.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
|
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"time-core",
|
"time-core",
|
||||||
|
|
@ -3224,7 +3359,7 @@ version = "0.22.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
|
|
@ -4070,3 +4205,9 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.101",
|
"syn 2.0.101",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ anyhow = { version = "1.0.94", features = ["backtrace"] }
|
||||||
bitcoincash = "0.32.2"
|
bitcoincash = "0.32.2"
|
||||||
bitcoin_hashes = { version = "0.14.100", default-features = false } # Same version as used by bitcoincash
|
bitcoin_hashes = { version = "0.14.100", default-features = false } # Same version as used by bitcoincash
|
||||||
electrum-client-netagnostic = { version = "0.21.2", features = ["use-rustls", "use-websocket"] }
|
electrum-client-netagnostic = { version = "0.21.2", features = ["use-rustls", "use-websocket"] }
|
||||||
hex = "0.4.3"
|
hex = { version = "0.4.3", features = ["serde"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.133"
|
serde_json = "1.0.133"
|
||||||
rocket = { version = "0.5.1", features = ["json"] }
|
rocket = { version = "0.5.1", features = ["json"] }
|
||||||
|
|
@ -33,6 +33,7 @@ reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-feature
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
configure_me = "0.4.0"
|
configure_me = "0.4.0"
|
||||||
futures = "0.3.32"
|
futures = "0.3.32"
|
||||||
|
serde_with = { version = "3.18.0", features = ["hex"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
configure_me_codegen = "0.4.8"
|
configure_me_codegen = "0.4.8"
|
||||||
|
|
|
||||||
|
|
@ -51,3 +51,15 @@ name = "moria_read_slots"
|
||||||
type = "u32"
|
type = "u32"
|
||||||
doc = "Number of read connection pool slots for the Moria lending database (default: 8)"
|
doc = "Number of read connection pool slots for the Moria lending database (default: 8)"
|
||||||
default = "8"
|
default = "8"
|
||||||
|
|
||||||
|
[[param]]
|
||||||
|
name = "ido_read_slots"
|
||||||
|
type = "u32"
|
||||||
|
doc = "Number of read connection pool slots for the Moria lending database (default: 8)"
|
||||||
|
default = "8"
|
||||||
|
|
||||||
|
[[param]]
|
||||||
|
name = "debug"
|
||||||
|
type = "bool"
|
||||||
|
doc = "Enable debug RPC endpoints (e.g. txchain inspection). Do not use in production."
|
||||||
|
default = "false"
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,9 @@ impl BlockUndoer for StoreBlockUndoer {
|
||||||
{
|
{
|
||||||
was_indexed = true;
|
was_indexed = true;
|
||||||
}
|
}
|
||||||
|
if db::ido::delete_entries(&self.db.ido_w, Some(&blockheader.block_hash())).await? > 0 {
|
||||||
|
was_indexed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if was_indexed {
|
if was_indexed {
|
||||||
config_set(
|
config_set(
|
||||||
|
|
|
||||||
3839
src/db/ido/mod.rs
Normal file
3839
src/db/ido/mod.rs
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -16,6 +16,7 @@ use crate::db::cauldron::config::check_db_version;
|
||||||
use crate::db::cauldron::prepare_tables as cauldron_prepare_tables;
|
use crate::db::cauldron::prepare_tables as cauldron_prepare_tables;
|
||||||
use crate::db::cauldron::tokentoken::create_table as tokentoken_prepare_tables;
|
use crate::db::cauldron::tokentoken::create_table as tokentoken_prepare_tables;
|
||||||
use crate::db::crc20::prepare_tables as crc20_prepare_tables;
|
use crate::db::crc20::prepare_tables as crc20_prepare_tables;
|
||||||
|
use crate::db::ido::prepare_tables as ido_prepare_tables;
|
||||||
use crate::db::moria::prepare_tables as moria_prepare_tables;
|
use crate::db::moria::prepare_tables as moria_prepare_tables;
|
||||||
use crate::db::oracle::prepare_tables as oracle_prepare_tables;
|
use crate::db::oracle::prepare_tables as oracle_prepare_tables;
|
||||||
|
|
||||||
|
|
@ -89,6 +90,7 @@ pub struct ReadSlots {
|
||||||
pub crc20: u32,
|
pub crc20: u32,
|
||||||
pub oracle: u32,
|
pub oracle: u32,
|
||||||
pub moria: u32,
|
pub moria: u32,
|
||||||
|
pub ido: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize all databases and return a DB struct
|
/// Initialize all databases and return a DB struct
|
||||||
|
|
@ -135,6 +137,13 @@ pub async fn initialize_databases(network: &str, read_slots: ReadSlots) -> Resul
|
||||||
moria_prepare_tables(&moria_db_write).await;
|
moria_prepare_tables(&moria_db_write).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize ido database
|
||||||
|
let (db_exists, ido_db_write, ido_db_read) =
|
||||||
|
create_db_pool(&db_path(db_dir, "ido.db"), read_slots.ido).await;
|
||||||
|
if !db_exists {
|
||||||
|
ido_prepare_tables(&ido_db_write).await;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(DB {
|
Ok(DB {
|
||||||
cauldron_w: cauldron_db_write,
|
cauldron_w: cauldron_db_write,
|
||||||
cauldron_r: cauldron_db_read,
|
cauldron_r: cauldron_db_read,
|
||||||
|
|
@ -146,5 +155,7 @@ pub async fn initialize_databases(network: &str, read_slots: ReadSlots) -> Resul
|
||||||
oracle_r: oracle_db_read,
|
oracle_r: oracle_db_read,
|
||||||
moria_w: moria_db_write,
|
moria_w: moria_db_write,
|
||||||
moria_r: moria_db_read,
|
moria_r: moria_db_read,
|
||||||
|
ido_r: ido_db_read,
|
||||||
|
ido_w: ido_db_write,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ pub mod bcmr;
|
||||||
pub mod blob;
|
pub mod blob;
|
||||||
pub mod cauldron;
|
pub mod cauldron;
|
||||||
pub mod crc20;
|
pub mod crc20;
|
||||||
|
pub mod ido;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
pub mod moria;
|
pub mod moria;
|
||||||
pub mod oracle;
|
pub mod oracle;
|
||||||
|
|
@ -35,4 +36,7 @@ pub struct DB {
|
||||||
// moria lending protocol
|
// moria lending protocol
|
||||||
pub moria_w: SqlitePool,
|
pub moria_w: SqlitePool,
|
||||||
pub moria_r: SqlitePool,
|
pub moria_r: SqlitePool,
|
||||||
|
// ido
|
||||||
|
pub ido_w: SqlitePool,
|
||||||
|
pub ido_r: SqlitePool,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ use riftenlabs_defi::{
|
||||||
};
|
};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
|
use crate::db::ido::{IDO_PREINIT_ANNOUNCEMENT_SIGNATURE, IDO_SIGNATURE};
|
||||||
|
|
||||||
/// Fetch blockchain tip from electrum server
|
/// Fetch blockchain tip from electrum server
|
||||||
pub fn electrum_get_tip(client: &Client) -> Result<(BlockHeader, u64)> {
|
pub fn electrum_get_tip(client: &Client) -> Result<(BlockHeader, u64)> {
|
||||||
let tip: Value =
|
let tip: Value =
|
||||||
|
|
@ -39,7 +41,9 @@ pub fn electrum_get_tip(client: &Client) -> Result<(BlockHeader, u64)> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch defi (cauldron + tokentoken) and oracle mempool transactions
|
/// Fetch defi (cauldron + tokentoken) and oracle mempool transactions
|
||||||
pub fn electrum_fetch_mempool(client: &Client) -> Result<(HashSet<Txid>, HashSet<Txid>)> {
|
pub fn electrum_fetch_mempool(
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<(HashSet<Txid>, HashSet<Txid>, HashSet<Txid>)> {
|
||||||
let cauldron_filter = json!({
|
let cauldron_filter = json!({
|
||||||
"scriptsig": hex::encode(&V2_CONTRACT_TEMPLATE[(V2_CONTRACT_TEMPLATE.len() - 43)..]), // cauldron spends
|
"scriptsig": hex::encode(&V2_CONTRACT_TEMPLATE[(V2_CONTRACT_TEMPLATE.len() - 43)..]), // cauldron spends
|
||||||
"scriptpubkey": hex::encode([0x6a /* op_return */, 0x06 /* push */, b'S', b'U', b'M', b'M', b'O', b'N']), // new pools (potentially)
|
"scriptpubkey": hex::encode([0x6a /* op_return */, 0x06 /* push */, b'S', b'U', b'M', b'M', b'O', b'N']), // new pools (potentially)
|
||||||
|
|
@ -72,6 +76,12 @@ pub fn electrum_fetch_mempool(client: &Client) -> Result<(HashSet<Txid>, HashSet
|
||||||
"scriptsig": hex::encode(DELPHI_V2_REDEEM_SCRIPT_BODY),
|
"scriptsig": hex::encode(DELPHI_V2_REDEEM_SCRIPT_BODY),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let ido_filter = json!({
|
||||||
|
"scriptsig": hex::encode(IDO_SIGNATURE), // ido state machine spends
|
||||||
|
"scriptpubkey": hex::encode(IDO_PREINIT_ANNOUNCEMENT_SIGNATURE), // preinit announcements (potentially)
|
||||||
|
"operation": "union"
|
||||||
|
});
|
||||||
|
|
||||||
let fetch_txs = |filter: Value| -> Result<HashSet<Txid>> {
|
let fetch_txs = |filter: Value| -> Result<HashSet<Txid>> {
|
||||||
let response = client.raw_call("mempool.get", [Param::Value(filter)])?;
|
let response = client.raw_call("mempool.get", [Param::Value(filter)])?;
|
||||||
|
|
||||||
|
|
@ -104,8 +114,9 @@ pub fn electrum_fetch_mempool(client: &Client) -> Result<(HashSet<Txid>, HashSet
|
||||||
defi_txs.extend(fetch_txs(tokentoken_filter)?);
|
defi_txs.extend(fetch_txs(tokentoken_filter)?);
|
||||||
let mut oracle_txs = fetch_txs(oracle_v1_filter)?;
|
let mut oracle_txs = fetch_txs(oracle_v1_filter)?;
|
||||||
oracle_txs.extend(fetch_txs(oracle_v2_filter)?);
|
oracle_txs.extend(fetch_txs(oracle_v2_filter)?);
|
||||||
|
let ido_txs = fetch_txs(ido_filter)?;
|
||||||
|
|
||||||
Ok((defi_txs, oracle_txs))
|
Ok((defi_txs, oracle_txs, ido_txs))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch blockchain tip from electrum server
|
/// Fetch blockchain tip from electrum server
|
||||||
|
|
|
||||||
54
src/index.rs
54
src/index.rs
|
|
@ -26,6 +26,7 @@ use crate::{
|
||||||
crc20::index_crc20,
|
crc20::index_crc20,
|
||||||
db::{
|
db::{
|
||||||
self,
|
self,
|
||||||
|
blob::ToBlob,
|
||||||
cauldron::{
|
cauldron::{
|
||||||
config::{config_get, config_set},
|
config::{config_get, config_set},
|
||||||
header::{db_get_header, store_headers},
|
header::{db_get_header, store_headers},
|
||||||
|
|
@ -67,18 +68,22 @@ fn moria_token_ids(network: Option<Network>) -> MoriaTokenIds {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_mempool(db: &DB, electrum: Arc<Mutex<Client>>) -> Result<()> {
|
pub async fn update_mempool(
|
||||||
|
db: &DB,
|
||||||
|
electrum: Arc<Mutex<Client>>,
|
||||||
|
network: Option<Network>,
|
||||||
|
) -> Result<()> {
|
||||||
let our_mempool_txs: HashSet<Txid> =
|
let our_mempool_txs: HashSet<Txid> =
|
||||||
db::cauldron::mempool::load_mempool(&db.cauldron_w).await?;
|
db::cauldron::mempool::load_mempool(&db.cauldron_w).await?;
|
||||||
|
|
||||||
let electrum_clone = electrum.clone();
|
let electrum_clone = electrum.clone();
|
||||||
let (defi_txs, oracle_txs) = tokio::task::spawn_blocking(move || {
|
let (cauldron_txs, oracle_txs, ido_txs) = tokio::task::spawn_blocking(move || {
|
||||||
electrum_fetch_mempool(&electrum_clone.lock().unwrap())
|
electrum_fetch_mempool(&electrum_clone.lock().unwrap())
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let txs_to_delete: Vec<Txid> = our_mempool_txs.difference(&defi_txs).cloned().collect();
|
let txs_to_delete: Vec<Txid> = our_mempool_txs.difference(&cauldron_txs).cloned().collect();
|
||||||
let txs_to_add: Vec<&Txid> = defi_txs.difference(&our_mempool_txs).collect();
|
let txs_to_add: Vec<&Txid> = cauldron_txs.difference(&our_mempool_txs).collect();
|
||||||
|
|
||||||
let electrum_for_fetch = electrum.clone();
|
let electrum_for_fetch = electrum.clone();
|
||||||
let txids_to_fetch: Vec<Txid> = txs_to_add.iter().map(|t| **t).collect();
|
let txids_to_fetch: Vec<Txid> = txs_to_add.iter().map(|t| **t).collect();
|
||||||
|
|
@ -182,6 +187,37 @@ pub async fn update_mempool(db: &DB, electrum: Arc<Mutex<Client>>) -> Result<()>
|
||||||
|
|
||||||
index_oracle(&db.oracle_w, &txs_to_add, &BlockHash::all_zeros()).await?;
|
index_oracle(&db.oracle_w, &txs_to_add, &BlockHash::all_zeros()).await?;
|
||||||
|
|
||||||
|
// ido updates
|
||||||
|
let ido_electrum = electrum.clone();
|
||||||
|
|
||||||
|
// filter txs already recorded in an ido's state chain
|
||||||
|
let mut ido_to_add = Vec::new();
|
||||||
|
for txid in ido_txs {
|
||||||
|
if !db::ido::has_indexed_tx(&db.ido_w, &txid.to_blob()).await? {
|
||||||
|
ido_to_add.push(txid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let txs_to_add: Vec<Transaction> = tokio::task::spawn_blocking(move || {
|
||||||
|
ido_to_add
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(
|
||||||
|
|txid| match electrum_get_tx(&ido_electrum.lock().unwrap(), &txid) {
|
||||||
|
Ok(tx) => Some(tx),
|
||||||
|
Err(e) => {
|
||||||
|
info!("Failed to get mempool tx {txid}: {e}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// an ido chain can have several unconfirmed txs in flight; index parents first
|
||||||
|
let txs_to_add = ttor_sorted_kahn(txs_to_add);
|
||||||
|
db::ido::index_txs(network, &db.ido_w, &txs_to_add, None).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,6 +299,14 @@ pub async fn index_blocks(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Before applying confirmed blocks, drop any mempool-indexed IDO state
|
||||||
|
// (blockhash IS NULL). A mempool tx that never confirmed — e.g. one replaced
|
||||||
|
// by a different on-chain tx — must not leave a stale snapshot that wins
|
||||||
|
// MAX(seq) or resurfaces after the reorg undo above. Confirmed blocks
|
||||||
|
// re-create rows for the txs they contain, and the next mempool pass
|
||||||
|
// (update_mempool) rebuilds the rest.
|
||||||
|
db::ido::delete_entries(&db.ido_w, None).await?;
|
||||||
|
|
||||||
let db_cpy = db.clone();
|
let db_cpy = db.clone();
|
||||||
let chain_cpy = chain.clone();
|
let chain_cpy = chain.clone();
|
||||||
let client_cpy = client.clone();
|
let client_cpy = client.clone();
|
||||||
|
|
@ -444,6 +488,8 @@ pub async fn index_blocks(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
db::ido::index_txs(network, &db.ido_w, &sorted_txs, Some(&blockhash)).await?;
|
||||||
|
|
||||||
let autheader_updates = if bcmr_enabled {
|
let autheader_updates = if bcmr_enabled {
|
||||||
let updates = index_bcmr(&db.bcmr_w, &blockhash, sorted_txs).await?;
|
let updates = index_bcmr(&db.bcmr_w, &blockhash, sorted_txs).await?;
|
||||||
updates as i64
|
updates as i64
|
||||||
|
|
|
||||||
12
src/main.rs
12
src/main.rs
|
|
@ -169,6 +169,7 @@ async fn start_program(
|
||||||
crc20: config.crc20_read_slots,
|
crc20: config.crc20_read_slots,
|
||||||
oracle: config.oracle_read_slots,
|
oracle: config.oracle_read_slots,
|
||||||
moria: config.moria_read_slots,
|
moria: config.moria_read_slots,
|
||||||
|
ido: config.ido_read_slots,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
@ -337,7 +338,7 @@ async fn start_program(
|
||||||
|
|
||||||
// Avoid overlapping writer while indexer is on
|
// Avoid overlapping writer while indexer is on
|
||||||
if !indexing_in_progress_clone.load(Ordering::Relaxed) {
|
if !indexing_in_progress_clone.load(Ordering::Relaxed) {
|
||||||
if let Err(e) = update_mempool(&db, client.clone()).await {
|
if let Err(e) = update_mempool(&db, client.clone(), Some(network)).await {
|
||||||
error!("Failed to update mempool: {e}");
|
error!("Failed to update mempool: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -634,6 +635,15 @@ async fn launch() -> _ {
|
||||||
rpc::tokentoken::list_tokens,
|
rpc::tokentoken::list_tokens,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.mount(
|
||||||
|
"/ido",
|
||||||
|
routes![
|
||||||
|
rpc::ido::list_idos,
|
||||||
|
rpc::ido::get_ido_by_id,
|
||||||
|
rpc::ido::get_ido_by_offering_token,
|
||||||
|
rpc::ido::list_ido_entries,
|
||||||
|
],
|
||||||
|
)
|
||||||
.mount("/", routes![rpc::health::health])
|
.mount("/", routes![rpc::health::health])
|
||||||
.attach(cors)
|
.attach(cors)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ pub enum ApiErrorCode {
|
||||||
// 404 Not Found codes
|
// 404 Not Found codes
|
||||||
PriceNotFound,
|
PriceNotFound,
|
||||||
PoolNotFound,
|
PoolNotFound,
|
||||||
|
IdoNotFound,
|
||||||
|
|
||||||
// 500 Internal Server Error codes
|
// 500 Internal Server Error codes
|
||||||
InternalError,
|
InternalError,
|
||||||
|
|
@ -61,6 +62,7 @@ impl fmt::Display for ApiErrorCode {
|
||||||
// 404 codes
|
// 404 codes
|
||||||
Self::PriceNotFound => "PRICE_NOT_FOUND",
|
Self::PriceNotFound => "PRICE_NOT_FOUND",
|
||||||
Self::PoolNotFound => "POOL_NOT_FOUND",
|
Self::PoolNotFound => "POOL_NOT_FOUND",
|
||||||
|
Self::IdoNotFound => "IDO_NOT_FOUND",
|
||||||
// 500 codes
|
// 500 codes
|
||||||
Self::InternalError => "INTERNAL_ERROR",
|
Self::InternalError => "INTERNAL_ERROR",
|
||||||
// 503 codes
|
// 503 codes
|
||||||
|
|
|
||||||
214
src/rpc/ido.rs
Normal file
214
src/rpc/ido.rs
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
// Copyright (C) 2024-2026 Whiterun LLC
|
||||||
|
//
|
||||||
|
// This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later.
|
||||||
|
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
|
||||||
|
use crate::db::blob::{blob_to_display_hex, display_hex_to_blob};
|
||||||
|
use crate::db::DB;
|
||||||
|
use crate::rpc::err::{bad_request, db_error, not_found, ApiErrorCode, CachedApiResult};
|
||||||
|
use crate::rpc::response::{cached_ok, CACHE_NONE};
|
||||||
|
use bitcoincash::{TokenID, Txid};
|
||||||
|
use rocket::{get, State};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
const LIST_DEFAULT_LIMIT: i64 = 20;
|
||||||
|
const LIST_MAX_LIMIT: i64 = 100;
|
||||||
|
|
||||||
|
/// Maximum number of comma-delimited owner_nfthash filters accepted per request.
|
||||||
|
const MAX_OWNER_NFTHASH_FILTERS: usize = 20;
|
||||||
|
|
||||||
|
/// Length in bytes of an owner_nfthash.
|
||||||
|
const OWNER_NFTHASH_LEN: usize = 32;
|
||||||
|
|
||||||
|
/// List IDOs with optional filters and pagination.
|
||||||
|
///
|
||||||
|
/// Filters:
|
||||||
|
/// - `is_valid`: boolean filter on the is_valid flag
|
||||||
|
/// - `status`: one of PREINIT, ACTIVE, DISTRIBUTING, DISTRIBUTED
|
||||||
|
/// - `offered_token_id`: 64-char hex token ID (reversed display format)
|
||||||
|
///
|
||||||
|
/// Pagination: `offset` (default 0), `limit` (default 20, max 100)
|
||||||
|
#[get("/list?<is_valid>&<status>&<offered_token_id>&<offset>&<limit>")]
|
||||||
|
pub async fn list_idos(
|
||||||
|
is_valid: Option<bool>,
|
||||||
|
status: Option<String>,
|
||||||
|
offered_token_id: Option<&str>,
|
||||||
|
offset: Option<i64>,
|
||||||
|
limit: Option<i64>,
|
||||||
|
db: &State<DB>,
|
||||||
|
) -> CachedApiResult<Value> {
|
||||||
|
let offset = offset.unwrap_or(0).max(0);
|
||||||
|
let limit = limit.unwrap_or(LIST_DEFAULT_LIMIT).clamp(1, LIST_MAX_LIMIT);
|
||||||
|
|
||||||
|
let offered_token_id_blob = offered_token_id
|
||||||
|
.map(|s| {
|
||||||
|
display_hex_to_blob::<TokenID>(s).map_err(|e| {
|
||||||
|
bad_request(
|
||||||
|
ApiErrorCode::InvalidParameters,
|
||||||
|
&format!("Invalid offered_token_id: {e}"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
let items = crate::db::ido::list_idos(
|
||||||
|
&db.ido_r,
|
||||||
|
is_valid,
|
||||||
|
status,
|
||||||
|
offered_token_id_blob,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(db_error)?;
|
||||||
|
|
||||||
|
Ok(cached_ok(serde_json::to_value(items).unwrap(), CACHE_NONE))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a single IDO by id token ID. Returns null if not found.
|
||||||
|
#[get("/by-id/<id>")]
|
||||||
|
pub async fn get_ido_by_id(id: &str, db: &State<DB>) -> CachedApiResult<Value> {
|
||||||
|
let blob = display_hex_to_blob::<TokenID>(id).map_err(|e| {
|
||||||
|
bad_request(
|
||||||
|
ApiErrorCode::InvalidParameters,
|
||||||
|
&format!("Invalid offering_token_id: {e}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let item = crate::db::ido::get_ido_by_preinit_txid(&db.ido_r, blob)
|
||||||
|
.await
|
||||||
|
.map_err(db_error)?;
|
||||||
|
|
||||||
|
Ok(cached_ok(serde_json::to_value(item).unwrap(), CACHE_NONE))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a single IDO by its offering token ID. Returns null if not found.
|
||||||
|
#[get("/by-offering-token/<offering_token_id>")]
|
||||||
|
pub async fn get_ido_by_offering_token(
|
||||||
|
offering_token_id: &str,
|
||||||
|
db: &State<DB>,
|
||||||
|
) -> CachedApiResult<Value> {
|
||||||
|
let blob = display_hex_to_blob::<TokenID>(offering_token_id).map_err(|e| {
|
||||||
|
bad_request(
|
||||||
|
ApiErrorCode::InvalidParameters,
|
||||||
|
&format!("Invalid offering_token_id: {e}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let item = crate::db::ido::get_ido_by_offering_token_id(&db.ido_r, blob)
|
||||||
|
.await
|
||||||
|
.map_err(db_error)?;
|
||||||
|
|
||||||
|
Ok(cached_ok(serde_json::to_value(item).unwrap(), CACHE_NONE))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the comma-delimited `owner_nfthash` query parameter into raw byte
|
||||||
|
/// filters. Returns an empty vec when the parameter is absent. Rejects more
|
||||||
|
/// than [`MAX_OWNER_NFTHASH_FILTERS`] values, blank entries, and invalid hex.
|
||||||
|
fn parse_owner_nfthash_filters(
|
||||||
|
owner_nfthash: Option<&str>,
|
||||||
|
) -> Result<Vec<Vec<u8>>, rocket::response::status::Custom<rocket::serde::json::Json<Value>>> {
|
||||||
|
let Some(raw) = owner_nfthash else {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
};
|
||||||
|
|
||||||
|
let parts: Vec<&str> = raw
|
||||||
|
.split(',')
|
||||||
|
.map(|s| s.trim())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect();
|
||||||
|
if parts.len() > MAX_OWNER_NFTHASH_FILTERS {
|
||||||
|
return Err(bad_request(
|
||||||
|
ApiErrorCode::InvalidParameters,
|
||||||
|
&format!("Too many owner_nfthash filters (max {MAX_OWNER_NFTHASH_FILTERS})"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
parts
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| {
|
||||||
|
let bytes = hex::decode(s).map_err(|e| {
|
||||||
|
bad_request(
|
||||||
|
ApiErrorCode::InvalidParameters,
|
||||||
|
&format!("Invalid owner_nfthash '{s}': {e}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
if bytes.len() != OWNER_NFTHASH_LEN {
|
||||||
|
return Err(bad_request(
|
||||||
|
ApiErrorCode::InvalidParameters,
|
||||||
|
&format!(
|
||||||
|
"Invalid owner_nfthash '{s}': expected {OWNER_NFTHASH_LEN} bytes, got {}",
|
||||||
|
bytes.len()
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(bytes)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List entries for an IDO.
|
||||||
|
///
|
||||||
|
/// - `id`: the IDO's public id (preinit txid, 64-char hex)
|
||||||
|
/// - `distributed`: optional boolean filter
|
||||||
|
/// - `owner_nfthash`: optional filter by owner nfthash (hex). Multiple hashes
|
||||||
|
/// may be supplied comma-delimited; up to 20 per request. An entry matches
|
||||||
|
/// if its owner_nfthash equals any of the supplied values.
|
||||||
|
///
|
||||||
|
/// Pagination: `offset` (default 0), `limit` (default 20, max 100)
|
||||||
|
#[get("/<id>/entries?<distributed>&<owner_nfthash>&<offset>&<limit>")]
|
||||||
|
pub async fn list_ido_entries(
|
||||||
|
id: &str,
|
||||||
|
distributed: Option<bool>,
|
||||||
|
owner_nfthash: Option<&str>,
|
||||||
|
offset: Option<i64>,
|
||||||
|
limit: Option<i64>,
|
||||||
|
db: &State<DB>,
|
||||||
|
) -> CachedApiResult<Value> {
|
||||||
|
let offset = offset.unwrap_or(0).max(0);
|
||||||
|
let limit = limit.unwrap_or(LIST_DEFAULT_LIMIT).clamp(1, LIST_MAX_LIMIT);
|
||||||
|
|
||||||
|
let owner_nfthashes = parse_owner_nfthash_filters(owner_nfthash)?;
|
||||||
|
|
||||||
|
let (internal_id, preinit_txid_hex) = resolve_ido(id, &db.ido_r).await?;
|
||||||
|
|
||||||
|
let items = crate::db::ido::list_ido_entries(
|
||||||
|
&db.ido_r,
|
||||||
|
internal_id,
|
||||||
|
&preinit_txid_hex,
|
||||||
|
distributed,
|
||||||
|
&owner_nfthashes,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(db_error)?;
|
||||||
|
|
||||||
|
Ok(cached_ok(serde_json::to_value(items).unwrap(), CACHE_NONE))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve_ido(
|
||||||
|
id: &str,
|
||||||
|
pool: &sqlx::SqlitePool,
|
||||||
|
) -> Result<(i64, String), rocket::response::status::Custom<rocket::serde::json::Json<Value>>> {
|
||||||
|
let preinit_blob = display_hex_to_blob::<Txid>(id).map_err(|e| {
|
||||||
|
bad_request(
|
||||||
|
ApiErrorCode::InvalidParameters,
|
||||||
|
&format!("Invalid IDO id (expected preinit_txid hex): {e}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let internal_id = crate::db::ido::lookup_internal_id_by_preinit_txid(pool, &preinit_blob)
|
||||||
|
.await
|
||||||
|
.map_err(db_error)?
|
||||||
|
.ok_or_else(|| not_found(ApiErrorCode::IdoNotFound, "IDO not found"))?;
|
||||||
|
|
||||||
|
let preinit_txid_hex = blob_to_display_hex::<Txid>(&preinit_blob).map_err(|e| {
|
||||||
|
bad_request(
|
||||||
|
ApiErrorCode::InvalidParameters,
|
||||||
|
&format!("Invalid IDO id: {e}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok((internal_id, preinit_txid_hex))
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ pub mod candlesticks;
|
||||||
pub mod contract;
|
pub mod contract;
|
||||||
pub mod err;
|
pub mod err;
|
||||||
pub mod health;
|
pub mod health;
|
||||||
|
pub mod ido;
|
||||||
pub mod moria;
|
pub mod moria;
|
||||||
pub mod oracle;
|
pub mod oracle;
|
||||||
pub mod pool;
|
pub mod pool;
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ mod test_utils {
|
||||||
oracle_r: pool.clone(),
|
oracle_r: pool.clone(),
|
||||||
moria_w: pool.clone(),
|
moria_w: pool.clone(),
|
||||||
moria_r: pool.clone(),
|
moria_r: pool.clone(),
|
||||||
|
ido_w: pool.clone(),
|
||||||
|
ido_r: pool.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue