diff --git a/app/src/main/resources/explorer-backend-openapi.json b/app/src/main/resources/explorer-backend-openapi.json
index 878cc2a56..9fb90d3bc 100644
--- a/app/src/main/resources/explorer-backend-openapi.json
+++ b/app/src/main/resources/explorer-backend-openapi.json
@@ -61,6 +61,9 @@
"$ref": "#/components/schemas/Token"
}
},
+ "fixedOutput": {
+ "type": "boolean"
+ },
"type": {
"type": "string"
},
@@ -74,6 +77,7 @@
"key",
"attoAlphAmount",
"address",
+ "fixedOutput",
"type"
]
},
@@ -188,6 +192,9 @@
"format": "address",
"type": "string"
},
+ "contractInput": {
+ "type": "boolean"
+ },
"unlockScript": {
"format": "hex-string",
"type": "string"
@@ -211,7 +218,8 @@
}
},
"required": [
- "outputRef"
+ "outputRef",
+ "contractInput"
]
},
"PerChainCount": {
@@ -253,6 +261,94 @@
"type"
]
},
+ "BlockEntry": {
+ "type": "object",
+ "title": "BlockEntry",
+ "properties": {
+ "parent": {
+ "format": "block-hash",
+ "type": "string"
+ },
+ "chainFrom": {
+ "format": "group-index",
+ "type": "integer"
+ },
+ "depStateHash": {
+ "format": "32-byte-hash",
+ "type": "string"
+ },
+ "deps": {
+ "type": "array",
+ "items": {
+ "format": "block-hash",
+ "type": "string"
+ }
+ },
+ "nonce": {
+ "format": "hex-string",
+ "type": "string"
+ },
+ "version": {
+ "type": "integer"
+ },
+ "target": {
+ "format": "hex-string",
+ "type": "string"
+ },
+ "mainChain": {
+ "type": "boolean"
+ },
+ "txsHash": {
+ "format": "32-byte-hash",
+ "type": "string"
+ },
+ "chainTo": {
+ "format": "group-index",
+ "type": "integer"
+ },
+ "ghostUncles": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/GhostUncle"
+ }
+ },
+ "hashRate": {
+ "format": "bigint",
+ "type": "string"
+ },
+ "txNumber": {
+ "format": "int32",
+ "type": "integer"
+ },
+ "hash": {
+ "format": "block-hash",
+ "type": "string"
+ },
+ "height": {
+ "format": "int32",
+ "type": "integer"
+ },
+ "timestamp": {
+ "format": "int64",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "hash",
+ "timestamp",
+ "chainFrom",
+ "chainTo",
+ "height",
+ "nonce",
+ "version",
+ "depStateHash",
+ "txsHash",
+ "txNumber",
+ "target",
+ "hashRate",
+ "mainChain"
+ ]
+ },
"ContractParent": {
"type": "object",
"title": "ContractParent",
@@ -344,6 +440,9 @@
"$ref": "#/components/schemas/Token"
}
},
+ "fixedOutput": {
+ "type": "boolean"
+ },
"type": {
"type": "string"
},
@@ -361,6 +460,7 @@
"key",
"attoAlphAmount",
"address",
+ "fixedOutput",
"type"
]
},
@@ -390,8 +490,12 @@
"format": "block-hash",
"type": "string"
},
- "coinbase": {
- "type": "boolean"
+ "scriptSignatures": {
+ "type": "array",
+ "items": {
+ "format": "hex-string",
+ "type": "string"
+ }
},
"inputs": {
"type": "array",
@@ -399,16 +503,35 @@
"$ref": "#/components/schemas/Input"
}
},
- "gasAmount": {
- "format": "int32",
- "type": "integer"
- },
"scriptExecutionOk": {
"type": "boolean"
},
"type": {
"type": "string"
},
+ "scriptOpt": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer"
+ },
+ "coinbase": {
+ "type": "boolean"
+ },
+ "inputSignatures": {
+ "type": "array",
+ "items": {
+ "format": "hex-string",
+ "type": "string"
+ }
+ },
+ "gasAmount": {
+ "format": "int32",
+ "type": "integer"
+ },
+ "networkId": {
+ "type": "integer"
+ },
"hash": {
"format": "32-byte-hash",
"type": "string"
@@ -426,6 +549,8 @@
"hash",
"blockHash",
"timestamp",
+ "version",
+ "networkId",
"gasAmount",
"gasPrice",
"scriptExecutionOk",
@@ -769,8 +894,12 @@
"format": "block-hash",
"type": "string"
},
- "coinbase": {
- "type": "boolean"
+ "scriptSignatures": {
+ "type": "array",
+ "items": {
+ "format": "hex-string",
+ "type": "string"
+ }
},
"inputs": {
"type": "array",
@@ -778,12 +907,31 @@
"$ref": "#/components/schemas/Input"
}
},
+ "scriptExecutionOk": {
+ "type": "boolean"
+ },
+ "scriptOpt": {
+ "type": "string"
+ },
+ "version": {
+ "type": "integer"
+ },
+ "coinbase": {
+ "type": "boolean"
+ },
+ "inputSignatures": {
+ "type": "array",
+ "items": {
+ "format": "hex-string",
+ "type": "string"
+ }
+ },
"gasAmount": {
"format": "int32",
"type": "integer"
},
- "scriptExecutionOk": {
- "type": "boolean"
+ "networkId": {
+ "type": "integer"
},
"hash": {
"format": "32-byte-hash",
@@ -802,6 +950,8 @@
"hash",
"blockHash",
"timestamp",
+ "version",
+ "networkId",
"gasAmount",
"gasPrice",
"scriptExecutionOk",
@@ -820,6 +970,24 @@
"type"
]
},
+ "GhostUncle": {
+ "type": "object",
+ "title": "GhostUncle",
+ "properties": {
+ "blockHash": {
+ "format": "block-hash",
+ "type": "string"
+ },
+ "miner": {
+ "format": "address",
+ "type": "string"
+ }
+ },
+ "required": [
+ "blockHash",
+ "miner"
+ ]
+ },
"NFTCollectionWithRoyalty": {
"type": "object",
"title": "NFTCollectionWithRoyalty",
@@ -2964,6 +3132,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -2982,6 +3151,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
@@ -2992,6 +3162,7 @@
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -3032,6 +3203,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -3050,6 +3222,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
@@ -3060,6 +3233,7 @@
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -3202,6 +3376,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -3220,15 +3395,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -3248,8 +3427,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -3271,6 +3456,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -3289,15 +3475,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -3317,8 +3507,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -3474,6 +3670,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -3492,15 +3689,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -3520,8 +3721,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -3543,6 +3750,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -3561,15 +3769,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -3589,8 +3801,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -3832,6 +4050,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -3850,15 +4069,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -3878,9 +4101,15 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
"type": "Accepted",
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -4003,6 +4232,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -4021,15 +4251,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -4049,8 +4283,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -4072,6 +4312,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -4090,15 +4331,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -4118,8 +4363,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -4286,6 +4537,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -4304,15 +4556,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -4332,8 +4588,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -4355,6 +4617,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -4373,15 +4636,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -4401,8 +4668,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -4700,6 +4973,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -4718,15 +4992,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -4746,8 +5024,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -4769,6 +5053,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -4787,15 +5072,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -4815,8 +5104,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -5230,12 +5525,27 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/BlockEntryLite"
+ "$ref": "#/components/schemas/BlockEntry"
},
"example": {
- "mainChain": true,
+ "parent": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
"chainFrom": 1,
+ "depStateHash": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
+ "deps": [
+ "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5"
+ ],
+ "nonce": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
+ "version": 1,
+ "target": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
+ "mainChain": true,
+ "txsHash": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"chainTo": 2,
+ "ghostUncles": [
+ {
+ "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
+ "miner": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y"
+ }
+ ],
"hashRate": "147573952589676412928",
"txNumber": 1,
"hash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
@@ -5590,6 +5900,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -5608,15 +5919,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -5636,8 +5951,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -5659,6 +5980,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -5677,15 +5999,19 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
],
"blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5",
- "coinbase": false,
+ "scriptSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -5705,8 +6031,14 @@
}
}
],
- "gasAmount": 20000,
"scriptExecutionOk": true,
+ "version": 1,
+ "coinbase": false,
+ "inputSignatures": [
+ "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
+ ],
+ "gasAmount": 20000,
+ "networkId": 0,
"hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69",
"gasPrice": "100000000000",
"timestamp": 1611041396892
@@ -8243,6 +8575,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -8261,6 +8594,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
@@ -8271,6 +8605,7 @@
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
@@ -8311,6 +8646,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": true,
"type": "AssetOutput",
"message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
@@ -8329,6 +8665,7 @@
"id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf"
}
],
+ "fixedOutput": false,
"type": "ContractOutput",
"key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef"
}
@@ -8339,6 +8676,7 @@
"inputs": [
{
"address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y",
+ "contractInput": false,
"unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28",
"attoAlphAmount": "2",
"tokens": [
diff --git a/app/src/main/scala/org/alephium/explorer/api/BlockEndpoints.scala b/app/src/main/scala/org/alephium/explorer/api/BlockEndpoints.scala
index c19512cd5..0f0b75c5b 100644
--- a/app/src/main/scala/org/alephium/explorer/api/BlockEndpoints.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/BlockEndpoints.scala
@@ -33,10 +33,10 @@ trait BlockEndpoints extends BaseEndpoint with QueryParams {
.tag("Blocks")
.in("blocks")
- val getBlockByHash: BaseEndpoint[BlockHash, BlockEntryLite] =
+ val getBlockByHash: BaseEndpoint[BlockHash, BlockEntry] =
blocksEndpoint.get
.in(path[BlockHash]("block_hash"))
- .out(jsonBody[BlockEntryLite])
+ .out(jsonBody[BlockEntry])
.description("Get a block with hash")
val getBlockTransactions: BaseEndpoint[(BlockHash, Pagination), ArraySeq[Transaction]] =
diff --git a/app/src/main/scala/org/alephium/explorer/api/EndpointExamples.scala b/app/src/main/scala/org/alephium/explorer/api/EndpointExamples.scala
index e532f1cb0..f722b45ea 100644
--- a/app/src/main/scala/org/alephium/explorer/api/EndpointExamples.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/EndpointExamples.scala
@@ -44,6 +44,9 @@ object EndpointExamples extends EndpointsExamples {
.from(Hex.unsafe("bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5"))
.get
+ val version: Byte = 1
+ val networkId: Byte = 0
+
private val outputRef: OutputRef =
OutputRef(hint = 23412, key = hash)
@@ -66,6 +69,8 @@ object EndpointExamples extends EndpointsExamples {
contract
)
+ private val addressAsset: Address.Asset = Address.asset(address1.toBase58).get
+
private val groupIndex1: GroupIndex = new GroupIndex(1)
private val groupIndex2: GroupIndex = new GroupIndex(2)
@@ -84,7 +89,8 @@ object EndpointExamples extends EndpointsExamples {
txHashRef = Some(txId),
address = Some(address1),
attoAlphAmount = Some(U256.Two),
- tokens = Some(tokens)
+ tokens = Some(tokens),
+ contractInput = false
)
private val outputAsset: AssetOutput =
@@ -95,7 +101,8 @@ object EndpointExamples extends EndpointsExamples {
address = address1,
tokens = Some(tokens),
lockTime = Some(ts),
- message = Some(hash.bytes)
+ message = Some(hash.bytes),
+ fixedOutput = true
)
private val outputContract: Output =
@@ -104,7 +111,8 @@ object EndpointExamples extends EndpointsExamples {
key = hash,
attoAlphAmount = U256.Two,
address = address1,
- tokens = Some(tokens)
+ tokens = Some(tokens),
+ fixedOutput = false
)
/** Main API objects
@@ -121,6 +129,26 @@ object EndpointExamples extends EndpointsExamples {
hashRate = HashRate.a128EhPerSecond.value
)
+ private val blockEntry: BlockEntry =
+ BlockEntry(
+ hash = blockHash,
+ timestamp = ts,
+ chainFrom = groupIndex1,
+ chainTo = groupIndex2,
+ height = Height.unsafe(42),
+ deps = ArraySeq(blockHash),
+ nonce = hash.bytes,
+ version = 1,
+ depStateHash = hash,
+ txsHash = hash,
+ txNumber = 1,
+ target = hash.bytes,
+ hashRate = HashRate.a128EhPerSecond.value,
+ parent = Some(blockHash),
+ mainChain = true,
+ ghostUncles = ArraySeq(GhostUncle(blockHash, addressAsset))
+ )
+
private val transaction: Transaction =
Transaction(
hash = txId,
@@ -128,9 +156,14 @@ object EndpointExamples extends EndpointsExamples {
timestamp = ts,
inputs = ArraySeq(input),
outputs = ArraySeq(outputAsset, outputContract),
+ version = version,
+ networkId = networkId,
+ scriptOpt = None,
gasAmount = org.alephium.protocol.model.minimalGas.value,
gasPrice = org.alephium.protocol.model.nonCoinbaseMinGasPrice.value,
scriptExecutionOk = true,
+ inputSignatures = ArraySeq(hash.bytes),
+ scriptSignatures = ArraySeq(hash.bytes),
coinbase = false
)
@@ -141,9 +174,14 @@ object EndpointExamples extends EndpointsExamples {
timestamp = ts,
inputs = ArraySeq(input),
outputs = ArraySeq(outputAsset, outputContract),
+ version = version,
+ networkId = networkId,
+ scriptOpt = None,
gasAmount = org.alephium.protocol.model.minimalGas.value,
gasPrice = org.alephium.protocol.model.nonCoinbaseMinGasPrice.value,
scriptExecutionOk = true,
+ inputSignatures = ArraySeq(hash.bytes),
+ scriptSignatures = ArraySeq(hash.bytes),
coinbase = false
)
@@ -313,6 +351,9 @@ object EndpointExamples extends EndpointsExamples {
implicit val blockEntryLiteExample: List[Example[BlockEntryLite]] =
simpleExample(blockEntryLite)
+ implicit val blockEntryExample: List[Example[BlockEntry]] =
+ simpleExample(blockEntry)
+
implicit val transactionsExample: List[Example[ArraySeq[Transaction]]] =
simpleExample(ArraySeq(transaction, transaction))
diff --git a/app/src/main/scala/org/alephium/explorer/api/model/BlockEntry.scala b/app/src/main/scala/org/alephium/explorer/api/model/BlockEntry.scala
index 48b85d88d..a427b546e 100644
--- a/app/src/main/scala/org/alephium/explorer/api/model/BlockEntry.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/model/BlockEntry.scala
@@ -20,13 +20,16 @@ import java.math.BigInteger
import scala.collection.immutable.ArraySeq
+import akka.util.ByteString
+
import org.alephium.api.UtilJson._
import org.alephium.explorer.api.Codecs._
import org.alephium.explorer.api.Json.groupIndexReadWriter
import org.alephium.explorer.service.FlowEntity
import org.alephium.json.Json._
+import org.alephium.protocol.Hash
import org.alephium.protocol.model.{BlockHash, GroupIndex}
-import org.alephium.util.TimeStamp
+import org.alephium.util.{AVector, TimeStamp}
final case class BlockEntry(
hash: BlockHash,
@@ -35,10 +38,39 @@ final case class BlockEntry(
chainTo: GroupIndex,
height: Height,
deps: ArraySeq[BlockHash],
- transactions: ArraySeq[Transaction],
+ nonce: ByteString,
+ version: Byte,
+ depStateHash: Hash,
+ txsHash: Hash,
+ txNumber: Int,
+ target: ByteString,
+ hashRate: BigInteger,
+ parent: Option[BlockHash],
mainChain: Boolean,
- hashRate: BigInteger
-) extends FlowEntity
+ ghostUncles: ArraySeq[GhostUncle]
+) extends FlowEntity {
+
+ def toProtocol(
+ transactions: ArraySeq[Transaction]
+ ): org.alephium.api.model.BlockEntry = {
+ org.alephium.api.model.BlockEntry(
+ hash,
+ timestamp,
+ chainFrom.value,
+ chainTo.value,
+ height.value,
+ AVector.from(deps),
+ AVector.from(transactions.map(_.toProtocol())),
+ nonce,
+ version,
+ depStateHash,
+ txsHash,
+ target,
+ AVector.from(ghostUncles.map(_.toProtocol()))
+ )
+ }
+
+}
object BlockEntry {
implicit val codec: ReadWriter[BlockEntry] = macroRW
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/model/BlockDepEntity.scala b/app/src/main/scala/org/alephium/explorer/api/model/GhostUncle.scala
similarity index 62%
rename from app/src/main/scala/org/alephium/explorer/persistence/model/BlockDepEntity.scala
rename to app/src/main/scala/org/alephium/explorer/api/model/GhostUncle.scala
index 2a1130e57..af93bdee6 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/model/BlockDepEntity.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/model/GhostUncle.scala
@@ -14,10 +14,17 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see .
-package org.alephium.explorer.persistence.model
+package org.alephium.explorer.api.model
-import org.alephium.protocol.model.BlockHash
+import org.alephium.api.model.GhostUncleBlockEntry
+import org.alephium.explorer.api.Codecs._
+import org.alephium.json.Json._
+import org.alephium.protocol.model.{Address, BlockHash}
-/** Class for defining rows in table [[org.alephium.explorer.persistence.schema.BlockDepsSchema]]
- */
-final case class BlockDepEntity(hash: BlockHash, dep: BlockHash, order: Int)
+final case class GhostUncle(blockHash: BlockHash, miner: Address.Asset) {
+ def toProtocol(): GhostUncleBlockEntry = GhostUncleBlockEntry(blockHash, miner)
+}
+
+object GhostUncle {
+ implicit val codec: ReadWriter[GhostUncle] = macroRW
+}
diff --git a/app/src/main/scala/org/alephium/explorer/api/model/Input.scala b/app/src/main/scala/org/alephium/explorer/api/model/Input.scala
index a65a43e68..279017cd9 100644
--- a/app/src/main/scala/org/alephium/explorer/api/model/Input.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/model/Input.scala
@@ -21,6 +21,7 @@ import scala.collection.immutable.ArraySeq
import akka.util.ByteString
import sttp.tapir.Schema
+import org.alephium.api.{model => protocol}
import org.alephium.api.TapirSchemas._
import org.alephium.api.UtilJson._
import org.alephium.explorer.api.Json._
@@ -35,8 +36,15 @@ final case class Input(
txHashRef: Option[TransactionId] = None,
address: Option[Address] = None,
attoAlphAmount: Option[U256] = None,
- tokens: Option[ArraySeq[Token]] = None
-)
+ tokens: Option[ArraySeq[Token]] = None,
+ contractInput: Boolean
+) {
+ def toProtocol(): protocol.AssetInput =
+ protocol.AssetInput(
+ outputRef = outputRef.toProtocol(),
+ unlockScript = unlockScript.getOrElse(ByteString.empty)
+ )
+}
object Input {
implicit val readWriter: ReadWriter[Input] = macroRW
diff --git a/app/src/main/scala/org/alephium/explorer/api/model/Output.scala b/app/src/main/scala/org/alephium/explorer/api/model/Output.scala
index b46cd0e6e..3cec0dba3 100644
--- a/app/src/main/scala/org/alephium/explorer/api/model/Output.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/model/Output.scala
@@ -29,6 +29,7 @@ import org.alephium.json.Json._
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{Address, TransactionId}
import org.alephium.util.{TimeStamp, U256}
+import org.alephium.util.AVector
sealed trait Output {
def hint: Int
@@ -37,6 +38,7 @@ sealed trait Output {
def address: Address
def tokens: Option[ArraySeq[Token]]
def spent: Option[TransactionId]
+ def fixedOutput: Boolean
}
@SuppressWarnings(Array("org.wartremover.warts.DefaultArguments"))
@@ -49,8 +51,9 @@ final case class AssetOutput(
tokens: Option[ArraySeq[Token]] = None,
lockTime: Option[TimeStamp] = None,
message: Option[ByteString] = None,
- spent: Option[TransactionId] = None
-) extends Output
+ spent: Option[TransactionId] = None,
+ fixedOutput: Boolean
+) extends Output {}
@SuppressWarnings(Array("org.wartremover.warts.DefaultArguments"))
@upickle.implicits.key("ContractOutput")
@@ -60,11 +63,69 @@ final case class ContractOutput(
attoAlphAmount: U256,
address: Address,
tokens: Option[ArraySeq[Token]] = None,
- spent: Option[TransactionId] = None
+ spent: Option[TransactionId] = None,
+ fixedOutput: Boolean
) extends Output
object Output {
+ def toFixedAssetOutput(
+ output: Output
+ ): Option[org.alephium.api.model.FixedAssetOutput] = {
+ output match {
+ case asset: AssetOutput if asset.fixedOutput =>
+ asset.address match {
+ case assetAddress: Address.Asset =>
+ val amount = org.alephium.api.model.Amount(asset.attoAlphAmount)
+ Some(
+ org.alephium.api.model.FixedAssetOutput(
+ asset.hint,
+ asset.key,
+ amount,
+ assetAddress,
+ tokens = asset.tokens
+ .map(tokens => AVector.from(tokens.map(_.toProtocol())))
+ .getOrElse(AVector.empty),
+ lockTime = asset.lockTime.getOrElse(TimeStamp.zero),
+ asset.message.getOrElse(ByteString.empty)
+ )
+ )
+ case _ => None
+ }
+ case _ => None
+ }
+ }
+
+ def toProtocol(output: Output): Option[org.alephium.api.model.Output] =
+ (output, output.address) match {
+ case (asset: AssetOutput, assetAddress: Address.Asset) =>
+ Some(
+ org.alephium.api.model.AssetOutput(
+ output.hint,
+ output.key,
+ org.alephium.api.model.Amount(output.attoAlphAmount),
+ assetAddress,
+ tokens =
+ output.tokens.map(t => AVector.from(t.map(_.toProtocol()))).getOrElse(AVector.empty),
+ lockTime = asset.lockTime.getOrElse(TimeStamp.zero),
+ message = asset.message.getOrElse(ByteString.empty)
+ )
+ )
+ case (_: ContractOutput, contractAddress: Address.Contract) =>
+ Some(
+ org.alephium.api.model.ContractOutput(
+ output.hint,
+ output.key,
+ org.alephium.api.model.Amount(output.attoAlphAmount),
+ contractAddress,
+ tokens = output.tokens
+ .map(tokens => AVector.from(tokens.map(_.toProtocol())))
+ .getOrElse(AVector.empty)
+ )
+ )
+ case _ => None
+ }
+
implicit val assetReadWriter: ReadWriter[AssetOutput] = macroRW
implicit val contractReadWriter: ReadWriter[ContractOutput] = macroRW
diff --git a/app/src/main/scala/org/alephium/explorer/api/model/OutputRef.scala b/app/src/main/scala/org/alephium/explorer/api/model/OutputRef.scala
index 4fcc13e7c..86b2c0a6c 100644
--- a/app/src/main/scala/org/alephium/explorer/api/model/OutputRef.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/model/OutputRef.scala
@@ -23,7 +23,11 @@ import org.alephium.explorer.api.Json._
import org.alephium.json.Json._
import org.alephium.protocol.Hash
-final case class OutputRef(hint: Int, key: Hash)
+final case class OutputRef(hint: Int, key: Hash) {
+
+ def toProtocol(): org.alephium.api.model.OutputRef =
+ org.alephium.api.model.OutputRef(hint, key)
+}
object OutputRef {
implicit val readWriter: ReadWriter[OutputRef] = macroRW
diff --git a/app/src/main/scala/org/alephium/explorer/api/model/Token.scala b/app/src/main/scala/org/alephium/explorer/api/model/Token.scala
index e5c0181b8..9e9e3ab43 100644
--- a/app/src/main/scala/org/alephium/explorer/api/model/Token.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/model/Token.scala
@@ -26,7 +26,9 @@ import org.alephium.protocol.model.TokenId
import org.alephium.serde._
import org.alephium.util.U256
-final case class Token(id: TokenId, amount: U256)
+final case class Token(id: TokenId, amount: U256) {
+ def toProtocol(): org.alephium.api.model.Token = org.alephium.api.model.Token(id, amount)
+}
object Token {
implicit val readWriter: ReadWriter[Token] = macroRW
diff --git a/app/src/main/scala/org/alephium/explorer/api/model/Transaction.scala b/app/src/main/scala/org/alephium/explorer/api/model/Transaction.scala
index 7ba3c3a04..1666a7077 100644
--- a/app/src/main/scala/org/alephium/explorer/api/model/Transaction.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/model/Transaction.scala
@@ -20,10 +20,12 @@ import java.time.Instant
import scala.collection.immutable.ArraySeq
+import akka.util.ByteString
import sttp.tapir.Schema
+import org.alephium.api.{model => protocol}
import org.alephium.api.TapirSchemas._
-import org.alephium.api.UtilJson.{timestampReader, timestampWriter}
+import org.alephium.api.UtilJson._
import org.alephium.explorer.api.Json._
import org.alephium.explorer.util.UtxoUtil
import org.alephium.json.Json._
@@ -31,6 +33,7 @@ import org.alephium.protocol.ALPH
import org.alephium.protocol.model.{BlockHash, TransactionId}
import org.alephium.protocol.model.Address
import org.alephium.util.{TimeStamp, U256}
+import org.alephium.util.AVector
final case class Transaction(
hash: TransactionId,
@@ -38,9 +41,14 @@ final case class Transaction(
timestamp: TimeStamp,
inputs: ArraySeq[Input],
outputs: ArraySeq[Output],
+ version: Byte,
+ networkId: Byte,
+ scriptOpt: Option[String],
gasAmount: Int,
gasPrice: U256,
scriptExecutionOk: Boolean,
+ inputSignatures: ArraySeq[ByteString],
+ scriptSignatures: ArraySeq[ByteString],
coinbase: Boolean
) {
def toCsv(address: Address): String = {
@@ -59,6 +67,29 @@ final case class Transaction(
.getOrElse("")
s"${hash.toHexString},${blockHash.toHexString},${timestamp.millis},$dateTime,$fromAddressesStr,$toAddresses,$amount,$amountHint\n"
}
+
+ def toProtocol(): org.alephium.api.model.Transaction = {
+ val (inputContracts, inputAssets) = inputs.partition(_.contractInput)
+ val (fixedOutputs, generatedOutputs) = outputs.partition(_.fixedOutput)
+ val unsigned: org.alephium.api.model.UnsignedTx = org.alephium.api.model.UnsignedTx(
+ txId = hash,
+ version = version,
+ networkId = networkId,
+ scriptOpt = scriptOpt.map(org.alephium.api.model.Script.apply),
+ gasAmount = gasAmount,
+ gasPrice = gasPrice,
+ inputs = AVector.from(inputAssets.map(_.toProtocol())),
+ fixedOutputs = AVector.from(fixedOutputs.flatMap(Output.toFixedAssetOutput))
+ )
+ org.alephium.api.model.Transaction(
+ unsigned = unsigned,
+ scriptExecutionOk = scriptExecutionOk,
+ contractInputs = AVector.from(inputContracts.map(_.outputRef.toProtocol())),
+ generatedOutputs = AVector.from(generatedOutputs.flatMap(Output.toProtocol)),
+ inputSignatures = AVector.from(inputSignatures),
+ scriptSignatures = AVector.from(scriptSignatures)
+ )
+ }
}
object Transaction {
diff --git a/app/src/main/scala/org/alephium/explorer/api/model/TransactionLike.scala b/app/src/main/scala/org/alephium/explorer/api/model/TransactionLike.scala
index a79809288..dc068153b 100644
--- a/app/src/main/scala/org/alephium/explorer/api/model/TransactionLike.scala
+++ b/app/src/main/scala/org/alephium/explorer/api/model/TransactionLike.scala
@@ -18,7 +18,9 @@ package org.alephium.explorer.api.model
import scala.collection.immutable.ArraySeq
-import org.alephium.api.UtilJson.{timestampReader, timestampWriter}
+import akka.util.ByteString
+
+import org.alephium.api.UtilJson._
import org.alephium.explorer.api.Json._
import org.alephium.json.Json._
import org.alephium.protocol.model.{BlockHash, GroupIndex, TransactionId}
@@ -42,9 +44,14 @@ final case class AcceptedTransaction(
timestamp: TimeStamp,
inputs: ArraySeq[Input],
outputs: ArraySeq[Output],
+ version: Byte,
+ networkId: Byte,
+ scriptOpt: Option[String],
gasAmount: Int,
gasPrice: U256,
scriptExecutionOk: Boolean,
+ inputSignatures: ArraySeq[ByteString],
+ scriptSignatures: ArraySeq[ByteString],
coinbase: Boolean
) extends TransactionLike
@@ -56,9 +63,14 @@ object AcceptedTransaction {
tx.timestamp,
tx.inputs,
tx.outputs,
+ tx.version,
+ tx.networkId,
+ tx.scriptOpt,
tx.gasAmount,
tx.gasPrice,
tx.scriptExecutionOk,
+ tx.inputSignatures,
+ tx.scriptSignatures,
tx.coinbase
)
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/DBInitializer.scala b/app/src/main/scala/org/alephium/explorer/persistence/DBInitializer.scala
index 571dd8c17..c0fb47f9a 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/DBInitializer.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/DBInitializer.scala
@@ -43,7 +43,6 @@ object DBInitializer extends StrictLogging {
val allTables =
ArraySeq(
BlockHeaderSchema.table,
- BlockDepsSchema.table,
TransactionSchema.table,
InputSchema.table,
OutputSchema.table,
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/dao/BlockDao.scala b/app/src/main/scala/org/alephium/explorer/persistence/dao/BlockDao.scala
index 90760f468..12d36b9ba 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/dao/BlockDao.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/dao/BlockDao.scala
@@ -44,7 +44,6 @@ import org.alephium.util.{Duration, TimeStamp}
object BlockDao {
def getLite(hash: BlockHash)(implicit
- ec: ExecutionContext,
dc: DatabaseConfig[PostgresProfile]
): Future[Option[BlockEntryLite]] =
run(getBlockEntryLiteAction(hash))
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/model/BlockEntity.scala b/app/src/main/scala/org/alephium/explorer/persistence/model/BlockEntity.scala
index 7b9cbd95a..a3046f34d 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/model/BlockEntity.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/model/BlockEntity.scala
@@ -22,7 +22,7 @@ import scala.collection.immutable.ArraySeq
import akka.util.ByteString
-import org.alephium.explorer.api.model.Height
+import org.alephium.explorer.api.model.{GhostUncle, Height}
import org.alephium.explorer.service.FlowEntity
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{BlockHash, GroupIndex}
@@ -44,22 +44,9 @@ final case class BlockEntity(
depStateHash: Hash,
txsHash: Hash,
target: ByteString,
- hashrate: BigInteger
+ hashrate: BigInteger,
+ ghostUncles: ArraySeq[GhostUncle]
) extends FlowEntity {
- def updateMainChain(newMainChain: Boolean): BlockEntity = {
- this.copy(
- mainChain = newMainChain,
- transactions = transactions.map(_.copy(mainChain = newMainChain)),
- inputs = inputs.map(_.copy(mainChain = newMainChain)),
- outputs = outputs.map(_.copy(mainChain = newMainChain))
- )
- }
-
- /** Builds entries for block_deps table */
- def toBlockDepEntities(): ArraySeq[BlockDepEntity] =
- deps.zipWithIndex map { case (dep, i) =>
- BlockDepEntity(hash = hash, dep = dep, order = i)
- }
@inline def toBlockHeader(groupNum: Int): BlockHeader =
BlockHeader.fromEntity(this, groupNum)
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/model/BlockHeader.scala b/app/src/main/scala/org/alephium/explorer/persistence/model/BlockHeader.scala
index 48966683c..2aa8842b4 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/model/BlockHeader.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/model/BlockHeader.scala
@@ -22,7 +22,7 @@ import scala.collection.immutable.ArraySeq
import akka.util.ByteString
-import org.alephium.explorer.api.model.{BlockEntry, BlockEntryLite, Height, Transaction}
+import org.alephium.explorer.api.model.{BlockEntry, BlockEntryLite, GhostUncle, Height}
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{BlockHash, GroupIndex}
import org.alephium.util.TimeStamp
@@ -41,17 +41,47 @@ final case class BlockHeader(
txsCount: Int,
target: ByteString,
hashrate: BigInteger,
- parent: Option[BlockHash]
+ parent: Option[BlockHash],
+ deps: ArraySeq[BlockHash],
+ ghostUncles: Option[ArraySeq[GhostUncle]]
) {
- def toApi(deps: ArraySeq[BlockHash], transactions: ArraySeq[Transaction]): BlockEntry =
- BlockEntry(hash, timestamp, chainFrom, chainTo, height, deps, transactions, mainChain, hashrate)
+
+ def toApi(): BlockEntry =
+ BlockEntry(
+ hash,
+ timestamp,
+ chainFrom,
+ chainTo,
+ height,
+ deps,
+ nonce,
+ version,
+ depStateHash,
+ txsHash,
+ txsCount,
+ target,
+ hashrate,
+ parent,
+ mainChain,
+ ghostUncles.getOrElse(ArraySeq.empty)
+ )
val toLiteApi: BlockEntryLite =
- BlockEntryLite(hash, timestamp, chainFrom, chainTo, height, txsCount, mainChain, hashrate)
+ BlockEntryLite(
+ hash,
+ timestamp,
+ chainFrom,
+ chainTo,
+ height,
+ txsCount,
+ mainChain,
+ hashrate
+ )
}
object BlockHeader {
- def fromEntity(blockEntity: BlockEntity, groupNum: Int): BlockHeader =
+ def fromEntity(blockEntity: BlockEntity, groupNum: Int): BlockHeader = {
+ val ghostUncles = if (blockEntity.ghostUncles.isEmpty) None else Some(blockEntity.ghostUncles)
BlockHeader(
blockEntity.hash,
blockEntity.timestamp,
@@ -66,6 +96,9 @@ object BlockHeader {
blockEntity.transactions.size,
blockEntity.target,
blockEntity.hashrate,
- blockEntity.parent(groupNum)
+ blockEntity.parent(groupNum),
+ blockEntity.deps,
+ ghostUncles
)
+ }
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/model/InputEntity.scala b/app/src/main/scala/org/alephium/explorer/persistence/model/InputEntity.scala
index 3005d0fd0..0aed40ce9 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/model/InputEntity.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/model/InputEntity.scala
@@ -20,11 +20,33 @@ import scala.collection.immutable.ArraySeq
import akka.util.ByteString
-import org.alephium.explorer.api.model.Token
+import org.alephium.explorer.api.model.{Input, OutputRef, Token}
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{Address, BlockHash, TransactionId}
import org.alephium.util.{TimeStamp, U256}
+trait InputEntityLike {
+ def hint: Int
+ def outputRefKey: Hash
+ def unlockScript: Option[ByteString]
+ def outputRefTxHash: Option[TransactionId]
+ def outputRefAddress: Option[Address]
+ def outputRefAmount: Option[U256]
+ def outputRefTokens: Option[ArraySeq[Token]]
+ def contractInput: Boolean
+
+ def toApi(): Input =
+ Input(
+ outputRef = OutputRef(hint, outputRefKey),
+ unlockScript = unlockScript,
+ txHashRef = outputRefTxHash,
+ address = outputRefAddress,
+ attoAlphAmount = outputRefAmount,
+ tokens = outputRefTokens,
+ contractInput = contractInput
+ )
+}
+
final case class InputEntity(
blockHash: BlockHash,
txHash: TransactionId,
@@ -38,8 +60,9 @@ final case class InputEntity(
outputRefTxHash: Option[TransactionId],
outputRefAddress: Option[Address],
outputRefAmount: Option[U256],
- outputRefTokens: Option[ArraySeq[Token]] // None if empty list
-) {
+ outputRefTokens: Option[ArraySeq[Token]], // None if empty list
+ contractInput: Boolean
+) extends InputEntityLike {
/** @return All hash types associated with this [[InputEntity]] */
def hashes(): (TransactionId, BlockHash) =
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/model/OutputEntity.scala b/app/src/main/scala/org/alephium/explorer/persistence/model/OutputEntity.scala
index 1af76972b..2754a8d63 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/model/OutputEntity.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/model/OutputEntity.scala
@@ -20,11 +20,52 @@ import scala.collection.immutable.ArraySeq
import akka.util.ByteString
-import org.alephium.explorer.api.model.Token
+import org.alephium.explorer.api.model.{AssetOutput, ContractOutput, Output, Token}
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{Address, BlockHash, TransactionId}
import org.alephium.util.{TimeStamp, U256}
+trait OutputEntityLike {
+
+ def outputType: OutputEntity.OutputType
+ def hint: Int
+ def key: Hash
+ def amount: U256
+ def address: Address
+ def tokens: Option[ArraySeq[Token]]
+ def lockTime: Option[TimeStamp]
+ def message: Option[ByteString]
+ def spentFinalized: Option[TransactionId]
+ def fixedOutput: Boolean
+
+ def toApi(): Output =
+ outputType match {
+ case OutputEntity.Asset =>
+ AssetOutput(
+ hint = hint,
+ key = key,
+ attoAlphAmount = amount,
+ address = address,
+ tokens = tokens,
+ lockTime = lockTime,
+ message = message,
+ spent = spentFinalized,
+ fixedOutput = fixedOutput
+ )
+
+ case OutputEntity.Contract =>
+ ContractOutput(
+ hint = hint,
+ key = key,
+ attoAlphAmount = amount,
+ address = address,
+ tokens = tokens,
+ spent = spentFinalized,
+ fixedOutput = fixedOutput
+ )
+ }
+}
+
final case class OutputEntity(
blockHash: BlockHash,
txHash: TransactionId,
@@ -42,8 +83,9 @@ final case class OutputEntity(
txOrder: Int,
coinbase: Boolean,
spentFinalized: Option[TransactionId],
- spentTimestamp: Option[TimeStamp]
-)
+ spentTimestamp: Option[TimeStamp],
+ fixedOutput: Boolean
+) extends OutputEntityLike
object OutputEntity {
sealed trait OutputType {
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/model/TransactionEntity.scala b/app/src/main/scala/org/alephium/explorer/persistence/model/TransactionEntity.scala
index 6342e6e7b..45c17a1d5 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/model/TransactionEntity.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/model/TransactionEntity.scala
@@ -20,6 +20,7 @@ import scala.collection.immutable.ArraySeq
import akka.util.ByteString
+import org.alephium.explorer.api.model.{Input, Output, Transaction}
import org.alephium.protocol.model.{BlockHash, GroupIndex, TransactionId}
import org.alephium.util.{TimeStamp, U256}
@@ -29,6 +30,9 @@ final case class TransactionEntity(
timestamp: TimeStamp,
chainFrom: GroupIndex,
chainTo: GroupIndex,
+ version: Byte,
+ networkId: Byte,
+ scriptOpt: Option[String],
gasAmount: Int,
gasPrice: U256,
order: Int,
@@ -37,4 +41,22 @@ final case class TransactionEntity(
inputSignatures: Option[ArraySeq[ByteString]],
scriptSignatures: Option[ArraySeq[ByteString]],
coinbase: Boolean
-)
+) {
+ def toApi(inputs: ArraySeq[Input], outputs: ArraySeq[Output]): Transaction =
+ Transaction(
+ hash,
+ blockHash,
+ timestamp,
+ inputs,
+ outputs,
+ version,
+ networkId,
+ scriptOpt,
+ gasAmount,
+ gasPrice,
+ scriptExecutionOk,
+ inputSignatures.getOrElse(ArraySeq.empty),
+ scriptSignatures.getOrElse(ArraySeq.empty),
+ coinbase
+ )
+}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/model/UInputEntity.scala b/app/src/main/scala/org/alephium/explorer/persistence/model/UInputEntity.scala
index 54219ecde..3231d61ee 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/model/UInputEntity.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/model/UInputEntity.scala
@@ -37,6 +37,7 @@ final case class UInputEntity(
None,
address,
None,
- None
+ None,
+ contractInput = false
)
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/model/UOutputEntity.scala b/app/src/main/scala/org/alephium/explorer/persistence/model/UOutputEntity.scala
index dcf2edc50..fe4eee20b 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/model/UOutputEntity.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/model/UOutputEntity.scala
@@ -45,6 +45,7 @@ final case class UOutputEntity(
tokens,
lockTime,
message,
- None
+ None,
+ fixedOutput = true
)
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/BlockDepQueries.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/BlockDepQueries.scala
deleted file mode 100644
index 8744ed6a7..000000000
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/BlockDepQueries.scala
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2018 The Alephium Authors
-// This file is part of the alephium project.
-//
-// The library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the library. If not, see .
-
-package org.alephium.explorer.persistence.queries
-
-import slick.jdbc.{PositionedParameters, SetParameter, SQLActionBuilder}
-import slick.jdbc.PostgresProfile.api._
-
-import org.alephium.explorer.persistence.DBActionW
-import org.alephium.explorer.persistence.model.BlockDepEntity
-import org.alephium.explorer.persistence.schema.CustomGetResult._
-import org.alephium.explorer.persistence.schema.CustomSetParameter._
-import org.alephium.explorer.util.SlickUtil._
-import org.alephium.protocol.model.BlockHash
-
-object BlockDepQueries {
-
- @SuppressWarnings(Array("org.wartremover.warts.PublicInference"))
- def getDepsForBlock(blockHash: BlockHash) = {
- sql"""
- SELECT dep
- FROM block_deps
- WHERE hash = $blockHash
- ORDER BY dep_order
- """.asAS[BlockHash]
- }
-
- /** Insert block_deps or ignore if there is a primary key conflict.
- *
- * Slick creates the following `INSERT` using string interpolation. Here the same is achieved by
- * manually creating the [[slick.jdbc.SQLActionBuilder]] so our inserts can write multiple rows
- * within a single `INSERT` statement.
- *
- * Splicing is
- * not used to insert values so these queries are still cacheable prepared-statements.
- */
- def insertBlockDeps(deps: Iterable[BlockDepEntity]): DBActionW[Int] =
- // generate '?' placeholders for the parameterised SQL query
- QuerySplitter.splitUpdates(rows = deps, columnsPerRow = 3) { (deps, placeholder) =>
- val query =
- s"""
- |INSERT INTO block_deps ("hash", "dep", "dep_order")
- |VALUES $placeholder
- |ON CONFLICT ON CONSTRAINT hash_deps_pk
- | DO NOTHING
- |""".stripMargin
-
- // set parameters following the insert order defined by the query above
- val parameters: SetParameter[Unit] =
- (_: Unit, params: PositionedParameters) =>
- deps foreach { dep =>
- params >> dep.hash
- params >> dep.dep
- params >> dep.order
- }
-
- // Return builder generated by Slick's string interpolation
- SQLActionBuilder(
- sql = query,
- setParameter = parameters
- ).asUpdate
- }
-}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/BlockQueries.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/BlockQueries.scala
index f880cd885..37c6f4948 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/BlockQueries.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/BlockQueries.scala
@@ -29,7 +29,6 @@ import org.alephium.explorer.GroupSetting
import org.alephium.explorer.api.model._
import org.alephium.explorer.persistence._
import org.alephium.explorer.persistence.model._
-import org.alephium.explorer.persistence.queries.BlockDepQueries._
import org.alephium.explorer.persistence.queries.InputQueries.insertInputs
import org.alephium.explorer.persistence.queries.OutputQueries.insertOutputs
import org.alephium.explorer.persistence.queries.TransactionQueries._
@@ -61,20 +60,21 @@ object BlockQueries extends StrictLogging {
)
}
- def buildBlockEntryAction(
- blockHeader: BlockHeader
- )(implicit ec: ExecutionContext): DBActionR[BlockEntry] =
- for {
- deps <- getDepsForBlock(blockHeader.hash)
- txs <- getTransactionsByBlockHash(blockHeader.hash)
- } yield blockHeader.toApi(deps, txs)
-
def getBlockEntryLiteAction(
hash: BlockHash
- )(implicit ec: ExecutionContext): DBActionR[Option[BlockEntryLite]] =
- for {
- header <- BlockHeaderSchema.table.filter(_.hash === hash).result.headOption
- } yield header.map(_.toLiteApi)
+ ): DBActionR[Option[BlockEntryLite]] =
+ sql"""
+ select hash,
+ block_timestamp,
+ chain_from,
+ chain_to,
+ height,
+ main_chain,
+ hashrate,
+ txs_count
+ from #$block_headers
+ where hash = $hash
+ """.asASE[BlockEntryLite](blockEntryListGetResult).headOption
/** For a given `BlockHash` returns its basic chain information */
def getBlockChainInfo(hash: BlockHash): DBActionR[Option[(GroupIndex, GroupIndex, Boolean)]] =
@@ -93,8 +93,7 @@ object BlockQueries extends StrictLogging {
)(implicit ec: ExecutionContext): DBActionR[Option[BlockEntry]] =
for {
headers <- BlockHeaderSchema.table.filter(_.hash === hash).result
- blocks <- DBIOAction.sequence(headers.map(buildBlockEntryAction))
- } yield blocks.headOption
+ } yield headers.headOption.map(_.toApi())
def getBlockHeaderAction(hash: BlockHash): DBActionR[Option[BlockHeader]] =
sql"""
@@ -152,8 +151,7 @@ object BlockQueries extends StrictLogging {
): DBActionR[ArraySeq[BlockEntry]] =
for {
headers <- getHeadersAtHeightQuery(fromGroup, toGroup, height)
- blocks <- DBIOAction.sequence(headers.map(buildBlockEntryAction))
- } yield blocks
+ } yield headers.map(_.toApi())
/** Order by query for [[org.alephium.explorer.persistence.schema.BlockHeaderSchema.table]]
*/
@@ -268,21 +266,6 @@ object BlockQueries extends StrictLogging {
).asUpdate
}
- def buildBlockEntryWithoutTxsAction(
- blockHeader: BlockHeader
- )(implicit ec: ExecutionContext): DBActionR[BlockEntry] =
- for {
- deps <- getDepsForBlock(blockHeader.hash)
- } yield blockHeader.toApi(deps, ArraySeq.empty)
-
- def getBlockEntryWithoutTxsAction(
- hash: BlockHash
- )(implicit ec: ExecutionContext): DBActionR[Option[BlockEntry]] =
- for {
- headers <- BlockHeaderSchema.table.filter(_.hash === hash).result
- blocks <- DBIOAction.sequence(headers.map(buildBlockEntryWithoutTxsAction))
- } yield blocks.headOption
-
def getLatestBlock(chainFrom: GroupIndex, chainTo: GroupIndex): DBActionR[Option[LatestBlock]] = {
LatestBlockSchema.table
.filter { block =>
@@ -295,10 +278,10 @@ object BlockQueries extends StrictLogging {
/** Inserts block_headers or ignore them if there is a primary key conflict */
// scalastyle:off magic.number
def insertBlockHeaders(blocks: Iterable[BlockHeader]): DBActionW[Int] =
- QuerySplitter.splitUpdates(rows = blocks, columnsPerRow = 14) { (blocks, placeholder) =>
+ QuerySplitter.splitUpdates(rows = blocks, columnsPerRow = 16) { (blocks, placeholder) =>
val query =
s"""
- insert into $block_headers ("hash",
+ INSERT INTO $block_headers ("hash",
"block_timestamp",
"chain_from",
"chain_to",
@@ -311,8 +294,10 @@ object BlockQueries extends StrictLogging {
"txs_count",
"target",
"hashrate",
- "parent")
- values $placeholder
+ "parent",
+ "deps",
+ "ghost_uncles")
+ VALUES $placeholder
ON CONFLICT ON CONSTRAINT block_headers_pkey
DO NOTHING
"""
@@ -334,6 +319,8 @@ object BlockQueries extends StrictLogging {
params >> block.target
params >> block.hashrate
params >> block.parent
+ params >> block.deps
+ params >> block.ghostUncles
}
SQLActionBuilder(
@@ -348,7 +335,6 @@ object BlockQueries extends StrictLogging {
Array("org.wartremover.warts.MutableDataStructures", "org.wartremover.warts.NonUnitStatements")
)
def insertBlockEntity(blocks: Iterable[BlockEntity], groupNum: Int): DBActionRWT[Unit] = {
- val blockDeps = ListBuffer.empty[BlockDepEntity]
val transactions = ListBuffer.empty[TransactionEntity]
val inputs = ListBuffer.empty[InputEntity]
val outputs = ListBuffer.empty[OutputEntity]
@@ -356,7 +342,6 @@ object BlockQueries extends StrictLogging {
// build data for all insert queries in single iteration
blocks foreach { block =>
- if (block.height.value != 0) blockDeps addAll block.toBlockDepEntities()
transactions addAll block.transactions
inputs addAll block.inputs
outputs addAll block.outputs
@@ -365,7 +350,6 @@ object BlockQueries extends StrictLogging {
val query =
DBIOAction.seq(
- insertBlockDeps(blockDeps),
insertTransactions(transactions),
insertOutputs(outputs),
insertInputs(inputs),
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/InputQueries.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/InputQueries.scala
index 0d59ae5e4..65064dc59 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/InputQueries.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/InputQueries.scala
@@ -38,7 +38,7 @@ object InputQueries {
/** Inserts inputs or ignore rows with primary key conflict */
// scalastyle:off magic.number
def insertInputs(inputs: Iterable[InputEntity]): DBActionW[Int] =
- QuerySplitter.splitUpdates(rows = inputs, columnsPerRow = 12) { (inputs, placeholder) =>
+ QuerySplitter.splitUpdates(rows = inputs, columnsPerRow = 13) { (inputs, placeholder) =>
val query =
s"""
INSERT INTO inputs ("block_hash",
@@ -52,7 +52,8 @@ object InputQueries {
"tx_order",
"output_ref_tx_hash",
"output_ref_address",
- "output_ref_amount")
+ "output_ref_amount",
+ "contract_input")
VALUES $placeholder
ON CONFLICT
ON CONSTRAINT inputs_pk
@@ -74,6 +75,7 @@ object InputQueries {
params >> input.outputRefTxHash
params >> input.outputRefAddress
params >> input.outputRefAmount
+ params >> input.contractInput
}
SQLActionBuilder(
@@ -98,15 +100,7 @@ object InputQueries {
val query =
s"""
- SELECT tx_hash,
- input_order,
- hint,
- output_ref_key,
- unlock_script,
- output_ref_tx_hash,
- output_ref_address,
- output_ref_amount,
- output_ref_tokens
+ SELECT ${InputsFromTxQR.selectFields}
FROM inputs
WHERE (tx_hash, block_hash) IN $params
@@ -127,13 +121,7 @@ object InputQueries {
def getInputsQuery(txHash: TransactionId, blockHash: BlockHash): DBActionSR[InputsQR] =
sql"""
- SELECT hint,
- output_ref_key,
- unlock_script,
- output_ref_tx_hash,
- output_ref_address,
- output_ref_amount,
- output_ref_tokens
+ SELECT #${InputsQR.selectFields}
FROM inputs
WHERE tx_hash = $txHash
AND block_hash = $blockHash
@@ -155,7 +143,8 @@ object InputQueries {
output_ref_tx_hash,
output_ref_address,
output_ref_amount,
- output_ref_tokens
+ output_ref_tokens,
+ contract_input
FROM inputs
WHERE main_chain = true
ORDER BY block_timestamp #${if (ascendingOrder) "" else "DESC"}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/OutputQueries.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/OutputQueries.scala
index 6ca8451f2..c1d8ac45c 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/OutputQueries.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/OutputQueries.scala
@@ -50,7 +50,7 @@ object OutputQueries {
// scalastyle:off magic.number method.length
private def insertBasicOutputs(outputs: Iterable[OutputEntity]): DBActionW[Int] =
QuerySplitter
- .splitUpdates(rows = outputs, columnsPerRow = 17) { (outputs, placeholder) =>
+ .splitUpdates(rows = outputs, columnsPerRow = 18) { (outputs, placeholder) =>
val query =
s"""
INSERT INTO outputs ("block_hash",
@@ -69,7 +69,8 @@ object OutputQueries {
"tx_order",
"coinbase",
"spent_finalized",
- "spent_timestamp")
+ "spent_timestamp",
+ "fixed_output")
VALUES $placeholder
ON CONFLICT
ON CONSTRAINT outputs_pk
@@ -96,6 +97,7 @@ object OutputQueries {
params >> output.coinbase
params >> output.spentFinalized
params >> output.spentTimestamp
+ params >> output.fixedOutput
}
SQLActionBuilder(
@@ -316,17 +318,7 @@ object OutputQueries {
val query =
s"""
- SELECT outputs.tx_hash,
- outputs.output_order,
- outputs.output_type,
- outputs.hint,
- outputs.key,
- outputs.amount,
- outputs.address,
- outputs.tokens,
- outputs.lock_time,
- outputs.message,
- outputs.spent_finalized
+ SELECT ${OutputsFromTxQR.selectFields}
FROM outputs
WHERE (outputs.tx_hash, outputs.block_hash) IN $params
"""
@@ -348,15 +340,7 @@ object OutputQueries {
def getOutputsQuery(txHash: TransactionId, blockHash: BlockHash): DBActionSR[OutputsQR] =
sql"""
- SELECT output_type,
- hint,
- key,
- amount,
- address,
- tokens,
- lock_time,
- message,
- spent_finalized
+ SELECT #${OutputsQR.selectFields}
FROM outputs
WHERE tx_hash = $txHash
AND block_hash = $blockHash
@@ -383,7 +367,9 @@ object OutputQueries {
output_order,
tx_order,
coinbase,
- spent_finalized
+ spent_finalized,
+ spent_timestamp,
+ fixed_output
FROM outputs
WHERE main_chain = true
ORDER BY block_timestamp #${if (ascendingOrder) "" else "DESC"}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/TransactionQueries.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/TransactionQueries.scala
index 02d45641c..a7b6d31fd 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/TransactionQueries.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/TransactionQueries.scala
@@ -55,9 +55,9 @@ object TransactionQueries extends StrictLogging {
}
/** Inserts transactions or ignore rows with primary key conflict */
- // scalastyle:off magic.number
+ // scalastyle:off magic.number method.length
def insertTransactions(transactions: Iterable[TransactionEntity]): DBActionW[Int] =
- QuerySplitter.splitUpdates(rows = transactions, columnsPerRow = 13) {
+ QuerySplitter.splitUpdates(rows = transactions, columnsPerRow = 16) {
(transactions, placeholder) =>
val query =
s"""
@@ -66,6 +66,9 @@ object TransactionQueries extends StrictLogging {
block_timestamp,
chain_from,
chain_to,
+ version,
+ network_id,
+ script_opt,
gas_amount,
gas_price,
tx_order,
@@ -87,6 +90,9 @@ object TransactionQueries extends StrictLogging {
params >> transaction.timestamp
params >> transaction.chainFrom
params >> transaction.chainTo
+ params >> transaction.version
+ params >> transaction.networkId
+ params >> transaction.scriptOpt
params >> transaction.gasAmount
params >> transaction.gasPrice
params >> transaction.order
@@ -113,37 +119,25 @@ object TransactionQueries extends StrictLogging {
private val getTransactionQuery = Compiled { (txHash: Rep[TransactionId]) =>
mainTransactions
.filter(_.hash === txHash)
- .map(tx =>
- (tx.blockHash, tx.timestamp, tx.gasAmount, tx.gasPrice, tx.scriptExecutionOk, tx.coinbase)
- )
}
def getTransactionAction(
txHash: TransactionId
)(implicit ec: ExecutionContext): DBActionR[Option[Transaction]] =
getTransactionQuery(txHash).result.headOption.flatMap {
- case None => DBIOAction.successful(None)
- case Some((blockHash, timestamp, gasAmount, gasPrice, scriptExecutionOk, coinbase)) =>
- getKnownTransactionAction(
- txHash,
- blockHash,
- timestamp,
- gasAmount,
- gasPrice,
- scriptExecutionOk,
- coinbase
- ).map(Some.apply)
+ case None => DBIOAction.successful(None)
+ case Some(tx) => getKnownTransactionAction(tx).map(Some.apply)
}
private def getTxHashesByBlockHashQuery(
blockHash: BlockHash
- ): DBActionSR[(TransactionId, BlockHash, TimeStamp, Int, Boolean)] =
+ ): DBActionSR[TxByAddressQR] =
sql"""
SELECT hash, block_hash, block_timestamp, tx_order, coinbase
FROM transactions
WHERE block_hash = $blockHash
ORDER BY tx_order
- """.asAS[(TransactionId, BlockHash, TimeStamp, Int, Boolean)]
+ """.asAS[TxByAddressQR]
private def getTxHashesByBlockHashWithPaginationQuery(
blockHash: BlockHash,
@@ -156,7 +150,7 @@ object TransactionQueries extends StrictLogging {
ORDER BY tx_order
"""
.paginate(pagination)
- .asAS[(TransactionId, BlockHash, TimeStamp, Int, Boolean)]
+ .asAS[TxByAddressQR]
def countAddressTransactions(address: Address): DBActionSR[Int] = {
sql"""
@@ -257,7 +251,7 @@ object TransactionQueries extends StrictLogging {
)(implicit ec: ExecutionContext): DBActionSR[Transaction] = {
for {
txHashesTs <- getTxHashesByBlockHashQuery(blockHash)
- txs <- getTransactions(TxByAddressQR(txHashesTs))
+ txs <- getTransactions(txHashesTs)
} yield txs
}
@@ -266,7 +260,7 @@ object TransactionQueries extends StrictLogging {
): DBActionR[ArraySeq[Transaction]] = {
for {
txHashesTs <- getTxHashesByBlockHashWithPaginationQuery(blockHash, pagination)
- txs <- getTransactions(TxByAddressQR(txHashesTs))
+ txs <- getTransactions(txHashesTs)
} yield txs
}
@@ -431,28 +425,33 @@ object TransactionQueries extends StrictLogging {
val insByTx = inputs.groupBy(_.txHash).view.mapValues { values =>
values
.sortBy(_.inputOrder)
- .map(_.toApiInput())
+ .map(_.toApi())
}
val ousByTx = outputs.groupBy(_.txHash).view.mapValues { values =>
values
.sortBy(_.outputOrder)
- .map(_.toApiOutput())
+ .map(_.toApi())
}
- val gasByTx = gases.groupBy(_.txHash).view.mapValues(_.map(_.info()))
+ val gasByTx = gases.groupBy(_.txHash)
txHashesTs.map { txn =>
- val ins = insByTx.getOrElse(txn.txHash, ArraySeq.empty)
- val ous = ousByTx.getOrElse(txn.txHash, ArraySeq.empty)
- val gas = gasByTx.getOrElse(txn.txHash, ArraySeq.empty)
- val (gasAmount, gasPrice, scriptExecutionOk) = gas.headOption.getOrElse((0, U256.Zero, true))
+ val ins = insByTx.getOrElse(txn.txHash, ArraySeq.empty)
+ val ous = ousByTx.getOrElse(txn.txHash, ArraySeq.empty)
+ val gas = gasByTx.getOrElse(txn.txHash, ArraySeq.empty)
+ val info = gas.headOption.getOrElse(InfoFromTxsQR.empty())
Transaction(
txn.txHash,
txn.blockHash,
txn.blockTimestamp,
ins,
ous,
- gasAmount,
- gasPrice,
- scriptExecutionOk,
+ info.version,
+ info.networkId,
+ info.scriptOpt,
+ info.gasAmount,
+ info.gasPrice,
+ info.scriptExecutionOk,
+ info.inputSignatures.getOrElse(ArraySeq.empty),
+ info.scriptSignatures.getOrElse(ArraySeq.empty),
txn.coinbase
)
}
@@ -462,7 +461,7 @@ object TransactionQueries extends StrictLogging {
if (hashes.nonEmpty) {
val params = paramPlaceholderTuple2(1, hashes.size)
val query = s"""
- SELECT hash, gas_amount, gas_price, script_execution_ok
+ SELECT ${InfoFromTxsQR.selectFields}
FROM transactions
WHERE (hash, block_hash) IN $params
"""
@@ -483,28 +482,27 @@ object TransactionQueries extends StrictLogging {
}
private def getKnownTransactionAction(
- txHash: TransactionId,
- blockHash: BlockHash,
- timestamp: TimeStamp,
- gasAmount: Int,
- gasPrice: U256,
- scriptExecutionOk: Boolean,
- coinbase: Boolean
+ tx: TransactionEntity
)(implicit ec: ExecutionContext): DBActionR[Transaction] =
for {
- ins <- getInputsQuery(txHash, blockHash)
- outs <- getOutputsQuery(txHash, blockHash)
+ ins <- getInputsQuery(tx.hash, tx.blockHash)
+ outs <- getOutputsQuery(tx.hash, tx.blockHash)
} yield {
Transaction(
- txHash,
- blockHash,
- timestamp,
- ins.map(_.toApiInput()),
- outs.map(_.toApiOutput()),
- gasAmount,
- gasPrice,
- scriptExecutionOk,
- coinbase
+ tx.hash,
+ tx.blockHash,
+ tx.timestamp,
+ ins.map(_.toApi()),
+ outs.map(_.toApi()),
+ tx.version,
+ tx.networkId,
+ tx.scriptOpt,
+ tx.gasAmount,
+ tx.gasPrice,
+ tx.scriptExecutionOk,
+ tx.inputSignatures.getOrElse(ArraySeq.empty),
+ tx.scriptSignatures.getOrElse(ArraySeq.empty),
+ tx.coinbase
)
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InfoFromTxsQR.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InfoFromTxsQR.scala
index 8cbe4ae17..f462019f8 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InfoFromTxsQR.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InfoFromTxsQR.scala
@@ -16,6 +16,9 @@
package org.alephium.explorer.persistence.queries.result
+import scala.collection.immutable.ArraySeq
+
+import akka.util.ByteString
import slick.jdbc.{GetResult, PositionedResult}
import org.alephium.explorer.persistence.schema.CustomGetResult._
@@ -24,26 +27,50 @@ import org.alephium.util.U256
object InfoFromTxsQR {
+ val selectFields: String =
+ "hash, version, network_id, script_opt, gas_amount, gas_price, script_execution_ok, input_signatures, script_signatures"
+
implicit val infoFromTxsQRGetResult: GetResult[InfoFromTxsQR] =
(result: PositionedResult) =>
InfoFromTxsQR(
txHash = result.<<,
+ version = result.<<,
+ networkId = result.<<,
+ scriptOpt = result.<,
gasAmount = result.<<,
gasPrice = result.<<,
- scriptExecutionOk = result.<<
+ scriptExecutionOk = result.<<,
+ inputSignatures = result.<,
+ scriptSignatures = result.<
)
+ def empty(): InfoFromTxsQR =
+ InfoFromTxsQR(
+ TransactionId.zero,
+ 0,
+ 0,
+ None,
+ 0,
+ U256.Zero,
+ true,
+ None,
+ None
+ )
}
/** Query result for [[org.alephium.explorer.persistence.queries.TransactionQueries]] */
final case class InfoFromTxsQR(
txHash: TransactionId,
+ version: Byte,
+ networkId: Byte,
+ scriptOpt: Option[String],
gasAmount: Int,
gasPrice: U256,
- scriptExecutionOk: Boolean
+ scriptExecutionOk: Boolean,
+ inputSignatures: Option[ArraySeq[ByteString]],
+ scriptSignatures: Option[ArraySeq[ByteString]]
) {
def info(): (Int, U256, Boolean) =
(gasAmount, gasPrice, scriptExecutionOk)
-
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InputsFromTxQR.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InputsFromTxQR.scala
index ead7b3881..4111ad6a9 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InputsFromTxQR.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InputsFromTxQR.scala
@@ -22,12 +22,17 @@ import akka.util.ByteString
import slick.jdbc.{GetResult, PositionedResult}
import org.alephium.explorer.api.model._
+import org.alephium.explorer.persistence.model.InputEntityLike
import org.alephium.explorer.persistence.schema.CustomGetResult._
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{Address, TransactionId}
import org.alephium.util.U256
object InputsFromTxQR {
+
+ val selectFields: String =
+ "tx_hash, input_order, hint, output_ref_key, unlock_script, output_ref_tx_hash, output_ref_address, output_ref_amount, output_ref_tokens, contract_input"
+
implicit val inputsFromTxQRGetResult: GetResult[InputsFromTxQR] =
(result: PositionedResult) =>
InputsFromTxQR(
@@ -36,10 +41,11 @@ object InputsFromTxQR {
hint = result.<<,
outputRefKey = result.<<,
unlockScript = result.<,
- txHashRef = result.<,
- address = result.<,
- amount = result.<,
- token = result.<
+ outputRefTxHash = result.<,
+ outputRefAddress = result.<,
+ outputRefAmount = result.<,
+ outputRefTokens = result.<,
+ contractInput = result.<<
)
}
@@ -50,19 +56,9 @@ final case class InputsFromTxQR(
hint: Int,
outputRefKey: Hash,
unlockScript: Option[ByteString],
- txHashRef: Option[TransactionId],
- address: Option[Address],
- amount: Option[U256],
- token: Option[ArraySeq[Token]]
-) {
-
- def toApiInput(): Input =
- Input(
- outputRef = OutputRef(hint, outputRefKey),
- unlockScript = unlockScript,
- txHashRef = txHashRef,
- address = address,
- attoAlphAmount = amount,
- tokens = token
- )
-}
+ outputRefTxHash: Option[TransactionId],
+ outputRefAddress: Option[Address],
+ outputRefAmount: Option[U256],
+ outputRefTokens: Option[ArraySeq[Token]],
+ contractInput: Boolean
+) extends InputEntityLike
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InputsQR.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InputsQR.scala
index 8bbffb274..76e90318b 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InputsQR.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/InputsQR.scala
@@ -22,12 +22,17 @@ import akka.util.ByteString
import slick.jdbc.{GetResult, PositionedResult}
import org.alephium.explorer.api.model._
+import org.alephium.explorer.persistence.model.InputEntityLike
import org.alephium.explorer.persistence.schema.CustomGetResult._
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{Address, TransactionId}
import org.alephium.util.U256
object InputsQR {
+
+ val selectFields: String =
+ "hint, output_ref_key, unlock_script, output_ref_tx_hash, output_ref_address, output_ref_amount, output_ref_tokens, contract_input"
+
implicit val inputsQRGetResult: GetResult[InputsQR] =
(result: PositionedResult) =>
InputsQR(
@@ -37,7 +42,8 @@ object InputsQR {
outputRefTxHash = result.<,
outputRefAddress = result.<,
outputRefAmount = result.<,
- outputRefTokens = result.<
+ outputRefTokens = result.<,
+ contractInput = result.<<
)
}
@@ -49,16 +55,6 @@ final case class InputsQR(
outputRefTxHash: Option[TransactionId],
outputRefAddress: Option[Address],
outputRefAmount: Option[U256],
- outputRefTokens: Option[ArraySeq[Token]]
-) {
-
- def toApiInput(): Input =
- Input(
- outputRef = OutputRef(hint, outputRefKey),
- unlockScript = unlockScript,
- txHashRef = outputRefTxHash,
- address = outputRefAddress,
- attoAlphAmount = outputRefAmount,
- tokens = outputRefTokens
- )
-}
+ outputRefTokens: Option[ArraySeq[Token]],
+ contractInput: Boolean
+) extends InputEntityLike
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/OutputsFromTxQR.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/OutputsFromTxQR.scala
index 374fe18aa..47b2de553 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/OutputsFromTxQR.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/OutputsFromTxQR.scala
@@ -22,13 +22,17 @@ import akka.util.ByteString
import slick.jdbc.{GetResult, PositionedResult}
import org.alephium.explorer.api.model._
-import org.alephium.explorer.persistence.model.OutputEntity
+import org.alephium.explorer.persistence.model.{OutputEntity, OutputEntityLike}
import org.alephium.explorer.persistence.schema.CustomGetResult._
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{Address, TransactionId}
import org.alephium.util.{TimeStamp, U256}
object OutputsFromTxQR {
+
+ val selectFields: String =
+ "tx_hash, output_order, output_type, hint, key, amount, address, tokens, lock_time, message, spent_finalized, fixed_output"
+
implicit val outputsFromTxQRGetResult: GetResult[OutputsFromTxQR] =
(result: PositionedResult) =>
OutputsFromTxQR(
@@ -42,7 +46,8 @@ object OutputsFromTxQR {
tokens = result.<,
lockTime = result.<,
message = result.<,
- spent = result.<
+ spentFinalized = result.<,
+ fixedOutput = result.<<
)
}
@@ -58,30 +63,6 @@ final case class OutputsFromTxQR(
tokens: Option[ArraySeq[Token]],
lockTime: Option[TimeStamp],
message: Option[ByteString],
- spent: Option[TransactionId]
-) {
- def toApiOutput(): Output =
- outputType match {
- case OutputEntity.Asset =>
- AssetOutput(
- hint = hint,
- key = key,
- attoAlphAmount = amount,
- address = address,
- tokens = tokens,
- lockTime = lockTime,
- message = message,
- spent = spent
- )
-
- case OutputEntity.Contract =>
- ContractOutput(
- hint = hint,
- key = key,
- attoAlphAmount = amount,
- address = address,
- tokens = tokens,
- spent = spent
- )
- }
-}
+ spentFinalized: Option[TransactionId],
+ fixedOutput: Boolean
+) extends OutputEntityLike
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/OutputsQR.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/OutputsQR.scala
index 4d2b675e3..03ed37fd3 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/OutputsQR.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/OutputsQR.scala
@@ -22,13 +22,17 @@ import akka.util.ByteString
import slick.jdbc.{GetResult, PositionedResult}
import org.alephium.explorer.api.model._
-import org.alephium.explorer.persistence.model.OutputEntity
+import org.alephium.explorer.persistence.model.{OutputEntity, OutputEntityLike}
import org.alephium.explorer.persistence.schema.CustomGetResult._
import org.alephium.protocol.Hash
import org.alephium.protocol.model.{Address, TransactionId}
import org.alephium.util.{TimeStamp, U256}
object OutputsQR {
+
+ val selectFields: String =
+ "output_type, hint, key, amount, address, tokens, lock_time, message, spent_finalized, fixed_output"
+
implicit val outputsQRGetResult: GetResult[OutputsQR] =
(result: PositionedResult) =>
OutputsQR(
@@ -40,7 +44,8 @@ object OutputsQR {
tokens = result.<,
lockTime = result.<,
message = result.<,
- spentFinalized = result.<
+ spentFinalized = result.<,
+ fixedOutput = result.<<
)
}
@@ -54,31 +59,6 @@ final case class OutputsQR(
tokens: Option[ArraySeq[Token]],
lockTime: Option[TimeStamp],
message: Option[ByteString],
- spentFinalized: Option[TransactionId]
-) {
-
- def toApiOutput(): Output =
- outputType match {
- case OutputEntity.Asset =>
- AssetOutput(
- hint = hint,
- key = key,
- attoAlphAmount = amount,
- address = address,
- tokens = tokens,
- lockTime = lockTime,
- message = message,
- spent = spentFinalized
- )
-
- case OutputEntity.Contract =>
- ContractOutput(
- hint = hint,
- key = key,
- attoAlphAmount = amount,
- address = address,
- tokens = tokens,
- spent = spentFinalized
- )
- }
-}
+ spentFinalized: Option[TransactionId],
+ fixedOutput: Boolean
+) extends OutputEntityLike
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/TxByTokenQR.scala b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/TxByTokenQR.scala
index ab2c33d73..fa07f73b4 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/queries/result/TxByTokenQR.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/queries/result/TxByTokenQR.scala
@@ -26,7 +26,8 @@ import org.alephium.util.TimeStamp
object TxByTokenQR {
- val selectFields = "tx_hash, block_hash, block_timestamp, tx_order"
+ val selectFields: String =
+ "tx_hash, block_hash, block_timestamp, tx_order"
private type Tuple = (TransactionId, BlockHash, TimeStamp, Int, Boolean)
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/schema/BlockDepsSchema.scala b/app/src/main/scala/org/alephium/explorer/persistence/schema/BlockDepsSchema.scala
deleted file mode 100644
index 124e3bd45..000000000
--- a/app/src/main/scala/org/alephium/explorer/persistence/schema/BlockDepsSchema.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2018 The Alephium Authors
-// This file is part of the alephium project.
-//
-// The library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the library. If not, see .
-
-package org.alephium.explorer.persistence.schema
-
-import slick.jdbc.PostgresProfile.api._
-import slick.lifted.{Index, PrimaryKey, ProvenShape}
-
-import org.alephium.explorer.persistence.model.BlockDepEntity
-import org.alephium.explorer.persistence.schema.CustomJdbcTypes._
-import org.alephium.protocol.model.BlockHash
-
-object BlockDepsSchema extends Schema[BlockDepEntity]("block_deps") {
-
- class BlockDeps(tag: Tag) extends Table[BlockDepEntity](tag, name) {
- def hash: Rep[BlockHash] = column[BlockHash]("hash", O.SqlType("BYTEA"))
- def dep: Rep[BlockHash] = column[BlockHash]("dep", O.SqlType("BYTEA"))
- def depOrder: Rep[Int] = column[Int]("dep_order")
-
- def pk: PrimaryKey = primaryKey("hash_deps_pk", (hash, dep))
- def depIdx: Index = index("deps_dep_idx", dep)
-
- def * : ProvenShape[BlockDepEntity] =
- (hash, dep, depOrder).<>((BlockDepEntity.apply _).tupled, BlockDepEntity.unapply)
- }
-
- val table: TableQuery[BlockDeps] = TableQuery[BlockDeps]
-}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/schema/BlockHeaderSchema.scala b/app/src/main/scala/org/alephium/explorer/persistence/schema/BlockHeaderSchema.scala
index 5ee5d7e18..bea9ccce6 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/schema/BlockHeaderSchema.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/schema/BlockHeaderSchema.scala
@@ -18,12 +18,14 @@ package org.alephium.explorer.persistence.schema
import java.math.BigInteger
+import scala.collection.immutable.ArraySeq
+
import akka.util.ByteString
import slick.jdbc.PostgresProfile.api._
import slick.lifted.{Index, ProvenShape}
import slick.sql.SqlAction
-import org.alephium.explorer.api.model.Height
+import org.alephium.explorer.api.model.{GhostUncle, Height}
import org.alephium.explorer.persistence.model.BlockHeader
import org.alephium.explorer.persistence.schema.CustomJdbcTypes._
import org.alephium.protocol.Hash
@@ -52,6 +54,9 @@ object BlockHeaderSchema extends SchemaMainChain[BlockHeader]("block_headers") {
O.SqlType("DECIMAL(80,0)")
) // TODO How much decimal we need? this one is the same as for U256
def parent: Rep[Option[BlockHash]] = column[Option[BlockHash]]("parent")
+ def deps: Rep[ArraySeq[BlockHash]] = column[ArraySeq[BlockHash]]("deps")
+ def ghostUncles: Rep[Option[ArraySeq[GhostUncle]]] =
+ column[Option[ArraySeq[GhostUncle]]]("ghost_uncles")
def timestampIdx: Index = index("blocks_timestamp_idx", timestamp)
def heightIdx: Index = index("blocks_height_idx", height)
@@ -71,7 +76,9 @@ object BlockHeaderSchema extends SchemaMainChain[BlockHeader]("block_headers") {
txsCount,
target,
hashrate,
- parent
+ parent,
+ deps,
+ ghostUncles
)
.<>((BlockHeader.apply _).tupled, BlockHeader.unapply)
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomGetResult.scala b/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomGetResult.scala
index 98f534b51..6a325f71c 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomGetResult.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomGetResult.scala
@@ -100,6 +100,17 @@ object CustomGetResult {
(result: PositionedResult) =>
result.nextBytesOption().map(bytes => ByteString.fromArrayUnsafe(bytes))
+ implicit val optionByteStringsGetResult: GetResult[Option[ArraySeq[ByteString]]] =
+ (result: PositionedResult) =>
+ result
+ .nextBytesOption()
+ .map { bytes =>
+ deserialize[ArraySeq[ByteString]](ByteString.fromArrayUnsafe(bytes)) match {
+ case Left(error) => throw error
+ case Right(value) => value
+ }
+ }
+
implicit val optionTokensGetResult: GetResult[Option[ArraySeq[Token]]] =
(result: PositionedResult) =>
result
@@ -111,6 +122,13 @@ object CustomGetResult {
}
}
+ implicit val blockHashesGetResult: GetResult[ArraySeq[BlockHash]] =
+ (result: PositionedResult) =>
+ deserialize[ArraySeq[BlockHash]](ByteString.fromArrayUnsafe(result.nextBytes())) match {
+ case Left(error) => throw error
+ case Right(value) => value
+ }
+
implicit val valsGetResult: GetResult[ArraySeq[Val]] =
(result: PositionedResult) => readBinary[ArraySeq[Val]](result.nextBytes())
@@ -165,7 +183,8 @@ object CustomGetResult {
txOrder = result.<<,
coinbase = result.<<,
spentFinalized = result.<,
- spentTimestamp = result.<
+ spentTimestamp = result.<,
+ fixedOutput = result.<<
)
val inputGetResult: GetResult[InputEntity] =
@@ -183,7 +202,8 @@ object CustomGetResult {
outputRefTxHash = result.<,
outputRefAddress = result.<,
outputRefAmount = result.<,
- outputRefTokens = result.<
+ outputRefTokens = result.<,
+ contractInput = result.<<
)
implicit val outputTypeGetResult: GetResult[OutputEntity.OutputType] =
@@ -195,6 +215,10 @@ object CustomGetResult {
implicit val optionInterfaceIdGetResult: GetResult[Option[InterfaceIdEntity]] =
(result: PositionedResult) => result.nextStringOption().map(InterfaceIdEntity.from)
+ implicit val optionGhostUnclesGetResult: GetResult[Option[ArraySeq[GhostUncle]]] =
+ (result: PositionedResult) =>
+ result.nextBytesOption().map(bytes => readBinary[ArraySeq[GhostUncle]](bytes))
+
/** GetResult type for BlockEntryLite
*
* @note
@@ -231,7 +255,9 @@ object CustomGetResult {
txsCount = result.<<,
target = result.<<,
hashrate = result.<<,
- parent = result.<
+ parent = result.<,
+ deps = result.<<,
+ ghostUncles = result.<
)
val mempoolTransactionGetResult: GetResult[MempoolTransactionEntity] =
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomJdbcTypes.scala b/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomJdbcTypes.scala
index 98f9302da..f11b60f3e 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomJdbcTypes.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomJdbcTypes.scala
@@ -141,6 +141,16 @@ object CustomJdbcTypes {
}
)
+ implicit val seqBlockHashType: JdbcType[ArraySeq[BlockHash]] =
+ MappedJdbcType.base[ArraySeq[BlockHash], Array[Byte]](
+ hashes => serialize(hashes).toArray,
+ bytes =>
+ deserialize[ArraySeq[BlockHash]](ByteString.fromArrayUnsafe(bytes)) match {
+ case Left(error) => throw error
+ case Right(value) => value
+ }
+ )
+
implicit val valsType: JdbcType[ArraySeq[Val]] =
MappedJdbcType.base[ArraySeq[Val], Array[Byte]](
vals => writeBinary(vals),
@@ -174,4 +184,10 @@ object CustomJdbcTypes {
.find(_.key == key)
.getOrElse(throw new Exception(s"Invalid ${classOf[AppStateKey[_]].getSimpleName}: $key"))
)
+
+ implicit val ghostUnclesType: JdbcType[ArraySeq[GhostUncle]] =
+ MappedJdbcType.base[ArraySeq[GhostUncle], Array[Byte]](
+ uncles => writeBinary(uncles),
+ bytes => readBinary[ArraySeq[GhostUncle]](bytes)
+ )
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomSetParameter.scala b/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomSetParameter.scala
index e6f974dd8..ab25b08e3 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomSetParameter.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/schema/CustomSetParameter.scala
@@ -176,6 +176,11 @@ object CustomSetParameter {
}
}
+ implicit object BlockHashesStringsOptionSetParameter extends SetParameter[ArraySeq[BlockHash]] {
+ override def apply(input: ArraySeq[BlockHash], params: PositionedParameters): Unit =
+ params setBytes serialize(input).toArray
+ }
+
implicit object ValsSetParameter extends SetParameter[ArraySeq[Val]] {
override def apply(input: ArraySeq[Val], params: PositionedParameters): Unit =
params setBytes writeBinary(input)
@@ -255,4 +260,22 @@ object CustomSetParameter {
params setTimestampOption None
}
}
+
+ implicit object GhostUnclesSetParameter extends SetParameter[ArraySeq[GhostUncle]] {
+ override def apply(input: ArraySeq[GhostUncle], params: PositionedParameters): Unit =
+ params setBytes writeBinary(input)
+ }
+
+ implicit object GhostUnclesOptionSetParameter extends SetParameter[Option[ArraySeq[GhostUncle]]] {
+ override def apply(option: Option[ArraySeq[GhostUncle]], params: PositionedParameters): Unit =
+ option match {
+ case Some(ghostUncles) =>
+ GhostUnclesSetParameter(ghostUncles, params)
+
+ case None =>
+ // scalastyle:off null
+ params setBytes null
+ // scalastyle:on null
+ }
+ }
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/schema/InputSchema.scala b/app/src/main/scala/org/alephium/explorer/persistence/schema/InputSchema.scala
index 88093f0c3..443f5f93b 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/schema/InputSchema.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/schema/InputSchema.scala
@@ -52,6 +52,7 @@ object InputSchema extends SchemaMainChain[InputEntity]("inputs") {
) // U256.MaxValue has 78 digits
def outputRefTokens: Rep[Option[ArraySeq[Token]]] =
column[Option[ArraySeq[Token]]]("output_ref_tokens")
+ def contractInput: Rep[Boolean] = column[Boolean]("contract_input")
def pk: PrimaryKey = primaryKey("inputs_pk", (outputRefKey, blockHash))
@@ -75,7 +76,8 @@ object InputSchema extends SchemaMainChain[InputEntity]("inputs") {
outputRefTxHash,
outputRefAddress,
outputRefAmount,
- outputRefTokens
+ outputRefTokens,
+ contractInput
)
.<>((InputEntity.apply _).tupled, InputEntity.unapply)
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/schema/OutputSchema.scala b/app/src/main/scala/org/alephium/explorer/persistence/schema/OutputSchema.scala
index 8c6707889..0778716de 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/schema/OutputSchema.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/schema/OutputSchema.scala
@@ -52,6 +52,7 @@ object OutputSchema extends SchemaMainChain[OutputEntity]("outputs") {
def spentFinalized: Rep[Option[TransactionId]] =
column[Option[TransactionId]]("spent_finalized", O.Default(None))
def spentTimestamp: Rep[Option[TimeStamp]] = column[Option[TimeStamp]]("spent_timestamp")
+ def fixedOutput: Rep[Boolean] = column[Boolean]("fixed_output")
def pk: PrimaryKey = primaryKey("outputs_pk", (key, blockHash))
@@ -80,7 +81,8 @@ object OutputSchema extends SchemaMainChain[OutputEntity]("outputs") {
txOrder,
coinbase,
spentFinalized,
- spentTimestamp
+ spentTimestamp,
+ fixedOutput
)
.<>((OutputEntity.apply _).tupled, OutputEntity.unapply)
}
diff --git a/app/src/main/scala/org/alephium/explorer/persistence/schema/TransactionSchema.scala b/app/src/main/scala/org/alephium/explorer/persistence/schema/TransactionSchema.scala
index d800074db..b80afe53f 100644
--- a/app/src/main/scala/org/alephium/explorer/persistence/schema/TransactionSchema.scala
+++ b/app/src/main/scala/org/alephium/explorer/persistence/schema/TransactionSchema.scala
@@ -30,12 +30,15 @@ import org.alephium.util.{TimeStamp, U256}
object TransactionSchema extends SchemaMainChain[TransactionEntity]("transactions") {
class Transactions(tag: Tag) extends Table[TransactionEntity](tag, name) {
- def hash: Rep[TransactionId] = column[TransactionId]("hash", O.SqlType("BYTEA"))
- def blockHash: Rep[BlockHash] = column[BlockHash]("block_hash", O.SqlType("BYTEA"))
- def timestamp: Rep[TimeStamp] = column[TimeStamp]("block_timestamp")
- def chainFrom: Rep[GroupIndex] = column[GroupIndex]("chain_from")
- def chainTo: Rep[GroupIndex] = column[GroupIndex]("chain_to")
- def gasAmount: Rep[Int] = column[Int]("gas_amount")
+ def hash: Rep[TransactionId] = column[TransactionId]("hash", O.SqlType("BYTEA"))
+ def blockHash: Rep[BlockHash] = column[BlockHash]("block_hash", O.SqlType("BYTEA"))
+ def timestamp: Rep[TimeStamp] = column[TimeStamp]("block_timestamp")
+ def chainFrom: Rep[GroupIndex] = column[GroupIndex]("chain_from")
+ def chainTo: Rep[GroupIndex] = column[GroupIndex]("chain_to")
+ def version: Rep[Byte] = column[Byte]("version")
+ def networkId: Rep[Byte] = column[Byte]("network_id")
+ def scriptOpt: Rep[Option[String]] = column[Option[String]]("script_opt")
+ def gasAmount: Rep[Int] = column[Int]("gas_amount")
def gasPrice: Rep[U256] =
column[U256]("gas_price", O.SqlType("DECIMAL(80,0)")) // U256.MaxValue has 78 digits
def txOrder: Rep[Int] = column[Int]("tx_order")
@@ -61,6 +64,9 @@ object TransactionSchema extends SchemaMainChain[TransactionEntity]("transaction
timestamp,
chainFrom,
chainTo,
+ version,
+ networkId,
+ scriptOpt,
gasAmount,
gasPrice,
txOrder,
diff --git a/app/src/main/scala/org/alephium/explorer/service/BlockFlowClient.scala b/app/src/main/scala/org/alephium/explorer/service/BlockFlowClient.scala
index 20ea89e07..8e7a3fcb0 100644
--- a/app/src/main/scala/org/alephium/explorer/service/BlockFlowClient.scala
+++ b/app/src/main/scala/org/alephium/explorer/service/BlockFlowClient.scala
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see .
+//scalastyle:off file.size.limit
package org.alephium.explorer.service
import java.math.BigInteger
@@ -441,7 +442,16 @@ object BlockFlowClient extends StrictLogging {
transactions.flatMap { case (tx, txOrder) =>
InputAddressUtil.convertSameAsPrevious(tx.unsigned.inputs.toArraySeq).zipWithIndex.map {
case (in, index) =>
- inputToEntity(in, hash, tx.unsigned.txId, block.timestamp, mainChain, index, txOrder)
+ inputToEntity(
+ in,
+ hash,
+ tx.unsigned.txId,
+ block.timestamp,
+ mainChain,
+ index,
+ txOrder,
+ contractInput = false
+ )
}
}
val contractInputs =
@@ -455,7 +465,8 @@ object BlockFlowClient extends StrictLogging {
block.timestamp,
mainChain,
shiftIndex,
- txOrder
+ txOrder,
+ contractInput = true
)
}
}
@@ -482,7 +493,8 @@ object BlockFlowClient extends StrictLogging {
block.timestamp,
mainChain,
txOrder,
- txId == coinbaseTxId
+ coinbase = txId == coinbaseTxId,
+ fixedOutput = true
)
}
}
@@ -498,7 +510,8 @@ object BlockFlowClient extends StrictLogging {
block.timestamp,
mainChain,
txOrder,
- false
+ coinbase = false,
+ fixedOutput = false
)
}
}
@@ -531,6 +544,10 @@ object BlockFlowClient extends StrictLogging {
// Genesis blocks don't have any transactions
val coinbaseTxId =
if (block.height == Height.genesis.value) null else block.transactions.last.unsigned.txId
+ val ghostUncles = block.ghostUncles.toArraySeq.map { ghostUncle =>
+ GhostUncle(ghostUncle.blockHash, ghostUncle.miner)
+ }
+
BlockEntity(
hash,
block.timestamp,
@@ -550,7 +567,8 @@ object BlockFlowClient extends StrictLogging {
block.depStateHash,
block.txsHash,
block.target,
- computeHashRate(block.target, block.timestamp)
+ computeHashRate(block.target, block.timestamp),
+ ghostUncles
)
}
// scalastyle:on null
@@ -618,6 +636,9 @@ object BlockFlowClient extends StrictLogging {
timestamp,
chainFrom,
chainTo,
+ tx.unsigned.version,
+ tx.unsigned.networkId,
+ tx.unsigned.scriptOpt.map(_.value),
tx.unsigned.gasAmount,
tx.unsigned.gasPrice,
index,
@@ -635,7 +656,8 @@ object BlockFlowClient extends StrictLogging {
None,
InputAddressUtil.addressFromProtocolInput(input),
None,
- None
+ None,
+ contractInput = false
)
}
@@ -646,7 +668,8 @@ object BlockFlowClient extends StrictLogging {
timestamp: TimeStamp,
mainChain: Boolean,
index: Int,
- txOrder: Int
+ txOrder: Int,
+ contractInput: Boolean
): InputEntity = {
InputEntity(
blockHash,
@@ -661,7 +684,8 @@ object BlockFlowClient extends StrictLogging {
None,
InputAddressUtil.addressFromProtocolInput(input),
None,
- None
+ None,
+ contractInput = contractInput
)
}
@@ -672,7 +696,8 @@ object BlockFlowClient extends StrictLogging {
timestamp: TimeStamp,
mainChain: Boolean,
index: Int,
- txOrder: Int
+ txOrder: Int,
+ contractInput: Boolean
): InputEntity = {
InputEntity(
blockHash,
@@ -687,7 +712,8 @@ object BlockFlowClient extends StrictLogging {
None,
None,
None,
- None
+ None,
+ contractInput = contractInput
)
}
@@ -704,7 +730,8 @@ object BlockFlowClient extends StrictLogging {
protocolTokensToTokens(output.tokens),
lockTime,
Some(output.message),
- None
+ None,
+ fixedOutput = true
)
}
@@ -735,18 +762,14 @@ object BlockFlowClient extends StrictLogging {
timestamp: TimeStamp,
mainChain: Boolean,
txOrder: Int,
- coinbase: Boolean
+ coinbase: Boolean,
+ fixedOutput: Boolean
): OutputEntity = {
val lockTime = output match {
case asset: api.model.AssetOutput if asset.lockTime.millis > 0 => Some(asset.lockTime)
case _ => None
}
- val hint = output.address.lockupScript match {
- case asset: LockupScript.Asset => Hint.ofAsset(asset.scriptHint)
- case contract: LockupScript.P2C => Hint.ofContract(contract.scriptHint)
- }
-
val outputType: OutputEntity.OutputType = output match {
case _: api.model.AssetOutput => OutputEntity.Asset
case _: api.model.ContractOutput => OutputEntity.Contract
@@ -764,8 +787,8 @@ object BlockFlowClient extends StrictLogging {
txId,
timestamp,
outputType,
- hint.value,
- protocol.model.TxOutputRef.key(txId, index).value,
+ output.hint,
+ output.key,
output.attoAlphAmount.value,
output.address,
tokens,
@@ -776,7 +799,8 @@ object BlockFlowClient extends StrictLogging {
txOrder,
coinbase,
None,
- None
+ None,
+ fixedOutput
)
}
diff --git a/app/src/main/scala/org/alephium/explorer/service/BlockFlowSyncService.scala b/app/src/main/scala/org/alephium/explorer/service/BlockFlowSyncService.scala
index 3795cdfb6..e0dacdc97 100644
--- a/app/src/main/scala/org/alephium/explorer/service/BlockFlowSyncService.scala
+++ b/app/src/main/scala/org/alephium/explorer/service/BlockFlowSyncService.scala
@@ -344,6 +344,7 @@ case object BlockFlowSyncService extends StrictLogging {
}).flatMap { _ =>
for {
_ <- BlockDao.insertWithEvents(block, events)
+ _ <- handleUncles(block.ghostUncles.map(_.blockHash), block.chainFrom)
_ <- BlockDao.updateMainChain(
block.hash,
block.chainFrom,
@@ -381,4 +382,25 @@ case object BlockFlowSyncService extends StrictLogging {
logger.debug(s"Downloading missing block $missing")
blockFlowClient.fetchBlockAndEvents(chainFrom, missing).flatMap(insertWithEvents)
}
+
+ // Ghost uncle blocks are only insterted in the database, we don't update the main chain
+ private def handleUncles(uncles: ArraySeq[BlockHash], chainFrom: GroupIndex)(implicit
+ ec: ExecutionContext,
+ dc: DatabaseConfig[PostgresProfile],
+ blockFlowClient: BlockFlowClient,
+ groupSetting: GroupSetting
+ ): Future[Unit] = {
+ if (uncles.nonEmpty) {
+ logger.debug(s"Downloading ghost uncles ${uncles}")
+ Future
+ .sequence(uncles.map { uncle =>
+ blockFlowClient
+ .fetchBlockAndEvents(chainFrom, uncle)
+ .flatMap(bwe => BlockDao.insertWithEvents(bwe.block, bwe.events))
+ })
+ .map(_ => ())
+ } else {
+ Future.successful(())
+ }
+ }
}
diff --git a/app/src/main/scala/org/alephium/explorer/service/BlockService.scala b/app/src/main/scala/org/alephium/explorer/service/BlockService.scala
index 628ec33a7..93ebeafdb 100644
--- a/app/src/main/scala/org/alephium/explorer/service/BlockService.scala
+++ b/app/src/main/scala/org/alephium/explorer/service/BlockService.scala
@@ -29,6 +29,11 @@ import org.alephium.explorer.persistence.dao.BlockDao
import org.alephium.protocol.model.BlockHash
trait BlockService {
+ def getBlockByHash(hash: BlockHash)(implicit
+ ec: ExecutionContext,
+ dc: DatabaseConfig[PostgresProfile]
+ ): Future[Option[BlockEntry]]
+
def getLiteBlockByHash(hash: BlockHash)(implicit
ec: ExecutionContext,
dc: DatabaseConfig[PostgresProfile]
@@ -59,6 +64,11 @@ trait BlockService {
}
object BlockService extends BlockService {
+ def getBlockByHash(hash: BlockHash)(implicit
+ ec: ExecutionContext,
+ dc: DatabaseConfig[PostgresProfile]
+ ): Future[Option[BlockEntry]] =
+ BlockDao.get(hash)
def getLiteBlockByHash(hash: BlockHash)(implicit
ec: ExecutionContext,
diff --git a/app/src/main/scala/org/alephium/explorer/service/FlowEntity.scala b/app/src/main/scala/org/alephium/explorer/service/FlowEntity.scala
index a30d6457d..429456095 100644
--- a/app/src/main/scala/org/alephium/explorer/service/FlowEntity.scala
+++ b/app/src/main/scala/org/alephium/explorer/service/FlowEntity.scala
@@ -18,8 +18,11 @@ package org.alephium.explorer.service
import scala.collection.immutable.ArraySeq
+import akka.util.ByteString
+
import org.alephium.explorer.AnyOps
-import org.alephium.explorer.api.model.Height
+import org.alephium.explorer.api.model.{GhostUncle, Height}
+import org.alephium.protocol.Hash
import org.alephium.protocol.model.{BlockHash, GroupIndex}
import org.alephium.util.TimeStamp
@@ -31,6 +34,12 @@ trait FlowEntity {
def chainTo: GroupIndex
def height: Height
def deps: ArraySeq[BlockHash]
+ def nonce: ByteString
+ def version: Byte
+ def depStateHash: Hash
+ def txsHash: Hash
+ def target: ByteString
+ def ghostUncles: ArraySeq[GhostUncle]
def mainChain: Boolean
def parent(groupNum: Int): Option[BlockHash] =
diff --git a/app/src/main/scala/org/alephium/explorer/service/SanityChecker.scala b/app/src/main/scala/org/alephium/explorer/service/SanityChecker.scala
index 7065e7fc8..e196d5044 100644
--- a/app/src/main/scala/org/alephium/explorer/service/SanityChecker.scala
+++ b/app/src/main/scala/org/alephium/explorer/service/SanityChecker.scala
@@ -166,7 +166,7 @@ object SanityChecker extends StrictLogging {
s"Checked $blockNum blocks , progress ${(nextBlockNum.toFloat / totalNbOfBlocks * 100.0).toInt}%"
)
}
- getBlockEntryWithoutTxsAction(hash)
+ getBlockEntryAction(hash)
.flatMap {
case Some(block) if !block.mainChain =>
logger.debug(s"Updating block ${block.hash} which should be on the mainChain")
diff --git a/app/src/main/scala/org/alephium/explorer/web/BlockServer.scala b/app/src/main/scala/org/alephium/explorer/web/BlockServer.scala
index 22af22a37..1259f9bd8 100644
--- a/app/src/main/scala/org/alephium/explorer/web/BlockServer.scala
+++ b/app/src/main/scala/org/alephium/explorer/web/BlockServer.scala
@@ -39,7 +39,7 @@ class BlockServer(implicit
route(listBlocks.serverLogicSuccess[Future](BlockService.listBlocks(_))),
route(getBlockByHash.serverLogic[Future] { hash =>
BlockService
- .getLiteBlockByHash(hash)
+ .getBlockByHash(hash)
.map(_.toRight(ApiError.NotFound(hash.value.toHexString)))
}),
route(getBlockTransactions.serverLogicSuccess[Future] { case (hash, pagination) =>
diff --git a/app/src/test/scala/org/alephium/explorer/BlockModelConversionSpec.scala b/app/src/test/scala/org/alephium/explorer/BlockModelConversionSpec.scala
new file mode 100644
index 000000000..fc5952f40
--- /dev/null
+++ b/app/src/test/scala/org/alephium/explorer/BlockModelConversionSpec.scala
@@ -0,0 +1,60 @@
+// Copyright 2018 The Alephium Authors
+// This file is part of the alephium project.
+//
+// The library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the library. If not, see .
+
+package org.alephium.explorer
+
+import scala.collection.immutable.ArraySeq
+
+import org.alephium.explorer.AlephiumSpec
+import org.alephium.explorer.ConfigDefaults.groupSetting
+import org.alephium.explorer.GenCoreApi._
+import org.alephium.explorer.api.model._
+import org.alephium.explorer.persistence.model._
+import org.alephium.explorer.service.BlockFlowClient
+
+class BlockModelConversionSpec() extends AlephiumSpec {
+
+ "BlockEntry" should {
+ "be converted to and from core api BlockEntry" in new Fixture {
+ forAll(blockEntryProtocolGen) { protocolBlockEntry =>
+ val blockEntity = BlockFlowClient.blockProtocolToEntity(protocolBlockEntry)
+
+ blockEntityToProtocol(blockEntity) is protocolBlockEntry
+ }
+ }
+ }
+
+ trait Fixture {
+
+ def blockEntityToProtocol(blockEntity: BlockEntity): org.alephium.api.model.BlockEntry = {
+
+ val transactions = transactionsApiFromBlockEntity(blockEntity)
+
+ blockEntity.toBlockHeader(groupSetting.groupNum).toApi().toProtocol(transactions)
+ }
+
+ def transactionsApiFromBlockEntity(
+ block: BlockEntity
+ ): ArraySeq[Transaction] = {
+ block.transactions.map { tx =>
+ tx.toApi(
+ block.inputs.filter(_.txHash == tx.hash).sortBy(_.inputOrder).map(_.toApi()),
+ block.outputs.filter(_.txHash == tx.hash).sortBy(_.outputOrder).map(_.toApi())
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/test/scala/org/alephium/explorer/ExplorerSpec.scala b/app/src/test/scala/org/alephium/explorer/ExplorerSpec.scala
index 898c1c2b9..b2e98273b 100644
--- a/app/src/test/scala/org/alephium/explorer/ExplorerSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/ExplorerSpec.scala
@@ -79,11 +79,27 @@ trait ExplorerSpec
val blockflow: ArraySeq[ArraySeq[model.BlockEntry]] =
blockFlowGen(maxChainSize = 5, startTimestamp = TimeStamp.now()).sample.get
+ val uncles = blockflow
+ .map(_.flatMap { block =>
+ block.ghostUncles.map { uncle =>
+ blockEntryProtocolGen.sample.get.copy(
+ hash = uncle.blockHash,
+ timestamp = block.timestamp,
+ chainFrom = block.chainFrom,
+ chainTo = block.chainTo
+ )
+
+ }
+ })
+ .flatten
+
val blocksProtocol: ArraySeq[model.BlockEntry] = blockflow.flatten
val blockEntities: ArraySeq[BlockEntity] =
blocksProtocol.map(BlockFlowClient.blockProtocolToEntity)
- val blocks: ArraySeq[BlockEntry] = blockEntitiesToBlockEntries(ArraySeq(blockEntities)).flatten
+ val blocks: ArraySeq[BlockEntryTest] = blockEntitiesToBlockEntries(
+ ArraySeq(blockEntities)
+ ).flatten
val transactions: ArraySeq[Transaction] = blocks.flatMap(_.transactions)
@@ -95,7 +111,7 @@ trait ExplorerSpec
val blockFlowPort = SocketUtil.temporaryLocalPort(SocketUtil.Both)
val blockFlowMock =
- new ExplorerSpec.BlockFlowServerMock(localhost, blockFlowPort, blockflow, networkId)
+ new ExplorerSpec.BlockFlowServerMock(localhost, blockFlowPort, blockflow, uncles, networkId)
val coingeckoPort = SocketUtil.temporaryLocalPort(SocketUtil.Both)
val coingeckoUri = s"http://${localhost.getHostAddress()}:$coingeckoPort"
@@ -382,6 +398,15 @@ trait ExplorerSpec
}
}
+ "insert uncle blocks" in {
+ uncles.foreach { uncle =>
+ Get(s"/blocks/${uncle.hash.toHexString}") check { response =>
+ val res = response.as[BlockEntry]
+ res.mainChain is false
+ }
+ }
+ }
+
"generate the documentation" in {
Get("/docs") check { response =>
response.code is StatusCode.Ok
@@ -425,6 +450,7 @@ object ExplorerSpec {
address: InetAddress,
port: Int,
blockflow: ArraySeq[ArraySeq[model.BlockEntry]],
+ uncles: ArraySeq[model.BlockEntry],
networkId: NetworkId
)(implicit groupSetting: GroupSetting)
extends ApiModelCodec
@@ -436,6 +462,7 @@ object ExplorerSpec {
implicit val groupConfig: GroupConfig = groupSetting.groupConfig
val blocks = blockflow.flatten
+ val blocksWithUncles = blockflow.flatten ++ uncles
val cliqueId = CliqueId.generate
@@ -482,7 +509,7 @@ object ExplorerSpec {
.in(path[BlockHash])
.out(jsonBody[model.BlockEntry])
.serverLogicSuccess[Future] { hash =>
- Future.successful(blocks.find(_.hash === hash).get)
+ Future.successful(blocksWithUncles.find(_.hash === hash).get)
}
),
route(
@@ -495,7 +522,7 @@ object ExplorerSpec {
Future
.successful(
model.BlockAndEvents(
- blocks.find(_.hash === hash).get,
+ blocksWithUncles.find(_.hash === hash).get,
AVector.from(Gen.listOfN(3, contractEventByBlockHash).sample.get)
)
)
diff --git a/app/src/test/scala/org/alephium/explorer/GenApiModel.scala b/app/src/test/scala/org/alephium/explorer/GenApiModel.scala
index 9ed6644e7..42c1bf64d 100644
--- a/app/src/test/scala/org/alephium/explorer/GenApiModel.scala
+++ b/app/src/test/scala/org/alephium/explorer/GenApiModel.scala
@@ -69,12 +69,14 @@ object GenApiModel extends ImplicitConversions {
val unlockScriptGen: Gen[ByteString] = hashGen.map(_.bytes)
val inputGen: Gen[Input] = for {
- outputRef <- outputRefGen
- unlockScript <- Gen.option(unlockScriptGen)
- txHashRef <- Gen.option(transactionHashGen)
- address <- Gen.option(addressGen)
- amount <- Gen.option(amountGen)
- } yield Input(outputRef, unlockScript, txHashRef, address, amount)
+ outputRef <- outputRefGen
+ unlockScript <- Gen.option(unlockScriptGen)
+ txHashRef <- Gen.option(transactionHashGen)
+ address <- Gen.option(addressGen)
+ amount <- Gen.option(amountGen)
+ tokens <- Gen.option(tokensGen)
+ contractInput <- arbitrary[Boolean]
+ } yield Input(outputRef, unlockScript, txHashRef, address, amount, tokens, contractInput)
val tokenGen: Gen[Token] = for {
id <- tokenIdGen
@@ -92,8 +94,9 @@ object GenApiModel extends ImplicitConversions {
spent <- Gen.option(transactionHashGen)
message <- Gen.option(bytesGen)
hint = 0
- key <- outputRefKeyGen.map(_.value)
- } yield AssetOutput(hint, key, amount, address, tokens, lockTime, message, spent)
+ key <- outputRefKeyGen.map(_.value)
+ fixedOutput <- arbitrary[Boolean]
+ } yield AssetOutput(hint, key, amount, address, tokens, lockTime, message, spent, fixedOutput)
val contractOutputGen: Gen[ContractOutput] =
for {
@@ -103,7 +106,7 @@ object GenApiModel extends ImplicitConversions {
spent <- Gen.option(transactionHashGen)
hint = 0
key <- outputRefKeyGen.map(_.value)
- } yield ContractOutput(hint, key, amount, address, tokens, spent)
+ } yield ContractOutput(hint, key, amount, address, tokens, spent, fixedOutput = false)
val outputGen: Gen[Output] =
Gen.oneOf(assetOutputGen: Gen[Output], contractOutputGen: Gen[Output])
@@ -114,9 +117,14 @@ object GenApiModel extends ImplicitConversions {
blockHash <- blockHashGen
timestamp <- timestampGen
outputs <- Gen.listOfN(5, outputGen)
+ version <- arbitrary[Byte]
+ networkId <- arbitrary[Byte]
+ scriptOpt <- Gen.option(hashGen.map(_.toHexString))
gasAmount <- Gen.posNum[Int]
gasPrice <- u256Gen
scriptExecutionOk <- arbitrary[Boolean]
+ inputSignatures <- Gen.listOfN(2, bytesGen)
+ scriptSignatures <- Gen.listOfN(2, bytesGen)
coinbase <- arbitrary[Boolean]
} yield Transaction(
hash,
@@ -124,9 +132,14 @@ object GenApiModel extends ImplicitConversions {
timestamp,
ArraySeq.empty,
ArraySeq.from(outputs),
+ version,
+ networkId,
+ scriptOpt,
gasAmount,
gasPrice,
scriptExecutionOk,
+ inputSignatures,
+ scriptSignatures,
coinbase
)
@@ -135,8 +148,13 @@ object GenApiModel extends ImplicitConversions {
hash <- transactionHashGen
chainFrom <- groupIndexGen
chainTo <- groupIndexGen
- inputs <- Gen.listOfN(3, inputGen.map(_.copy(attoAlphAmount = None, txHashRef = None)))
- outputs <- Gen.listOfN(3, assetOutputGen.map(_.copy(spent = None)))
+ inputs <- Gen.listOfN(
+ 3,
+ inputGen.map(
+ _.copy(attoAlphAmount = None, txHashRef = None, tokens = None, contractInput = false)
+ )
+ )
+ outputs <- Gen.listOfN(3, assetOutputGen.map(_.copy(spent = None, fixedOutput = true)))
gasAmount <- Gen.posNum[Int]
gasPrice <- u256Gen
lastSeen <- timestampGen
@@ -185,15 +203,23 @@ object GenApiModel extends ImplicitConversions {
def blockEntryGen(implicit groupSetting: GroupSetting): Gen[BlockEntry] =
for {
- hash <- blockHashGen
- timestamp <- timestampGen
- chainFrom <- groupIndexGen
- chainTo <- groupIndexGen
- height <- heightGen
- deps <- Gen.listOfN(2 * groupSetting.groupNum - 1, blockHashGen)
- transactions <- Gen.listOfN(2, transactionGen)
- mainChain <- arbitrary[Boolean]
- hashrate <- arbitrary[Long].map(BigInteger.valueOf)
+ hash <- blockHashGen
+ timestamp <- timestampGen
+ chainFrom <- groupIndexGen
+ chainTo <- groupIndexGen
+ height <- heightGen
+ deps <- Gen.listOfN(2 * groupSetting.groupNum - 1, blockHashGen)
+ nonce <- bytesGen
+ depStateHash <- hashGen
+ txsHash <- hashGen
+ txsCount <- Gen.posNum[Int]
+ target <- bytesGen
+ version <- arbitrary[Byte]
+ mainChain <- arbitrary[Boolean]
+ hashrate <- arbitrary[Long].map(BigInteger.valueOf)
+ parent <- Gen.option(blockHashGen)
+ ghostUnclesSize <- Gen.choose(0, 5)
+ ghostUncles <- Gen.listOfN(ghostUnclesSize, ghostUncleGen())
} yield {
BlockEntry(
hash,
@@ -202,9 +228,16 @@ object GenApiModel extends ImplicitConversions {
chainTo,
height,
deps,
- transactions,
+ nonce,
+ version,
+ depStateHash,
+ txsHash,
+ txsCount,
+ target,
+ hashrate,
+ parent,
mainChain,
- hashrate
+ ghostUncles
)
}
@@ -339,6 +372,11 @@ object GenApiModel extends ImplicitConversions {
)
)
+ def ghostUncleGen()(implicit groupSetting: GroupSetting): Gen[GhostUncle] = for {
+ blockHash <- blockHashGen
+ miner <- addressAssetProtocolGen()
+ } yield GhostUncle(blockHash, miner)
+
/** Generates [[Pagination]] instance for the generated data.
*
* @return
diff --git a/app/src/test/scala/org/alephium/explorer/GenCoreApi.scala b/app/src/test/scala/org/alephium/explorer/GenCoreApi.scala
index 1da3b3184..16da49d43 100644
--- a/app/src/test/scala/org/alephium/explorer/GenCoreApi.scala
+++ b/app/src/test/scala/org/alephium/explorer/GenCoreApi.scala
@@ -33,7 +33,7 @@ import org.alephium.explorer.Generators._
import org.alephium.explorer.api.model.{Height, StdInterfaceId}
import org.alephium.explorer.persistence.model.ContractEntity
import org.alephium.explorer.service.BlockFlowClient
-import org.alephium.protocol.model.{BlockHash, ChainIndex, CliqueId, NetworkId, Target}
+import org.alephium.protocol.model.{BlockHash, ChainIndex, CliqueId, Hint, NetworkId, Target}
import org.alephium.serde._
import org.alephium.util.{AVector, Duration, Hex, I256, TimeStamp, U256}
@@ -123,12 +123,12 @@ object GenCoreApi {
for {
unsigned <- unsignedTxGen
scriptExecutionOk <- arbitrary[Boolean]
- contractInputsSize <- Gen.choose(0, 5)
+ contractInputsSize <- Gen.choose(0, 1)
contractInputs <- Gen.listOfN(contractInputsSize, outputRefProtocolGen)
- generatedOutputsSize <- Gen.choose(0, 5)
+ generatedOutputsSize <- Gen.choose(0, 1)
generatedOutputs <- Gen.listOfN(generatedOutputsSize, outputProtocolGen)
- inputSignatures <- Gen.listOfN(2, bytesGen)
- scriptSignatures <- Gen.listOfN(2, bytesGen)
+ inputSignatures <- Gen.listOfN(1, bytesGen)
+ scriptSignatures <- Gen.listOfN(1, bytesGen)
} yield Transaction(
unsigned,
scriptExecutionOk,
@@ -146,13 +146,13 @@ object GenCoreApi {
chainTo <- GenApiModel.groupIndexGen
height <- GenApiModel.heightGen
deps <- Gen.listOfN(2 * groupSetting.groupNum - 1, blockHashGen)
- transactionSize <- Gen.choose(1, 10)
+ transactionSize <- Gen.choose(1, 1)
transactions <- Gen.listOfN(transactionSize, transactionProtocolGen)
nonce <- bytesGen
version <- Gen.posNum[Byte]
depStateHash <- hashGen
txsHash <- hashGen
- ghostUnclesSize <- Gen.choose(0, 5)
+ ghostUnclesSize <- Gen.choose(0, 1)
ghostUncles <- Gen.listOfN(ghostUnclesSize, ghostUncleBlockEntry)
} yield {
// From `alephium` repo
@@ -226,13 +226,12 @@ object GenCoreApi {
def fixedOutputAssetProtocolGen(implicit groupSetting: GroupSetting): Gen[FixedAssetOutput] =
for {
- hint <- Gen.posNum[Int]
key <- hashGen
amount <- amountGen
lockTime <- timestampGen
address <- addressAssetProtocolGen()
} yield FixedAssetOutput(
- hint,
+ Hint.ofAsset(address.lockupScript.scriptHint).value,
key,
Amount(amount),
address,
@@ -251,12 +250,17 @@ object GenCoreApi {
def outputContractProtocolGen(implicit groupSetting: GroupSetting): Gen[ContractOutput] =
for {
- hint <- Gen.posNum[Int]
key <- hashGen
amount <- amountGen
address <- addressContractProtocolGen
tokens <- Gen.listOfN(1, tokenProtocolGen)
- } yield ContractOutput(hint, key, Amount(amount), address, AVector.from(tokens))
+ } yield ContractOutput(
+ Hint.ofAsset(address.lockupScript.scriptHint).value,
+ key,
+ Amount(amount),
+ address,
+ AVector.from(tokens)
+ )
def outputProtocolGen(implicit groupSetting: GroupSetting): Gen[Output] =
Gen.oneOf(outputAssetProtocolGen: Gen[Output], outputContractProtocolGen: Gen[Output])
diff --git a/app/src/test/scala/org/alephium/explorer/GenDBModel.scala b/app/src/test/scala/org/alephium/explorer/GenDBModel.scala
index c06fcf24f..287ea1ba5 100644
--- a/app/src/test/scala/org/alephium/explorer/GenDBModel.scala
+++ b/app/src/test/scala/org/alephium/explorer/GenDBModel.scala
@@ -24,6 +24,7 @@ import org.scalacheck.{Arbitrary, Gen}
import org.scalacheck.Arbitrary.arbitrary
import org.alephium.api.model.{Val, ValAddress, ValByteVec}
+import org.alephium.explorer.ConfigDefaults.groupSetting
import org.alephium.explorer.GenApiModel._
import org.alephium.explorer.GenCoreApi.{blockEntryProtocolGen, valByteVecGen, valGen}
import org.alephium.explorer.GenCoreProtocol._
@@ -60,6 +61,7 @@ object GenDBModel {
outputOrder <- arbitrary[Int]
txOrder <- arbitrary[Int]
coinbase <- arbitrary[Boolean]
+ fixedOutput <- arbitrary[Boolean]
} yield OutputEntity(
blockHash = blockHash,
txHash = txHash,
@@ -77,7 +79,8 @@ object GenDBModel {
txOrder = txOrder,
spentFinalized = None,
spentTimestamp = None,
- coinbase = coinbase
+ coinbase = coinbase,
+ fixedOutput = fixedOutput
)
val finalizedOutputEntityGen: Gen[OutputEntity] =
@@ -118,9 +121,10 @@ object GenDBModel {
@SuppressWarnings(Array("org.wartremover.warts.DefaultArguments"))
def inputEntityGen(outputEntityGen: Gen[OutputEntity] = outputEntityGen): Gen[InputEntity] =
for {
- outputEntity <- outputEntityGen
- unlockScript <- Gen.option(unlockScriptGen)
- txOrder <- arbitrary[Int]
+ outputEntity <- outputEntityGen
+ unlockScript <- Gen.option(unlockScriptGen)
+ txOrder <- arbitrary[Int]
+ contractInput <- arbitrary[Boolean]
} yield {
InputEntity(
blockHash = outputEntity.blockHash,
@@ -135,7 +139,8 @@ object GenDBModel {
None,
None,
None,
- None
+ None,
+ contractInput
)
}
@@ -352,6 +357,9 @@ object GenDBModel {
timestamp <- timestampGen
chainFrom <- groupIndexGen
chainTo <- groupIndexGen
+ version <- arbitrary[Byte]
+ networkId <- arbitrary[Byte]
+ scriptOpt <- Gen.option(hashGen.map(_.toHexString))
gasAmount <- Gen.posNum[Int]
gasPrice <- u256Gen
order <- Gen.posNum[Int]
@@ -364,6 +372,9 @@ object GenDBModel {
timestamp = timestamp,
chainFrom = chainFrom,
chainTo = chainTo,
+ version = version,
+ networkId = networkId,
+ scriptOpt = scriptOpt,
gasAmount = gasAmount,
gasPrice = gasPrice,
order = order,
@@ -414,6 +425,7 @@ object GenDBModel {
target <- bytesGen
hashrate <- arbitrary[Long].map(BigInteger.valueOf)
mainChain <- Arbitrary.arbitrary[Boolean]
+ deps <- Gen.listOfN(2 * groupSetting.groupNum - 1, blockHashGen)
parent <- Gen.option(blockHashGen)
} yield BlockHeader(
hash = hash,
@@ -429,7 +441,9 @@ object GenDBModel {
txsCount = txsCount,
target = target,
hashrate = hashrate,
- parent = parent
+ parent = parent,
+ deps = deps,
+ ghostUncles = None
)
val blockHeaderTransactionEntityGen: Gen[(BlockHeader, List[TransactionEntity])] =
@@ -438,19 +452,24 @@ object GenDBModel {
transaction <- Gen.listOf(transactionEntityGen(Gen.const(blockHeader.hash)))
} yield (blockHeader, transaction)
- def blockHeaderWithHashrate(timestamp: TimeStamp, hashrate: Double): Gen[BlockHeader] = {
+ def blockHeaderWithHashrate(timestamp: TimeStamp, hashrate: Double)(implicit
+ groupSetting: GroupSetting
+ ): Gen[BlockHeader] = {
for {
- hash <- blockHashGen
- from <- groupIndexGen
- to <- groupIndexGen
- height <- heightGen
- nonce <- bytesGen
- version <- Gen.posNum[Byte]
- depStateHash <- hashGen
- txsHash <- hashGen
- txsCount <- Gen.posNum[Int]
- target <- bytesGen
- parent <- Gen.option(blockHashGen)
+ hash <- blockHashGen
+ from <- groupIndexGen
+ to <- groupIndexGen
+ height <- heightGen
+ nonce <- bytesGen
+ version <- Gen.posNum[Byte]
+ depStateHash <- hashGen
+ txsHash <- hashGen
+ txsCount <- Gen.posNum[Int]
+ target <- bytesGen
+ parent <- Gen.option(blockHashGen)
+ deps <- Gen.listOfN(2 * groupSetting.groupNum - 1, blockHashGen)
+ ghostUnclesSize <- Gen.choose(0, 5)
+ ghostUncles <- Gen.option(Gen.listOfN(ghostUnclesSize, ghostUncleGen()))
} yield {
BlockHeader(
hash,
@@ -466,40 +485,13 @@ object GenDBModel {
txsCount,
target,
BigDecimal(hashrate).toBigInt.bigInteger,
- parent
+ parent,
+ deps,
+ ghostUncles
)
}
}
- /** Update toUpdate's primary key to be the same as `original` */
- def copyPrimaryKeys(original: BlockDepEntity, toUpdate: BlockDepEntity): BlockDepEntity =
- toUpdate.copy(
- hash = original.hash,
- dep = original.dep
- )
-
- val blockDepGen: Gen[BlockDepEntity] =
- for {
- hash <- blockHashGen
- dep <- blockHashGen
- order <- Gen.posNum[Int]
- } yield BlockDepEntity(
- hash = hash,
- dep = dep,
- order = order
- )
-
- /** Generates a tuple2 of [[BlockDepEntity]] where the second one has the same primary key as the
- * first one but with different values
- */
- val blockDepUpdatedGen: Gen[(BlockDepEntity, BlockDepEntity)] =
- for {
- dep1 <- blockDepGen
- dep2 <- blockDepGen
- } yield {
- (dep1, copyPrimaryKeys(dep1, dep2))
- }
-
/** Table `uinputs` applies uniqueness on `(output_ref_key, tx_hash)`.
*
* Table `uoutputs` applies uniqueness on `(tx_hash, address, uoutput_order)`.
diff --git a/app/src/test/scala/org/alephium/explorer/Generators.scala b/app/src/test/scala/org/alephium/explorer/Generators.scala
index fe9a36d6a..665c1c554 100644
--- a/app/src/test/scala/org/alephium/explorer/Generators.scala
+++ b/app/src/test/scala/org/alephium/explorer/Generators.scala
@@ -16,9 +16,8 @@
package org.alephium.explorer
-import java.math.BigInteger
-
import scala.collection.immutable.ArraySeq
+import scala.util.Random
import org.scalacheck.Gen
@@ -33,41 +32,58 @@ object Generators {
def parentIndex(chainTo: GroupIndex)(implicit groupSetting: GroupSetting) =
groupSetting.groupNum - 1 + chainTo.value
+ def blockEntityToTransactions(
+ block: BlockEntity,
+ outputs: ArraySeq[OutputEntity]
+ ): ArraySeq[Transaction] = {
+ val coinbaseTxId = block.transactions.last.hash
+ block.transactions.map { tx =>
+ Transaction(
+ tx.hash,
+ block.hash,
+ block.timestamp,
+ block.inputs
+ .filter(_.txHash === tx.hash)
+ .map(input =>
+ inputEntityToApi(input, outputs(Random.nextInt(outputs.size)))
+ ), // TODO Fix when we have a valid blockchain generator
+ block.outputs.filter(_.txHash === tx.hash).map(out => outputEntityToApi(out, None)),
+ tx.version,
+ tx.networkId,
+ tx.scriptOpt,
+ tx.gasAmount,
+ tx.gasPrice,
+ tx.scriptExecutionOk,
+ tx.inputSignatures.getOrElse(ArraySeq.empty),
+ tx.scriptSignatures.getOrElse(ArraySeq.empty),
+ coinbase = coinbaseTxId == tx.hash
+ )
+ }
+ }
+
def blockEntitiesToBlockEntries(
blocks: ArraySeq[ArraySeq[BlockEntity]]
- ): ArraySeq[ArraySeq[BlockEntry]] = {
- val outputs: ArraySeq[OutputEntity] = blocks.flatMap(_.flatMap(_.outputs))
-
+ ): ArraySeq[ArraySeq[BlockEntryTest]] = {
blocks.map(_.map { block =>
- val coinbaseTxId = block.transactions.last.hash
- val transactions =
- block.transactions.map { tx =>
- Transaction(
- tx.hash,
- block.hash,
- block.timestamp,
- block.inputs
- .filter(_.txHash === tx.hash)
- .map(input =>
- inputEntityToApi(input, outputs.head)
- ), // TODO Fix when we have a valid blockchain generator
- block.outputs.filter(_.txHash === tx.hash).map(out => outputEntityToApi(out, None)),
- tx.gasAmount,
- tx.gasPrice,
- tx.scriptExecutionOk,
- coinbase = coinbaseTxId == tx.hash
- )
- }
- BlockEntry(
+ val transactions = blockEntityToTransactions(block, blocks.flatMap(_.flatMap(_.outputs)))
+ BlockEntryTest(
block.hash,
block.timestamp,
block.chainFrom,
block.chainTo,
block.height,
- block.deps,
transactions,
+ block.deps,
+ block.nonce,
+ block.version,
+ block.depStateHash,
+ block.txsHash,
+ transactions.size,
+ block.target,
+ block.hashrate,
+ None,
mainChain = true,
- BigInteger.ZERO
+ ghostUncles = block.ghostUncles
)
})
}
@@ -79,15 +95,26 @@ object Generators {
Some(outputRef.txHash),
Some(outputRef.address),
Some(outputRef.amount),
- outputRef.tokens
+ outputRef.tokens,
+ input.contractInput
)
def outputEntityToApi(o: OutputEntity, spent: Option[TransactionId]): Output = {
o.outputType match {
case OutputEntity.Asset =>
- AssetOutput(o.hint, o.key, o.amount, o.address, o.tokens, o.lockTime, o.message, spent)
+ AssetOutput(
+ o.hint,
+ o.key,
+ o.amount,
+ o.address,
+ o.tokens,
+ o.lockTime,
+ o.message,
+ spent,
+ o.fixedOutput
+ )
case OutputEntity.Contract =>
- ContractOutput(o.hint, o.key, o.amount, o.address, o.tokens, spent)
+ ContractOutput(o.hint, o.key, o.amount, o.address, o.tokens, spent, o.fixedOutput)
}
}
}
diff --git a/app/src/test/scala/org/alephium/explorer/api/model/ApiModelSpec.scala b/app/src/test/scala/org/alephium/explorer/api/model/ApiModelSpec.scala
index 936e8229e..9494ac84b 100644
--- a/app/src/test/scala/org/alephium/explorer/api/model/ApiModelSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/api/model/ApiModelSpec.scala
@@ -54,9 +54,14 @@ class ApiModelSpec() extends AlephiumSpec {
| "timestamp": ${tx.timestamp.millis},
| "inputs": [],
| "outputs": ${write(tx.outputs)},
+ | "version": ${tx.version},
+ | "networkId": ${tx.networkId},
+ | "scriptOpt": ${tx.scriptOpt.map(s => s""""$s"""").getOrElse("null")},
| "gasAmount": ${tx.gasAmount},
| "gasPrice": "${tx.gasPrice}",
| "scriptExecutionOk": ${tx.scriptExecutionOk},
+ | "inputSignatures": ${write(tx.inputSignatures)},
+ | "scriptSignatures": ${write(tx.scriptSignatures)},
| "coinbase": ${tx.coinbase}
|}""".stripMargin
check(tx, expected)
@@ -87,7 +92,8 @@ class ApiModelSpec() extends AlephiumSpec {
unlockScript = None,
address = Some(address),
attoAlphAmount = Some(ALPH.alph(10)),
- tokens = None
+ tokens = None,
+ contractInput = false
)
),
ArraySeq(
@@ -99,7 +105,8 @@ class ApiModelSpec() extends AlephiumSpec {
tokens = None,
lockTime = None,
message = None,
- spent = None
+ spent = None,
+ fixedOutput = true
),
AssetOutput(
hint = 0,
@@ -109,7 +116,8 @@ class ApiModelSpec() extends AlephiumSpec {
tokens = None,
lockTime = None,
message = None,
- spent = None
+ spent = None,
+ fixedOutput = true
),
AssetOutput(
hint = 0,
@@ -119,12 +127,18 @@ class ApiModelSpec() extends AlephiumSpec {
tokens = None,
lockTime = None,
message = None,
- spent = None
+ spent = None,
+ fixedOutput = true
)
),
+ version = 0,
+ networkId = 0,
+ scriptOpt = None,
gasAmount = 1,
gasPrice = ALPH.alph(1),
false,
+ ArraySeq.empty,
+ ArraySeq.empty,
true
)
@@ -145,9 +159,14 @@ class ApiModelSpec() extends AlephiumSpec {
| "timestamp": ${tx.timestamp.millis},
| "inputs": ${write(tx.inputs)},
| "outputs": ${write(tx.outputs)},
+ | "version": ${tx.version},
+ | "networkId": ${tx.networkId},
+ | "scriptOpt": ${tx.scriptOpt.map(s => s""""$s"""").getOrElse("null")},
| "gasAmount": ${tx.gasAmount},
| "gasPrice": "${tx.gasPrice}",
| "scriptExecutionOk": ${tx.scriptExecutionOk},
+ | "inputSignatures": ${write(tx.inputSignatures)},
+ | "scriptSignatures": ${write(tx.scriptSignatures)},
| "coinbase": ${tx.coinbase}
|}""".stripMargin
check(AcceptedTransaction.from(tx), expected)
@@ -196,6 +215,7 @@ class ApiModelSpec() extends AlephiumSpec {
| ${output.spent
.map(spent => s""","spent": "${spent.value.toHexString}"""")
.getOrElse("")}
+ | ,"fixedOutput": ${output.fixedOutput}
|}""".stripMargin
check(output, expected)
}
@@ -215,6 +235,7 @@ class ApiModelSpec() extends AlephiumSpec {
| ${output.spent
.map(spent => s""","spent": "${spent.value.toHexString}"""")
.getOrElse("")}
+ | ,"fixedOutput": ${output.fixedOutput}
|}""".stripMargin
check(output, expected)
}
@@ -236,6 +257,10 @@ class ApiModelSpec() extends AlephiumSpec {
| ${input.attoAlphAmount
.map(attoAlphAmount => s""","attoAlphAmount": "${attoAlphAmount}"""")
.getOrElse("")}
+ | ${input.tokens
+ .map(tokens => s""","tokens": ${write(tokens)}""")
+ .getOrElse("")}
+ | ,"contractInput": ${input.contractInput}
|}""".stripMargin
check(input, expected)
}
@@ -302,9 +327,18 @@ class ApiModelSpec() extends AlephiumSpec {
| "chainTo": ${block.chainTo.value},
| "height": ${block.height.value},
| "deps": ${write(block.deps)},
- | "transactions": ${write(block.transactions)},
+ | "nonce": ${write(block.nonce)},
+ | "version": ${block.version},
+ | "depStateHash": "${block.depStateHash.toHexString}",
+ | "txsHash": "${block.txsHash.toHexString}",
+ | "txNumber": ${block.txNumber},
+ | "target": ${write(block.target)},
+ | "hashRate": ${write(block.hashRate)},
+ | "parent": ${if (block.parent.isDefined) {
+ s""""${block.parent.get.toHexString}""""
+ } else { "null" }},
| "mainChain": ${block.mainChain},
- | "hashRate": "${block.hashRate}"
+ | "ghostUncles": ${write(block.ghostUncles)}
|}""".stripMargin
check(block, expected)
}
diff --git a/app/src/test/scala/org/alephium/explorer/api/model/BlockEntryTest.scala b/app/src/test/scala/org/alephium/explorer/api/model/BlockEntryTest.scala
new file mode 100644
index 000000000..08da07465
--- /dev/null
+++ b/app/src/test/scala/org/alephium/explorer/api/model/BlockEntryTest.scala
@@ -0,0 +1,48 @@
+// Copyright 2018 The Alephium Authors
+// This file is part of the alephium project.
+//
+// The library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the library. If not, see .
+
+package org.alephium.explorer.api.model
+
+import java.math.BigInteger
+
+import scala.collection.immutable.ArraySeq
+
+import akka.util.ByteString
+
+import org.alephium.explorer.service.FlowEntity
+import org.alephium.protocol.Hash
+import org.alephium.protocol.model.{BlockHash, GroupIndex}
+import org.alephium.util.TimeStamp
+
+final case class BlockEntryTest(
+ hash: BlockHash,
+ timestamp: TimeStamp,
+ chainFrom: GroupIndex,
+ chainTo: GroupIndex,
+ height: Height,
+ transactions: ArraySeq[Transaction],
+ deps: ArraySeq[BlockHash],
+ nonce: ByteString,
+ version: Byte,
+ depStateHash: Hash,
+ txsHash: Hash,
+ txsCount: Int,
+ target: ByteString,
+ hashRate: BigInteger,
+ parent: Option[BlockHash],
+ mainChain: Boolean,
+ ghostUncles: ArraySeq[GhostUncle]
+) extends FlowEntity
diff --git a/app/src/test/scala/org/alephium/explorer/persistence/dao/BlockDaoSpec.scala b/app/src/test/scala/org/alephium/explorer/persistence/dao/BlockDaoSpec.scala
index ee93a2132..653ed3230 100644
--- a/app/src/test/scala/org/alephium/explorer/persistence/dao/BlockDaoSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/persistence/dao/BlockDaoSpec.scala
@@ -88,12 +88,10 @@ class BlockDaoSpec extends AlephiumFutureSpec with DatabaseFixtureForEach with D
val inputQuery = InputSchema.table.filter(_.blockHash === block.hash).result
val outputQuery = OutputSchema.table.filter(_.blockHash === block.hash).result
- val blockDepsQuery =
- BlockDepsSchema.table.filter(_.hash === block.hash).map(_.dep).result
val transactionsQuery =
TransactionSchema.table.filter(_.blockHash === block.hash).result
- val queries = Seq(inputQuery, outputQuery, blockDepsQuery, transactionsQuery)
- val dbInputs = Seq(block.inputs, block.outputs, block.deps, block.transactions)
+ val queries = Seq(inputQuery, outputQuery, transactionsQuery)
+ val dbInputs = Seq(block.inputs, block.outputs, block.transactions)
def checkDuplicates[T](dbInput: Seq[T], dbOutput: Seq[T]) = {
dbOutput.size is dbInput.size
diff --git a/app/src/test/scala/org/alephium/explorer/persistence/queries/BlockDepQueriesSpec.scala b/app/src/test/scala/org/alephium/explorer/persistence/queries/BlockDepQueriesSpec.scala
deleted file mode 100644
index 70a417379..000000000
--- a/app/src/test/scala/org/alephium/explorer/persistence/queries/BlockDepQueriesSpec.scala
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2018 The Alephium Authors
-// This file is part of the alephium project.
-//
-// The library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the library. If not, see .
-
-package org.alephium.explorer.persistence.queries
-
-import org.scalacheck.Gen
-import slick.jdbc.PostgresProfile.api._
-
-import org.alephium.explorer.{AlephiumFutureSpec, GenDBModel}
-import org.alephium.explorer.persistence.{DatabaseFixtureForEach, DBRunner}
-import org.alephium.explorer.persistence.queries.BlockDepQueries._
-import org.alephium.explorer.persistence.schema.BlockDepsSchema
-
-class BlockDepQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForEach with DBRunner {
-
- "insert and ignore block_deps" in {
-
- forAll(Gen.listOf(GenDBModel.blockDepUpdatedGen)) { deps =>
- // clean existing rows
- run(BlockDepsSchema.table.delete).futureValue
-
- val original = deps.map(_._1)
- val ignored = deps.map(_._2)
-
- run(insertBlockDeps(original)).futureValue is original.size
- run(BlockDepsSchema.table.result).futureValue is original
-
- // Ignore the same data with do nothing order
- run(insertBlockDeps(ignored)).futureValue is 0
- // it should contain original rows
- run(BlockDepsSchema.table.result).futureValue should contain allElementsOf original
- }
- }
-
-}
diff --git a/app/src/test/scala/org/alephium/explorer/persistence/queries/BlockQueriesSpec.scala b/app/src/test/scala/org/alephium/explorer/persistence/queries/BlockQueriesSpec.scala
index dd5ec96e2..2e0c44bd4 100644
--- a/app/src/test/scala/org/alephium/explorer/persistence/queries/BlockQueriesSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/persistence/queries/BlockQueriesSpec.scala
@@ -122,7 +122,6 @@ class BlockQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForEach wi
run(TransactionSchema.table.delete).futureValue
run(InputSchema.table.delete).futureValue
run(OutputSchema.table.delete).futureValue
- run(BlockDepsSchema.table.delete).futureValue
// execute insert on blocks and expect all tables get inserted
run(BlockQueries.insertBlockEntity(entities, groupSetting.groupNum)).futureValue is ()
@@ -147,11 +146,6 @@ class BlockQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForEach wi
// val expectedOutputs = entities.flatMap(_.outputs)
// actualOutputs should contain allElementsOf expectedOutputs
- // check block_deps table
- val actualDeps = run(BlockDepsSchema.table.result).futureValue
- val expectedBlockDeps = entities.flatMap(_.toBlockDepEntities())
- actualDeps should contain allElementsOf expectedBlockDeps
-
// There is no need for testing updates here since updates are already
// tested each table's individual test-cases.
}
diff --git a/app/src/test/scala/org/alephium/explorer/persistence/queries/InputQueriesSpec.scala b/app/src/test/scala/org/alephium/explorer/persistence/queries/InputQueriesSpec.scala
index 99bc0f105..3dda46bb9 100644
--- a/app/src/test/scala/org/alephium/explorer/persistence/queries/InputQueriesSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/persistence/queries/InputQueriesSpec.scala
@@ -106,10 +106,11 @@ class InputQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForEach wi
hint = entity.hint,
outputRefKey = entity.outputRefKey,
unlockScript = entity.unlockScript,
- txHashRef = entity.outputRefTxHash,
- address = entity.outputRefAddress,
- amount = entity.outputRefAmount,
- token = entity.outputRefTokens
+ outputRefTxHash = entity.outputRefTxHash,
+ outputRefAddress = entity.outputRefAddress,
+ outputRefAmount = entity.outputRefAmount,
+ outputRefTokens = entity.outputRefTokens,
+ contractInput = entity.contractInput
)
}
@@ -155,7 +156,8 @@ class InputQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForEach wi
outputRefTxHash = input.outputRefTxHash,
outputRefAddress = input.outputRefAddress,
outputRefAmount = input.outputRefAmount,
- outputRefTokens = input.outputRefTokens
+ outputRefTokens = input.outputRefTokens,
+ contractInput = input.contractInput
)
actual.toList should contain only expected
diff --git a/app/src/test/scala/org/alephium/explorer/persistence/queries/OutputQueriesSpec.scala b/app/src/test/scala/org/alephium/explorer/persistence/queries/OutputQueriesSpec.scala
index 56ee02d97..50d273c36 100644
--- a/app/src/test/scala/org/alephium/explorer/persistence/queries/OutputQueriesSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/persistence/queries/OutputQueriesSpec.scala
@@ -101,7 +101,8 @@ class OutputQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForEach w
tokens = entity.tokens,
lockTime = entity.lockTime,
message = entity.message,
- spent = entity.spentFinalized
+ spentFinalized = entity.spentFinalized,
+ fixedOutput = entity.fixedOutput
)
}
@@ -149,7 +150,8 @@ class OutputQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForEach w
tokens = output.tokens,
lockTime = output.lockTime,
message = output.message,
- spentFinalized = output.spentFinalized
+ spentFinalized = output.spentFinalized,
+ fixedOutput = output.fixedOutput
)
actual.toList should contain only expected
diff --git a/app/src/test/scala/org/alephium/explorer/persistence/queries/TransactionQueriesSpec.scala b/app/src/test/scala/org/alephium/explorer/persistence/queries/TransactionQueriesSpec.scala
index 77f9eeca4..05c0ee8db 100644
--- a/app/src/test/scala/org/alephium/explorer/persistence/queries/TransactionQueriesSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/persistence/queries/TransactionQueriesSpec.scala
@@ -200,9 +200,14 @@ class TransactionQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForE
output.timestamp,
inputs,
ArraySeq(outputEntityToApi(output, spent)),
+ version = 1,
+ networkId = 1,
+ scriptOpt = None,
1,
ALPH.alph(1),
scriptExecutionOk = true,
+ inputSignatures = ArraySeq.empty,
+ scriptSignatures = ArraySeq.empty,
coinbase = false
)
}
@@ -233,6 +238,9 @@ class TransactionQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForE
tx1.timestamp,
chainFrom,
chainTo,
+ tx1.version,
+ tx1.networkId,
+ tx1.scriptOpt,
tx1.gasAmount,
tx1.gasPrice,
0,
@@ -575,7 +583,8 @@ class TransactionQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForE
0,
false,
None,
- None
+ None,
+ fixedOutput = false
)
def input(hint: Int, outputRefKey: Hash): InputEntity =
@@ -592,7 +601,8 @@ class TransactionQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForE
None,
None,
None,
- None
+ None,
+ contractInput = false
)
def transaction(output: OutputEntity): TransactionEntity = {
TransactionEntity(
@@ -601,6 +611,9 @@ class TransactionQueriesSpec extends AlephiumFutureSpec with DatabaseFixtureForE
output.timestamp,
GroupIndex.Zero,
new GroupIndex(1),
+ version = 1,
+ networkId = 1,
+ scriptOpt = None,
1,
ALPH.alph(1),
0,
diff --git a/app/src/test/scala/org/alephium/explorer/service/BlockFlowSyncServiceSpec.scala b/app/src/test/scala/org/alephium/explorer/service/BlockFlowSyncServiceSpec.scala
index 3f80dedd3..5bc20755b 100644
--- a/app/src/test/scala/org/alephium/explorer/service/BlockFlowSyncServiceSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/service/BlockFlowSyncServiceSpec.scala
@@ -202,25 +202,35 @@ class BlockFlowSyncServiceSpec extends AlephiumFutureSpec with DatabaseFixtureFo
def blockFlowEntity: ArraySeq[ArraySeq[BlockEntity]] =
chains :+ chainOToO
- def blockFlow: ArraySeq[ArraySeq[BlockEntry]] =
+ val uncles = blockFlowEntity.map(_.flatMap { block =>
+ block.ghostUncles.map { uncle =>
+ blockEntity(None, ChainIndex(block.chainFrom, block.chainTo)).copy(hash = uncle.blockHash)
+ }
+ })
+
+ def blockFlow: ArraySeq[ArraySeq[BlockEntryTest]] =
blockEntitiesToBlockEntries(blockFlowEntity)
implicit val blockCache: BlockCache = TestBlockCache()
def blockEntities = ArraySeq.from(blockFlowEntity.flatten)
- def blocks: ArraySeq[BlockEntry] = blockFlow.flatten
+ def unclesEntities = ArraySeq.from(uncles.flatten)
+
+ def blocksAndUncles = blockEntities ++ unclesEntities
+
+ def blocks: ArraySeq[BlockEntryTest] = blockFlow.flatten
implicit val blockFlowClient: BlockFlowClient = new EmptyBlockFlowClient {
override def fetchBlock(from: GroupIndex, hash: BlockHash): Future[BlockEntity] =
- Future.successful(blockEntities.find(_.hash === hash).get)
+ Future.successful(blocksAndUncles.find(_.hash === hash).get)
override def fetchBlockAndEvents(
fromGroup: GroupIndex,
hash: BlockHash
): Future[BlockEntityWithEvents] =
Future.successful(
- BlockEntityWithEvents(blockEntities.find(_.hash === hash).get, ArraySeq.empty)
+ BlockEntityWithEvents(blocksAndUncles.find(_.hash === hash).get, ArraySeq.empty)
)
override def fetchBlocks(
diff --git a/app/src/test/scala/org/alephium/explorer/service/EmptyBlockService.scala b/app/src/test/scala/org/alephium/explorer/service/EmptyBlockService.scala
index a758f1156..56d5f4d53 100644
--- a/app/src/test/scala/org/alephium/explorer/service/EmptyBlockService.scala
+++ b/app/src/test/scala/org/alephium/explorer/service/EmptyBlockService.scala
@@ -29,6 +29,11 @@ import org.alephium.protocol.model.BlockHash
trait EmptyBlockService extends BlockService {
+ def getBlockByHash(hash: BlockHash)(implicit
+ ec: ExecutionContext,
+ dc: DatabaseConfig[PostgresProfile]
+ ): Future[Option[BlockEntry]] = Future.successful(None)
+
def getLiteBlockByHash(hash: BlockHash)(implicit
ec: ExecutionContext,
dc: DatabaseConfig[PostgresProfile]
diff --git a/app/src/test/scala/org/alephium/explorer/service/HashrateServiceSpec.scala b/app/src/test/scala/org/alephium/explorer/service/HashrateServiceSpec.scala
index 96da7e93d..71ecb50cb 100644
--- a/app/src/test/scala/org/alephium/explorer/service/HashrateServiceSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/service/HashrateServiceSpec.scala
@@ -23,6 +23,7 @@ import scala.collection.immutable.ArraySeq
import slick.jdbc.PostgresProfile.api._
import org.alephium.explorer.AlephiumFutureSpec
+import org.alephium.explorer.ConfigDefaults.groupSetting
import org.alephium.explorer.GenDBModel.blockHeaderWithHashrate
import org.alephium.explorer.api.model.{Hashrate, IntervalType}
import org.alephium.explorer.persistence.{DatabaseFixtureForEach, DBRunner}
diff --git a/app/src/test/scala/org/alephium/explorer/service/TokenSupplyServiceSpec.scala b/app/src/test/scala/org/alephium/explorer/service/TokenSupplyServiceSpec.scala
index 0993bebfe..4b5d12af0 100644
--- a/app/src/test/scala/org/alephium/explorer/service/TokenSupplyServiceSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/service/TokenSupplyServiceSpec.scala
@@ -207,7 +207,8 @@ class TokenSupplyServiceSpec extends AlephiumFutureSpec with DatabaseFixtureForE
None,
None,
None,
- None
+ None,
+ contractInput = false
)
},
diff --git a/app/src/test/scala/org/alephium/explorer/service/TransactionServiceSpec.scala b/app/src/test/scala/org/alephium/explorer/service/TransactionServiceSpec.scala
index b28e337e3..cbab589f4 100644
--- a/app/src/test/scala/org/alephium/explorer/service/TransactionServiceSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/service/TransactionServiceSpec.scala
@@ -103,10 +103,8 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
val fetchedAmout =
BlockDao
- .get(block.hash)
+ .getTransactions(block.hash, Pagination.unsafe(1, 1000))
.futureValue
- .get
- .transactions
.flatMap(_.outputs.map(_.attoAlphAmount))
.head
fetchedAmout is amount
@@ -128,6 +126,9 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
ts0,
groupIndex,
groupIndex,
+ version,
+ networkId,
+ scriptOpt,
gasAmount,
gasPrice,
0,
@@ -156,7 +157,8 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
0,
coinbase = false,
None,
- None
+ None,
+ fixedOutput = true
)
val block0 = defaultBlockEntity.copy(
@@ -176,6 +178,9 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
ts1,
groupIndex,
groupIndex,
+ version,
+ networkId,
+ scriptOpt,
gasAmount1,
gasPrice1,
0,
@@ -198,7 +203,8 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
None,
None,
None,
- None
+ None,
+ contractInput = false
)
val output1 = OutputEntity(
blockHash1,
@@ -217,7 +223,8 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
0,
coinbase = false,
None,
- None
+ None,
+ fixedOutput = true
)
val block1 = defaultBlockEntity.copy(
@@ -242,11 +249,26 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
ts0,
ArraySeq.empty,
ArraySeq(
- AssetOutput(output0.hint, output0.key, U256.One, address0, None, None, None, Some(tx1.hash))
+ AssetOutput(
+ output0.hint,
+ output0.key,
+ U256.One,
+ address0,
+ None,
+ None,
+ None,
+ Some(tx1.hash),
+ true
+ )
),
+ version,
+ networkId,
+ scriptOpt,
gasAmount,
gasPrice,
scriptExecutionOk = true,
+ scriptSignatures = ArraySeq.empty,
+ inputSignatures = ArraySeq.empty,
coinbase = false
)
@@ -255,12 +277,27 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
blockHash1,
ts1,
ArraySeq(
- Input(OutputRef(0, output0.key), None, Some(output0.txHash), Some(address0), Some(U256.One))
+ Input(
+ OutputRef(0, output0.key),
+ None,
+ Some(output0.txHash),
+ Some(address0),
+ Some(U256.One),
+ None,
+ false
+ )
+ ),
+ ArraySeq(
+ AssetOutput(output1.hint, output1.key, U256.One, address1, None, None, None, None, true)
),
- ArraySeq(AssetOutput(output1.hint, output1.key, U256.One, address1, None, None, None, None)),
+ version,
+ networkId,
+ scriptOpt,
gasAmount1,
gasPrice1,
scriptExecutionOk = true,
+ scriptSignatures = ArraySeq.empty,
+ inputSignatures = ArraySeq.empty,
coinbase = false
)
@@ -283,6 +320,9 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
ts0,
groupIndex,
groupIndex,
+ version,
+ networkId,
+ scriptOpt,
Gen.posNum[Int].sample.get,
amountGen.sample.get,
0,
@@ -311,7 +351,8 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
0,
coinbase = false,
None,
- None
+ None,
+ fixedOutput = true
)
val block0 = defaultBlockEntity.copy(
@@ -554,8 +595,11 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
trait Fixture {
implicit val blockCache: BlockCache = TestBlockCache()
- val groupIndex = GroupIndex.Zero
- val chainIndex = ChainIndex(groupIndex, groupIndex)
+ val groupIndex = GroupIndex.Zero
+ val chainIndex = ChainIndex(groupIndex, groupIndex)
+ val version: Byte = 1
+ val networkId: Byte = 1
+ val scriptOpt = None
val defaultBlockEntity: BlockEntity =
BlockEntity(
@@ -574,7 +618,8 @@ class TransactionServiceSpec extends AlephiumActorSpecLike with DatabaseFixtureF
depStateHash = hashGen.sample.get,
txsHash = hashGen.sample.get,
target = bytesGen.sample.get,
- hashrate = BigInteger.ZERO
+ hashrate = BigInteger.ZERO,
+ ghostUncles = ArraySeq.empty
)
}
diff --git a/app/src/test/scala/org/alephium/explorer/web/AddressServerSpec.scala b/app/src/test/scala/org/alephium/explorer/web/AddressServerSpec.scala
index 5035a534a..4e5f5cb46 100644
--- a/app/src/test/scala/org/alephium/explorer/web/AddressServerSpec.scala
+++ b/app/src/test/scala/org/alephium/explorer/web/AddressServerSpec.scala
@@ -26,6 +26,7 @@ import akka.util.ByteString
import io.reactivex.rxjava3.core.Flowable
import io.vertx.core.buffer.Buffer
import org.scalacheck.Gen
+import org.scalatest.time.{Seconds, Span}
import slick.basic.DatabaseConfig
import slick.jdbc.PostgresProfile
import sttp.model.{Header, StatusCode}
@@ -63,6 +64,9 @@ class AddressServerSpec()
with DatabaseFixtureForAll
with HttpServerFixture {
+ implicit override val patienceConfig: PatienceConfig =
+ PatienceConfig(timeout = Span(120, Seconds))
+
val exportTxsNumberThreshold = 1000
var addressHasMoreTxs = false
@@ -317,7 +321,7 @@ class AddressServerSpec()
def getToTs(intervalType: IntervalType) =
fromTs + maxTimeSpan(intervalType).millis
- "return the deprecated amount history as json" in {
+ "return the deprecated amount history as json" ignore {
intervalTypes.foreach { intervalType =>
val toTs = getToTs(intervalType)
diff --git a/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/DataGenerator.scala b/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/DataGenerator.scala
index ae507343b..e955abe0c 100644
--- a/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/DataGenerator.scala
+++ b/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/DataGenerator.scala
@@ -53,6 +53,9 @@ object DataGenerator {
timestamp = blockTimestamp,
chainFrom = new GroupIndex(1),
chainTo = new GroupIndex(3),
+ version = 1,
+ networkId = 1,
+ scriptOpt = Some(Random.alphanumeric.take(10).mkString),
gasAmount = Random.nextInt(1000),
gasPrice = U256.unsafe(0),
order = Random.nextInt(1000),
@@ -84,7 +87,8 @@ object DataGenerator {
txOrder = order,
coinbase = transaction.hash == coinbaseTxHash,
spentFinalized = None,
- spentTimestamp = None
+ spentTimestamp = None,
+ fixedOutput = Random.nextBoolean()
)
}
}
@@ -104,7 +108,8 @@ object DataGenerator {
None,
None,
None,
- None
+ None,
+ contractInput = Random.nextBoolean()
)
}
@@ -144,7 +149,8 @@ object DataGenerator {
depStateHash = Hash.generate,
txsHash = Hash.generate,
target = ByteString.fromString(Random.alphanumeric.take(10).mkString),
- hashrate = BigInteger.valueOf(Random.nextLong(Long.MaxValue))
+ hashrate = BigInteger.valueOf(Random.nextLong(Long.MaxValue)),
+ ghostUncles = ArraySeq.empty
)
}
diff --git a/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/AddressReadState.scala b/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/AddressReadState.scala
index d04aedf13..4483bc5a6 100644
--- a/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/AddressReadState.scala
+++ b/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/AddressReadState.scala
@@ -99,7 +99,8 @@ class AddressReadState(val db: DBExecutor)
None,
None,
None,
- None
+ None,
+ contractInput = false
)
}
private def generateTransaction(
@@ -113,6 +114,9 @@ class AddressReadState(val db: DBExecutor)
timestamp = timestamp,
chainFrom = new GroupIndex(1),
chainTo = new GroupIndex(3),
+ version = 1,
+ networkId = 1,
+ scriptOpt = Some(Random.alphanumeric.take(10).mkString),
gasAmount = 0,
gasPrice = U256.unsafe(0),
order = 0,
@@ -146,7 +150,8 @@ class AddressReadState(val db: DBExecutor)
txOrder = 0,
coinbase = false,
spentFinalized = None,
- spentTimestamp = None
+ spentTimestamp = None,
+ fixedOutput = Random.nextBoolean()
)
}
@@ -188,7 +193,8 @@ class AddressReadState(val db: DBExecutor)
depStateHash = Blake2b.generate,
txsHash = Blake2b.generate,
target = ByteString.emptyByteString,
- hashrate = BigInteger.ONE
+ hashrate = BigInteger.ONE,
+ ghostUncles = ArraySeq.empty
)
})
diff --git a/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/BlockHeaderMainChainReadState.scala b/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/BlockHeaderMainChainReadState.scala
index d26084ef0..54ef47e32 100644
--- a/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/BlockHeaderMainChainReadState.scala
+++ b/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/BlockHeaderMainChainReadState.scala
@@ -18,6 +18,7 @@ package org.alephium.explorer.benchmark.db.state
import java.math.BigInteger
+import scala.collection.immutable.ArraySeq
import scala.util.Random
import akka.util.ByteString
@@ -59,7 +60,9 @@ class BlockHeaderMainChainReadState(
txsCount = Random.nextInt(),
target = ByteString.emptyByteString,
hashrate = BigInteger.ONE,
- parent = Some(BlockHash.generate)
+ parent = Some(BlockHash.generate),
+ deps = ArraySeq.from(0 to 4).map(_ => BlockHash.generate),
+ ghostUncles = None
)
def persist(data: Array[BlockHeader]): Unit = {
diff --git a/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/ListBlocksReadState.scala b/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/ListBlocksReadState.scala
index 942311783..b43a526d4 100644
--- a/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/ListBlocksReadState.scala
+++ b/benchmark/src/test/scala/org/alephium/explorer/benchmark/db/state/ListBlocksReadState.scala
@@ -18,6 +18,7 @@ package org.alephium.explorer.benchmark.db.state
import java.math.BigInteger
+import scala.collection.immutable.ArraySeq
import scala.util.Random
import akka.util.ByteString
@@ -80,7 +81,9 @@ class ListBlocksReadState(
txsCount = scala.math.abs(Random.nextInt()),
target = ByteString.emptyByteString,
hashrate = BigInteger.ONE,
- parent = Some(BlockHash.generate)
+ parent = Some(BlockHash.generate),
+ deps = ArraySeq.from(0 to 4).map(_ => BlockHash.generate),
+ ghostUncles = None
)
private def generateTransactions(header: BlockHeader): Seq[TransactionEntity] =
@@ -91,6 +94,9 @@ class ListBlocksReadState(
timestamp = header.timestamp,
chainFrom = new GroupIndex(1),
chainTo = new GroupIndex(3),
+ version = 1,
+ networkId = 0,
+ scriptOpt = Some(Random.alphanumeric.take(10).mkString),
gasAmount = 0,
gasPrice = U256.unsafe(0),
order = 0,