ChainSafe / ChainSafe/gossamer
Fuzzing result for ChainSpec target
- Dominant language
- Go
- Stars
- 454
- Forks
- 144
- PR merge metrics
- No merged PRs in 30d
Description
# Target
### ```Substrate```
```rust
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
enum Genesis {
Runtime(G),
Raw(RawGenesis),
/// State root hash of the genesis storage.
StateRootHash(StorageData),
}
/// A configuration of a client. Does not include runtime storage initialization.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
struct ClientSpec {
name: String,
id: String,
#[serde(default)]
chain_type: ChainType,
boot_nodes: Vec,
telemetry_endpoints: Option,
protocol_id: Option,
/// Arbitrary string. Nodes will only synchronize with other nodes that have the same value
/// in their `fork_id`. This can be used in order to segregate nodes in cases when multiple
/// chains have the same genesis hash.
#[serde(default = "Default::default", skip_serializing_if = "Option::is_none")]
fork_id: Option,
properties: Option,
#[serde(flatten)]
extensions: E,
// Never used, left only for backward compatibility.
#[serde(default, skip_serializing)]
#[allow(unused)]
consensus_engine: (),
#[serde(skip_serializing)]
#[allow(unused)]
genesis: serde::de::IgnoredAny,
/// Mapping from `block_number` to `wasm_code`.
///
/// The given `wasm_code` will be used to substitute the on-chain wasm code starting with the
/// given block number until the `spec_version` on chain changes.
#[serde(default)]
code_substitutes: BTreeMap,
}
/// A configuration of a chain. Can be used to build a genesis block.
pub struct ChainSpec {
client_spec: ClientSpec,
genesis: GenesisSource,
}
pub fn from_json_bytes(json: impl Into>) -> Result {
let json = json.into();
let client_spec =
json::from_slice(json.as_ref()).map_err(|e| format!("Error parsing spec file: {}", e))?;
Ok(ChainSpec {
client_spec,
genesis: GenesisSource::Binary(json),
})
}
```
### ```Smoldot```
```rust
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub(super) struct ClientSpec {
pub(super) name: String,
pub(super) id: String,
#[serde(default)]
pub(super) chain_type: ChainType,
/// Mapping from a block number to a hex-encoded wasm runtime code (normally found in the
/// `:code` storage key).
///
/// The given runtime code will be used to substitute the on-chain runtime code starting with
/// the given block number until the `spec_version`
/// ([`crate::executor::host::CoreVersionRef::spec_version`]) on chain changes.
#[serde(default)]
// TODO: make use of this
pub(super) code_substitutes: HashMap,
pub(super) boot_nodes: Vec,
pub(super) telemetry_endpoints: Option>,
pub(super) protocol_id: Option,
#[serde(default = "Default::default", skip_serializing_if = "Option::is_none")]
pub(super) fork_id: Option,
/// The `blockNumberBytes` field is (at the time of writing of this comment) a custom addition
/// to the format of smoldot chain specs compared to Substrate. It is necessary because,
/// contrary to Substrate, smoldot has no way to know the size of the block number field of
/// various data structures. If the field is missing, a value of 4 is assumed.
// TODO: revisit this field in the future to maybe bring compatibility with Substrate
#[serde(default = "Default::default", skip_serializing_if = "Option::is_none")]
pub(super) block_number_bytes: Option,
pub(super) properties: Option>,
// TODO: make use of this
pub(super) fork_blocks: Option>,
pub(super) bad_blocks: Option>,
// Unused but for some reason still part of the chain specs.
#[serde(default, skip_serializing)]
#[allow(unused)]
pub(super) consensus_engine: (),
pub(super) genesis: Genesis,
pub(super) light_sync_state: Option,
#[serde(flatten)]
pub(super) parachain: Option,
}
/// A configuration of a chain. Can be used to build a genesis block.
#[derive(Clone)]
pub struct ChainSpec {
client_spec: structs::ClientSpec,
}
pub fn from_json_bytes(json: impl AsRef<[u8]>) -> Result {
let client_spec: structs::ClientSpec = serde_json::from_slice(json.as_ref())
.map_err(ParseErrorInner::Serde)
.map_err(ParseError)?;
// TODO: we don't support child tries in the genesis block
assert!(match &client_spec.genesis {
structs::Genesis::Raw(genesis) => genesis.children_default.is_empty(),
structs::Genesis::StateRootHash(_) => true,
});
// Make sure that the light sync state can be successfully decoded.
if let Some(light_sync_state) = &client_spec.light_sync_state {
// TODO: this "4" constant is repeated
light_sync_state.decode(client_spec.block_number_bytes.unwrap_or(4).into())?;
}
Ok(ChainSpec { client_spec })
}
```
### ```Gossamer```
```go
// Genesis stores the data parsed from the genesis configuration file
type Genesis struct {
Name string `json:"name"`
ID string `json:"id"`
ChainType string `json:"chainType"`
Bootnodes []string `json:"bootNodes"`
TelemetryEndpoints []interface{} `json:"telemetryEndpoints"`
ProtocolID string `json:"protocolId"`
Genesis Fields `json:"genesis"`
Properties map[string]interface{} `json:"properties"`
ForkBlocks []string `json:"forkBlocks"`
BadBlocks []string `json:"badBlocks"`
ConsensusEngine string `json:"consensusEngine"`
CodeSubstitutes map[string]string `json:"codeSubstitutes"`
}
```
# ```ChainSpec``` Reproducing Scripts
### ```Substrate```
```rust
pub fn substrate_chain_spec_from_json_bytes(file_name: &String) {
println!("[+] Substrate Result:");
let buf = read_bytes(file_name).unwrap();
#[derive(Debug, Serialize, Deserialize)]
struct Genesis(BTreeMap);
let ret = GenericChainSpec::::from_json_bytes(Cow::Owned(buf));
if let Err(e) = ret {
println!("[-] ChainSpec from_json_bytes result: {:?}", e);
} else {
println!("[+] ChainSpec from_json_bytes result: Ok()");
}
}
```
### ```Smoldot```
```rust
pub fn smoldot_chain_spec_from_json_bytes(file_name: &String) {
println!("[+] Smoldot Result:");
let buf = read_bytes(file_name).unwrap();
let ret = smoldot::chain_spec::ChainSpec::from_json_bytes(buf);
if let Err(e) = ret {
println!("[-] ChainSpec from_json_bytes result: {:?}", e);
} else {
println!("[+] ChainSpec from_json_bytes result: Ok()");
}
}
```
### ```Gossamer```
```go
func glib_chain_spec_from_json_bytes(data_ptr unsafe.Pointer, data_size int) {
fmt.Println("[+] Gossamer Result:")
var data []byte
sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
sh.Data = uintptr(data_ptr)
sh.Len = data_size
sh.Cap = data_size
g := new(genesis.Genesis)
err := json.Unmarshal(data, g)
if err != nil {
fmt.Println("[-] ChainSpec json.Unmarshal result:", err)
} else {
fmt.Println("[+] ChainSpec json.Unmarshal result:", g)
}
}
```
# Crash 1
The differential fuzzer catches a crash. The ```Substrate``` and ```Smoldot``` targets give an error message but The ```Gossamer``` target executes the given data successfully.
```
./reproducer run all chainspec /crash-0c19ea3d6837e5554e52c8df3719f39f218d01aa
[+] Smoldot Result:
[-] ChainSpec from_json_bytes result: ParseError(Serde(Error("missing field `name`", line: 1, column: 3)))
[+] Substrate Result:
[-] ChainSpec from_json_bytes result: "Error parsing spec file: missing field `name` at line 1 column 3"
[+] Gossamer Result:
[+] ChainSpec json.Unmarshal result: &{ [] [] {map[] map[]} map[] [] [] map[]}
```
### Artifacts 1
[chainspec_crash1.zip](https://github.com/ChainSafe/gossamer/files/10723972/chainspec_crash1.zip)
# Crash 2
The differential fuzzer catches a crash. The ```Substrate``` and ```Smoldot``` targets give an error message but The ```Gossamer``` target executes the given data successfully.
```
./reproducer run all chainspec /1b067468def90d08
[+] Smoldot Result:
[-] ChainSpec from_json_bytes result: ParseError(Serde(Error("Odd number of digits", line: 22, column: 73)))
[+] Substrate Result:
[-] ChainSpec from_json_bytes result: "Error parsing spec file: missing field `id` at line 139 column 1"
[+] Gossamer Result:
[+] ChainSpec json.Unmarshal result: &{Gossamer Testnet [Dev] Development [] [] gssmr_test {map[childrenDefault:map[] ...``````...]] map[]} map[] [] [] map[]}
```
### Artifacts 2
[chainspec_crash2.zip](https://github.com/ChainSafe/gossamer/files/10723973/chainspec_crash2.zip)
Contributor guide
Assessment
This issue has not been assessed yet.