module os.neptune.standards.plumb

// PLUMBv2: full-Digest authorities and atomic same-sibling tree transitions.
// Normative contract: Trident reference/plumb-v2.md. Legacyv1 is experimental.

pub fn tree_depth() -> Field { 20 }

pub fn pair(left: Digest, right: Digest) -> Digest {
    let (a,b,c,d,e) = left
    let (f,g,h,i,j) = right
    hash(a,b,c,d,e,f,g,h,i,j)
}

pub fn is_zero(value: Digest) -> Bool {
    let (a,b,c,d,e) = value
    if a == 0 {
        if b == 0 {
            if c == 0 {
                if d == 0 { e == 0 } else { false }
            } else { false }
        } else { false }
    } else { false }
}

pub fn hash_config(
    admin_auth: Digest, pay_auth: Digest, lock_auth: Digest,
    mint_auth: Digest, burn_auth: Digest,
    pay_hook: Field, lock_hook: Field, update_hook: Field,
    mint_hook: Field, burn_hook: Field
) -> Digest {
    let a = pair(admin_auth,pay_auth)
    let b = pair(lock_auth,mint_auth)
    let hooks = hash(pay_hook,lock_hook,update_hook,mint_hook,burn_hook,2,2,0,0,0)
    let c = pair(burn_auth,hooks)
    pair(pair(a,b),c)
}

pub fn verify_config(
    admin_auth: Digest, pay_auth: Digest, lock_auth: Digest,
    mint_auth: Digest, burn_auth: Digest,
    pay_hook: Field, lock_hook: Field, update_hook: Field,
    mint_hook: Field, burn_hook: Field, expected: Digest
) {
    assert_digest(hash_config(admin_auth,pay_auth,lock_auth,mint_auth,burn_auth,
        pay_hook,lock_hook,update_hook,mint_hook,burn_hook),expected)
}

pub fn verify_auth(authority: Digest) {
    assert(is_zero(authority) == false)
    let secret: Digest = divine5()
    let (a,b,c,d,e) = secret
    assert_digest(hash(a,b,c,d,e,2,1,0,0,0),authority)
}

// Hook IDs are requests to the integrating verifier, not verified hook proofs.
pub fn signal_hook(hook: Field) {
    if hook == 0 { } else { pub_write(hook) }
}

pub fn assert_non_negative(value: Field) { let _: U32 = as_u32(value) }

pub fn next_nonce(nonce: Field) -> Field {
    assert_non_negative(nonce)
    let next = nonce + 1
    assert_non_negative(next)
    next
}

pub fn check_index(id: Field, index: U32) {
    assert((id == 0) == false)
    assert_eq(id,as_field(index))
    assert(index < as_u32(1048576))
}

pub fn empty_leaf() -> Digest { hash(0,0,0,0,0,0,0,0,2,0) }

// Recompute old and new roots from exactly the same20 sibling Digests.
// Return the new root only after authenticating the original leaf and index.
pub fn update_leaf(old_leaf: Digest, new_leaf: Digest, old_root: Digest, index: U32) -> Digest {
    assert(index < as_u32(1048576))
    let mut old_node: Digest = old_leaf
    let mut new_node: Digest = new_leaf
    let mut idx: U32 = index
    for level in 0..20 bounded 20 {
        let sibling: Digest = divine5()
        let (parent, side): (U32,U32) = idx /% as_u32(2)
        if side == as_u32(0) {
            old_node = pair(old_node,sibling)
            new_node = pair(new_node,sibling)
        } else {
            old_node = pair(sibling,old_node)
            new_node = pair(sibling,new_node)
        }
        idx = parent
    }
    assert(idx == as_u32(0))
    assert_digest(old_node,old_root)
    new_node
}

Graph